mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-25 06:16:48 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 78758771fd | |||
| d808104725 | |||
| b71b217814 | |||
| d9f218eacf | |||
| 0faba0fdd8 | |||
| ad113e1283 | |||
| 4a761b51f5 | |||
| 73f8e6aea7 | |||
| e5072460cd | |||
| 311b75a955 | |||
| 6f24133d35 | |||
| 091b4d4113 | |||
| 06d87e21a7 | |||
| b7f1f1728f | |||
| 8074541057 | |||
| d755ca3a59 | |||
| 777b728205 | |||
| 24616ed655 | |||
| 6201c4052b | |||
| ab99024307 | |||
| b7d2b2fa3a | |||
| 5fa14c3dea | |||
| 7fa8506fac | |||
| 9d68e7b9ca | |||
| 9ffbfe0dbb | |||
| 5b00c8b451 | |||
| a5b6f83a3d | |||
| 908d85a51a | |||
| 9089a20ce3 | |||
| d4a5420851 | |||
| 87bc6bfbab | |||
| a5e94cf0d8 | |||
| 2c1d445d5b | |||
| d86f89090b | |||
| 973f61c6dc | |||
| f47aa4d81a | |||
| fa9a3383aa | |||
| 104591a80b | |||
| 030e6e5bb4 | |||
| a031310a66 | |||
| 436e3f7b2a | |||
| 335d571854 | |||
| f78b380b71 | |||
| 2cebc3f60b | |||
| 0a8159505d | |||
| 301fba5a8b | |||
| ba90cf220d | |||
| 6318892a97 | |||
| 1a6336bd20 |
@@ -0,0 +1,382 @@
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "pytest",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
"""Check (and by default fix) whitespace issues in tracked source files:
|
||||
- stray CR, e.g. 'hello\\rworld' -> 'helloworld'
|
||||
- line ending mismatch, e.g. 'hello\\r\\n' -> 'hello\\n' (or vice versa)
|
||||
- missing newline at end of file
|
||||
- extra newline(s) at end of file
|
||||
- trailing whitespace at end of line
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import BinaryIO, Literal, cast
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class C:
|
||||
RED = "\033[31m"
|
||||
GREEN = "\033[32m"
|
||||
YELLOW = "\033[33m"
|
||||
RESET = "\033[0m"
|
||||
|
||||
|
||||
CR = b"\r"
|
||||
CRLF = b"\r\n"
|
||||
LF = b"\n"
|
||||
|
||||
LineSeparator = Literal[b"\r\n", b"\n"]
|
||||
SYSTEM_LINE_SEPARATOR = cast(LineSeparator, os.linesep.encode())
|
||||
|
||||
|
||||
class Checker:
|
||||
def __init__(self, newline: LineSeparator = SYSTEM_LINE_SEPARATOR) -> None:
|
||||
self.newline = newline
|
||||
self.issues = 0
|
||||
|
||||
def report(self, label: str, issue: str) -> None:
|
||||
self.issues += 1
|
||||
print(f"{label}: {C.RED}{issue}{C.RESET}")
|
||||
|
||||
def check_stray_cr(self, filepath: Path, check: bool) -> None:
|
||||
with filepath.open("r+b") as f:
|
||||
self._check_stray_cr(f, str(filepath), check)
|
||||
|
||||
def _check_stray_cr(self, f: BinaryIO, label: str, check: bool) -> None:
|
||||
# a CR is "stray" if it isn't immediately followed by a LF, i.e. not part of a CRLF pair
|
||||
# CRLF/CR mismatch will be reported separately.
|
||||
stray_cr = re.compile(rb"\r(?!\n)")
|
||||
|
||||
content = f.read()
|
||||
matches = list(stray_cr.finditer(content))
|
||||
if not matches:
|
||||
return
|
||||
|
||||
line_numbers = dict.fromkeys(content.count(b"\n", 0, m.start()) + 1 for m in matches)
|
||||
for line_number in line_numbers:
|
||||
self.report(f"{label}:{line_number}", "stray carriage return")
|
||||
if check:
|
||||
return
|
||||
|
||||
f.seek(0)
|
||||
f.write(stray_cr.sub(b"", content))
|
||||
f.truncate()
|
||||
|
||||
def check_line_endings_mismatch(self, filepath: Path, check: bool) -> None:
|
||||
with filepath.open("r+b") as f:
|
||||
self._check_line_endings_mismatch(f, str(filepath), check)
|
||||
|
||||
def _check_line_endings_mismatch(self, f: BinaryIO, label: str, check: bool) -> None:
|
||||
NEWLINE = self.newline
|
||||
|
||||
def get_line_ending(line: bytes) -> LineSeparator | None:
|
||||
if line.endswith(CRLF):
|
||||
return CRLF
|
||||
if line.endswith(LF):
|
||||
return LF
|
||||
# last line with no trailing newline at all; check_eof_newline handles that
|
||||
return None
|
||||
|
||||
changed = False
|
||||
fixed_lines = []
|
||||
for line_number, line in enumerate(f, start=1):
|
||||
found = get_line_ending(line)
|
||||
if found in (NEWLINE, None):
|
||||
fixed_lines.append(line)
|
||||
continue
|
||||
|
||||
self.report(f"{label}:{line_number}", f"line ending mismatch (expected {NEWLINE!r}, found {found!r})")
|
||||
changed = True
|
||||
content = line[: -len(found)]
|
||||
fixed_lines.append(content + NEWLINE)
|
||||
|
||||
if changed and not check:
|
||||
f.seek(0)
|
||||
f.write(b"".join(fixed_lines))
|
||||
f.truncate()
|
||||
|
||||
def check_eof_newline(self, filepath: Path, check: bool) -> None:
|
||||
with filepath.open("r+b") as f:
|
||||
self._check_eof_newline(f, str(filepath), check)
|
||||
|
||||
def _check_eof_newline(self, f: BinaryIO, label: str, check: bool) -> None:
|
||||
NEWLINE = self.newline
|
||||
NEWLINE_SIZE = len(NEWLINE)
|
||||
|
||||
size = f.seek(0, os.SEEK_END)
|
||||
if size == 0:
|
||||
return
|
||||
|
||||
trailing_newlines = 0
|
||||
while True:
|
||||
pos = f.seek((-trailing_newlines - 1) * NEWLINE_SIZE, os.SEEK_END)
|
||||
if f.read(NEWLINE_SIZE) != NEWLINE:
|
||||
break
|
||||
trailing_newlines += 1
|
||||
if pos == 0:
|
||||
break
|
||||
|
||||
if trailing_newlines == 0:
|
||||
self.report(label, "missing newline at end of file")
|
||||
if check:
|
||||
return
|
||||
f.seek(0, os.SEEK_END)
|
||||
f.write(NEWLINE)
|
||||
elif trailing_newlines > 1:
|
||||
self.report(label, f"{trailing_newlines} trailing newlines at end of file")
|
||||
if check:
|
||||
return
|
||||
f.truncate(size - (trailing_newlines - 1) * NEWLINE_SIZE)
|
||||
|
||||
def check_trailing_whitespaces(self, filepath: Path, check: bool) -> None:
|
||||
with filepath.open("r+b") as f:
|
||||
self._check_trailing_whitespaces(f, str(filepath), check)
|
||||
|
||||
def _check_trailing_whitespaces(self, f: BinaryIO, label: str, check: bool) -> None:
|
||||
NEWLINE = self.newline
|
||||
NEWLINE_SIZE = len(NEWLINE)
|
||||
|
||||
changed = False
|
||||
fixed_lines = []
|
||||
for line_number, line in enumerate(f, start=1):
|
||||
has_newline = line.endswith(NEWLINE)
|
||||
content = line[:-NEWLINE_SIZE] if has_newline else line
|
||||
stripped = content.rstrip()
|
||||
if stripped != content:
|
||||
self.report(f"{label}:{line_number}", "trailing whitespace")
|
||||
changed = True
|
||||
fixed_lines.append(stripped + (NEWLINE if has_newline else b""))
|
||||
|
||||
if changed and not check:
|
||||
f.seek(0)
|
||||
f.write(b"".join(fixed_lines))
|
||||
f.truncate()
|
||||
|
||||
|
||||
CheckMethod = Callable[[Checker, BinaryIO, str, bool], None]
|
||||
|
||||
|
||||
class TestChecker:
|
||||
def _assert_check(
|
||||
self,
|
||||
method: CheckMethod,
|
||||
content: bytes,
|
||||
expected_issues: int,
|
||||
fixed: bytes,
|
||||
check: bool,
|
||||
line_ending: LineSeparator,
|
||||
*,
|
||||
transform: bool = True,
|
||||
) -> None:
|
||||
checker = Checker(line_ending)
|
||||
if line_ending == CRLF and transform:
|
||||
content = content.replace(LF, CRLF)
|
||||
fixed = fixed.replace(LF, CRLF)
|
||||
buffer = io.BytesIO(content)
|
||||
method(checker, buffer, "test", check)
|
||||
assert buffer.getvalue() == (content if check else fixed)
|
||||
assert checker.issues == expected_issues
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("content", "expected_issues", "fixed"),
|
||||
(
|
||||
# OK
|
||||
(b"", 0, b""),
|
||||
(b"hello\n", 0, b"hello\n"),
|
||||
(b"line1\r\nline2\n", 0, b"line1\r\nline2\n"),
|
||||
# ERR
|
||||
(b"hello\rworld\n", 1, b"helloworld\n"),
|
||||
(b"a\rb\rc\n", 1, b"abc\n"),
|
||||
(b"hello\r", 1, b"hello"),
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("check", [False, True])
|
||||
def test_check_stray_cr(self, content: bytes, expected_issues: int, fixed: bytes, check: bool) -> None:
|
||||
# Don't parametrize by line endings, since in this case it doesn't matter.
|
||||
self._assert_check(Checker._check_stray_cr, content, expected_issues, fixed, check, LF)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("content", "expected_issues", "fixed", "line_ending"),
|
||||
(
|
||||
# OK
|
||||
(b"", 0, b"", LF),
|
||||
(b"hello\n", 0, b"hello\n", LF),
|
||||
(b"hello\r\n", 0, b"hello\r\n", CRLF),
|
||||
# ERR
|
||||
(b"hello\r\n", 1, b"hello\n", LF),
|
||||
(b"a\nb\r\nc\n", 1, b"a\nb\nc\n", LF),
|
||||
(b"a\r\nb\r\n", 2, b"a\nb\n", LF),
|
||||
(b"hello\n", 1, b"hello\r\n", CRLF),
|
||||
(b"a\r\nb\nc\r\n", 1, b"a\r\nb\r\nc\r\n", CRLF),
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("check", [False, True])
|
||||
def test_check_line_endings_mismatch(
|
||||
self, content: bytes, expected_issues: int, fixed: bytes, line_ending: LineSeparator, check: bool
|
||||
) -> None:
|
||||
self._assert_check(
|
||||
Checker._check_line_endings_mismatch, content, expected_issues, fixed, check, line_ending, transform=False
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("content", "expected_issues", "fixed"),
|
||||
(
|
||||
# OK
|
||||
(b"", 0, b""),
|
||||
(b"hello\n", 0, b"hello\n"),
|
||||
# ERR
|
||||
(b"hello", 1, b"hello\n"),
|
||||
(b"hello\n\n\n", 1, b"hello\n"),
|
||||
(b"\n\n\n", 1, b"\n"),
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("check", [False, True])
|
||||
@pytest.mark.parametrize("line_ending", [LF, CRLF])
|
||||
def test_check_eof_newline(
|
||||
self, content: bytes, expected_issues: int, fixed: bytes, check: bool, line_ending: LineSeparator
|
||||
) -> None:
|
||||
self._assert_check(Checker._check_eof_newline, content, expected_issues, fixed, check, line_ending)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("content", "expected_issues", "fixed"),
|
||||
(
|
||||
# OK
|
||||
(b"", 0, b""),
|
||||
(b"hello\n", 0, b"hello\n"),
|
||||
(b"hello", 0, b"hello"),
|
||||
# ERR
|
||||
(b" ", 1, b""),
|
||||
(b"hello ", 1, b"hello"),
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("check", [False, True])
|
||||
@pytest.mark.parametrize("line_ending", [LF, CRLF])
|
||||
def test_check_trailing_whitespaces(
|
||||
self, content: bytes, expected_issues: int, fixed: bytes, check: bool, line_ending: LineSeparator
|
||||
) -> None:
|
||||
self._assert_check(Checker._check_trailing_whitespaces, content, expected_issues, fixed, check, line_ending)
|
||||
|
||||
@staticmethod
|
||||
def run_tests(extra_args: list[str] | None = None) -> None:
|
||||
pytest.main([__file__, *(extra_args or [])])
|
||||
|
||||
|
||||
def existing_path(value: str) -> Path:
|
||||
path = Path(value)
|
||||
if not path.exists():
|
||||
raise argparse.ArgumentTypeError(f"path not found: {value}")
|
||||
return path
|
||||
|
||||
|
||||
# Python files are covered by `black`.
|
||||
PATTERNS = (
|
||||
"*.cpp",
|
||||
"*.h",
|
||||
"*.i",
|
||||
"*.cmake",
|
||||
"*/CMakeLists.txt",
|
||||
)
|
||||
|
||||
REPO_ROOT = Path(subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text=True).strip())
|
||||
|
||||
# Generated files; formatted by the express codegen, not by this script.
|
||||
IGNORED_DIRS = (
|
||||
REPO_ROOT / "src/ifcparse/schemas",
|
||||
REPO_ROOT / "win/patches",
|
||||
)
|
||||
|
||||
|
||||
def get_tracked_files(root: Path | None = None) -> list[Path]:
|
||||
output = subprocess.check_output(
|
||||
["git", "ls-files", "--others", "--cached", "--exclude-standard", *PATTERNS],
|
||||
cwd=root,
|
||||
text=True,
|
||||
)
|
||||
base = root if root is not None else Path()
|
||||
filepaths = []
|
||||
for line in output.splitlines():
|
||||
filepath = base / line
|
||||
if not any(filepath.resolve().is_relative_to(d) for d in IGNORED_DIRS):
|
||||
filepaths.append(filepath)
|
||||
return filepaths
|
||||
|
||||
|
||||
def main() -> int:
|
||||
# anything after "--" is forwarded to pytest, e.g. `--test -- --capture=no`
|
||||
argv = sys.argv[1:]
|
||||
if "--" in argv:
|
||||
split = argv.index("--")
|
||||
argv, extra_args = argv[:split], argv[split + 1 :]
|
||||
else:
|
||||
extra_args = []
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=__doc__,
|
||||
)
|
||||
parser.add_argument("paths", type=existing_path, nargs="*", help="files or directories to check")
|
||||
parser.add_argument(
|
||||
"--check",
|
||||
action="store_true",
|
||||
help="only check for whitespace issues without applying fixes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--test",
|
||||
action="store_true",
|
||||
help="run self-tests",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="print each checked path",
|
||||
)
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.test:
|
||||
TestChecker.run_tests(extra_args)
|
||||
return 0
|
||||
|
||||
if args.paths:
|
||||
filepaths: list[Path] = []
|
||||
for path in args.paths:
|
||||
filepaths.extend(get_tracked_files(path) if path.is_dir() else [path])
|
||||
else:
|
||||
filepaths = get_tracked_files()
|
||||
|
||||
# dict.fromkeys() dedupes while preserving order, unlike set().
|
||||
filepaths = list(dict.fromkeys(filepaths))
|
||||
|
||||
checker = Checker()
|
||||
for filepath in filepaths:
|
||||
if args.verbose:
|
||||
print(f"checking {filepath}")
|
||||
checker.check_stray_cr(filepath, args.check)
|
||||
checker.check_line_endings_mismatch(filepath, args.check)
|
||||
checker.check_eof_newline(filepath, args.check)
|
||||
checker.check_trailing_whitespaces(filepath, args.check)
|
||||
print(f"{len(filepaths)} file(s) checked.")
|
||||
if not checker.issues:
|
||||
color = C.GREEN
|
||||
elif args.check:
|
||||
color = C.RED
|
||||
else:
|
||||
color = C.YELLOW
|
||||
outcome = "found" if args.check else "found and fixed"
|
||||
print(f"{color}{checker.issues} issue(s) {outcome}.{C.RESET}")
|
||||
|
||||
return 1 if args.check and checker.issues else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
working-directory: src/bonsaiviewer-autodesk
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ on:
|
||||
- 'src/ifc5d/ifc5d/**'
|
||||
- 'src/ifccityjson/**'
|
||||
branches:
|
||||
- v0.8.0
|
||||
- v0.9.0
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -51,19 +51,10 @@ jobs:
|
||||
name: "Linux Build",
|
||||
short_name: linux,
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
short_name: macos,
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
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:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
|
||||
@@ -128,7 +119,7 @@ jobs:
|
||||
blender --command extension install-file -r user_default -e $bonsai_zip
|
||||
blender --command extension list
|
||||
|
||||
git clone https://github.com/IfcOpenShell/IfcOpenShell.git IfcOpenShell
|
||||
git clone --branch ${{ github.ref_name }} --single-branch https://github.com/IfcOpenShell/IfcOpenShell.git IfcOpenShell
|
||||
|
||||
# Reregister Bonsai.
|
||||
# Note that running it in background might miss some errors
|
||||
|
||||
@@ -34,19 +34,10 @@ jobs:
|
||||
name: "Linux Build",
|
||||
short_name: linux,
|
||||
}
|
||||
- {
|
||||
name: "MacOS Build",
|
||||
short_name: macos,
|
||||
}
|
||||
- {
|
||||
name: "MacOS ARM Build",
|
||||
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:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-python@v7 # https://github.com/actions/setup-python
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
- '.github/workflows/ci-ifcsverchok-build.yml'
|
||||
- 'src/ifcsverchok/*'
|
||||
branches:
|
||||
- v0.8.0
|
||||
- v0.9.0
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
|
||||
@@ -3,11 +3,13 @@ name: ci-ifctester-org
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- v0.9.0
|
||||
paths:
|
||||
- src/ifctester/**
|
||||
|
||||
jobs:
|
||||
publish_website:
|
||||
publish_ifctester_org:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
+227
-54
@@ -32,20 +32,7 @@ Example usage:
|
||||
python build-all.py IfcParse IfcOpenShell-Python
|
||||
|
||||
|
||||
Available arguments:
|
||||
``-py-313`` - build for specific Python version
|
||||
(building for all supported Python version by default).
|
||||
``-occt-xxx`` - use a specific OCCT version (e.g. ``-occt-7.8.1``) instead of the default
|
||||
``-wasm`` - compile for wasm
|
||||
``-without-xxx`` - do not build dependency ``xxx`` (e.g. ``--without-swig``)
|
||||
``-mac-cross-compile-intel`` - cross compile for Intel Mac on Apple Silicon host
|
||||
``-shared`` - build shared libraries. By default will build static.
|
||||
``-ifcopenshell-shared`` - build only IfcOpenShell's own libraries as shared
|
||||
(dependencies stay static). Redundant if ``-shared`` is also passed.
|
||||
``-diskcleanup`` - clean up build directories after finishing building dependencies
|
||||
``-build-examples`` - build IfcOpenShell examples
|
||||
``-lto`` - enable link-time optimization (adds ``-flto`` to compiler flags)
|
||||
``-v`` - enable verbose logs
|
||||
Run with --help to see available arguments.
|
||||
|
||||
|
||||
Used environment variables:
|
||||
@@ -74,6 +61,8 @@ Used environment variables:
|
||||
`ADD_COMMIT_SHA` and `VERSION_OVERRIDE` will be set to `ON` while configuring IfcOpenShell
|
||||
- ``BUILD_BONSAIVIEWER`` - enable building BonsaiViewer, `off` by default.
|
||||
- ``IFCOS_BUILD_PYTHON_WRAPPER`` - enable building the Python wrapper, `on` by default.
|
||||
- ``PYTHON_USER_SITE`` - install the Python wrapper into the user's site-packages directory
|
||||
instead of the interpreter's prefix, `off` by default.
|
||||
|
||||
# This script builds IfcOpenShell and its dependencies #
|
||||
# #
|
||||
@@ -118,6 +107,9 @@ Used environment variables:
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import logging
|
||||
import multiprocessing
|
||||
@@ -129,12 +121,13 @@ import subprocess as sp
|
||||
import sys
|
||||
import sysconfig
|
||||
import tarfile
|
||||
import textwrap
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Generator, Sequence
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
from typing import Literal, NamedTuple
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
from typing_extensions import assert_never
|
||||
@@ -162,6 +155,7 @@ ADD_COMMIT_SHA = is_on_off(os.getenv("ADD_COMMIT_SHA"), default=False)
|
||||
IFCOS_BUILD_PYTHON_WRAPPER = is_on_off(os.getenv("IFCOS_BUILD_PYTHON_WRAPPER"), default=True)
|
||||
BUILD_BONSAIVIEWER = is_on_off(os.getenv("BUILD_BONSAIVIEWER"), default=False)
|
||||
USE_OCCT = is_on_off(os.getenv("USE_OCCT"), default=True)
|
||||
PYTHON_USER_SITE = is_on_off(os.getenv("PYTHON_USER_SITE"), default=False)
|
||||
|
||||
PYTHON_VERSIONS = ["3.10.3", "3.11.8", "3.12.1", "3.13.6", "3.14.0"]
|
||||
JSON_VERSION = "3.11.3"
|
||||
@@ -202,10 +196,142 @@ strip = "strip"
|
||||
xz = "xz" # Used implicitly for `tar -xf *.tar.xz`.
|
||||
brew = "brew"
|
||||
|
||||
explicit_targets = [s for s in sys.argv[1:] if not s.startswith("-")]
|
||||
|
||||
class Args(NamedTuple):
|
||||
explicit_targets: list[str]
|
||||
build_examples: bool
|
||||
diskcleanup: bool
|
||||
lto: bool
|
||||
verbose: bool
|
||||
shared: bool
|
||||
ifcopenshell_shared: bool
|
||||
occt_shared: bool
|
||||
mac_cross_compile_intel: bool
|
||||
wasm: bool
|
||||
|
||||
|
||||
class DynamicArgs(NamedTuple):
|
||||
without: set[str]
|
||||
py_versions: set[str]
|
||||
occt_version: str | None
|
||||
|
||||
@classmethod
|
||||
def from_unknown_flags(cls, unknown_flags: list[str], arg_parser: argparse.ArgumentParser) -> DynamicArgs:
|
||||
flags = set(s.lstrip("-") for s in unknown_flags if s.startswith("-"))
|
||||
|
||||
without: set[str] = set()
|
||||
py_versions: set[str] = set()
|
||||
occt_versions: set[str] = set()
|
||||
leftover: set[str] = set()
|
||||
|
||||
for f in flags:
|
||||
if f.startswith("without-"):
|
||||
without.add(f.removeprefix("without-").lower())
|
||||
elif f.startswith("py-"):
|
||||
py_versions.add(f.removeprefix("py-"))
|
||||
elif f.startswith("occt-"):
|
||||
occt_versions.add(f.removeprefix("occt-"))
|
||||
else:
|
||||
leftover.add(f)
|
||||
|
||||
if leftover:
|
||||
arg_parser.error(f"unrecognized arguments: {', '.join('-' + f for f in sorted(leftover))}")
|
||||
if len(occt_versions) > 1:
|
||||
arg_parser.error(f"more than one OCCT version provided: {', '.join(sorted(occt_versions))}")
|
||||
|
||||
occt_version = next(iter(occt_versions), None)
|
||||
return cls(without=without, py_versions=py_versions, occt_version=occt_version)
|
||||
|
||||
|
||||
def parse_args() -> tuple[Args, DynamicArgs]:
|
||||
arg_parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=textwrap.dedent("""\
|
||||
Additional dynamic -flags (not declared above):
|
||||
-py-313 build for specific Python version
|
||||
(building for all supported Python versions by default)
|
||||
-occt-xxx use a specific OCCT version (e.g. -occt-7.8.1) instead of the default
|
||||
-without-xxx do not build dependency `xxx` (e.g. --without-swig)"""),
|
||||
)
|
||||
arg_parser.add_argument("explicit_targets", nargs="*", help="Targets provided by CLI.")
|
||||
arg_parser.add_argument(
|
||||
"--build-examples",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Build IfcOpenShell examples.",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"-diskcleanup",
|
||||
"--diskcleanup",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Clean up build directories after finishing building dependencies.",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"-lto",
|
||||
"--lto",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Enable link-time optimization (adds -flto to compiler flags).",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Enable verbose logs.",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"-shared",
|
||||
"--shared",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Build shared libraries. By default will build static.",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"-ifcopenshell-shared",
|
||||
"--ifcopenshell-shared",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Build only IfcOpenShell's own libraries as shared (dependencies stay static). "
|
||||
"Redundant if -shared is also passed.",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--occt-shared",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Build OCCT as shared. Redundant if -shared is also passed.",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"-mac-cross-compile-intel",
|
||||
"--mac-cross-compile-intel",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Cross compile for Intel Mac on Apple Silicon host.",
|
||||
)
|
||||
arg_parser.add_argument("-wasm", "--wasm", action="store_true", default=False, help="Compile for wasm.")
|
||||
namespace, unknown_flags = arg_parser.parse_known_args()
|
||||
args = Args(
|
||||
explicit_targets=namespace.explicit_targets,
|
||||
build_examples=namespace.build_examples,
|
||||
diskcleanup=namespace.diskcleanup,
|
||||
lto=namespace.lto,
|
||||
verbose=namespace.verbose,
|
||||
shared=namespace.shared,
|
||||
ifcopenshell_shared=namespace.ifcopenshell_shared or namespace.shared,
|
||||
occt_shared=namespace.occt_shared or namespace.shared,
|
||||
mac_cross_compile_intel=namespace.mac_cross_compile_intel,
|
||||
wasm=namespace.wasm,
|
||||
)
|
||||
|
||||
dynamic_args = DynamicArgs.from_unknown_flags(unknown_flags, arg_parser)
|
||||
return args, dynamic_args
|
||||
|
||||
|
||||
ARGS, DYNAMIC_ARGS = parse_args()
|
||||
|
||||
explicit_targets: set[str] = set(ARGS.explicit_targets)
|
||||
"""Targets provided by CLI."""
|
||||
flags = set(s.lstrip("-") for s in sys.argv[1:] if s.startswith("-"))
|
||||
"""CLI flags."""
|
||||
|
||||
# Helper function for coloured printing
|
||||
|
||||
@@ -224,17 +350,11 @@ def cecho(message, color=NO_COLOR):
|
||||
logger.info(f"{color}{message}\033[0m")
|
||||
|
||||
|
||||
# Flags.
|
||||
BUILD_EXAMPLES = "build-examples" in flags
|
||||
DISK_CLEANUP = "diskcleanup" in flags
|
||||
LTO = "lto" in flags
|
||||
VERBOSE = "v" in flags
|
||||
|
||||
APPLE = platform.system() == "Darwin"
|
||||
MAC_CROSS_COMPILE_INTEL = "mac-cross-compile-intel" in flags
|
||||
MAC_CROSS_COMPILE_INTEL = ARGS.mac_cross_compile_intel
|
||||
assert platform.system() == "Darwin" or not MAC_CROSS_COMPILE_INTEL
|
||||
|
||||
WASM = "wasm" in flags
|
||||
WASM = ARGS.wasm
|
||||
"""Build WASM outside pyodide build environment."""
|
||||
WASM_CMAKE_IS_USING_INIT_VARS = False
|
||||
if WASM:
|
||||
@@ -380,12 +500,12 @@ dependency_tree: dict[str, tuple[str, ...]] = {
|
||||
def gather_dependencies(dep: str) -> Generator[str]:
|
||||
yield dep
|
||||
for d in dependency_tree[dep]:
|
||||
if f"without-{d.lower()}" not in flags:
|
||||
if d.lower() not in DYNAMIC_ARGS.without:
|
||||
for x in gather_dependencies(d):
|
||||
yield x
|
||||
|
||||
|
||||
if VERBOSE:
|
||||
if ARGS.verbose:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
||||
ch.setFormatter(formatter)
|
||||
@@ -406,29 +526,26 @@ else:
|
||||
MAC_CROSS_COMPILE_INTEL_AUTOCONF_HOST_ARGS = []
|
||||
|
||||
OFF_ON = ["OFF", "ON"]
|
||||
BUILD_STATIC = "shared" not in flags
|
||||
BUILD_STATIC = not ARGS.shared
|
||||
"""Whether dependencies are built static."""
|
||||
IFCOPENSHELL_STATIC = BUILD_STATIC and "ifcopenshell-shared" not in flags
|
||||
"""Whether IfcOpenShell's own libraries are built static."""
|
||||
ENABLE_FLAG = "--enable-static" if BUILD_STATIC else "--enable-shared"
|
||||
DISABLE_FLAG = "--disable-shared" if BUILD_STATIC else "--disable-static"
|
||||
LINK_TYPE = "static" if BUILD_STATIC else "shared"
|
||||
LINK_TYPE_UCFIRST = LINK_TYPE.capitalize()
|
||||
LIBRARY_EXT = "a" if BUILD_STATIC else "so"
|
||||
PIC = "-fPIC" if BUILD_STATIC else ""
|
||||
|
||||
if any(f.startswith("py-") for f in flags):
|
||||
PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if f"py-{''.join(pyv.split('.')[:2])}" in flags]
|
||||
if DYNAMIC_ARGS.py_versions:
|
||||
PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if "".join(pyv.split(".")[:2]) in DYNAMIC_ARGS.py_versions]
|
||||
|
||||
if any(f.startswith("occt-") for f in flags):
|
||||
OCCT_VERSION = next(f.split("-", 1)[1] for f in flags if f.startswith("occt-"))
|
||||
if DYNAMIC_ARGS.occt_version is not None:
|
||||
OCCT_VERSION = DYNAMIC_ARGS.occt_version
|
||||
|
||||
if explicit_targets:
|
||||
targets = {dep for target in explicit_targets for dep in gather_dependencies(target)}
|
||||
else:
|
||||
targets = set(dependency_tree.keys())
|
||||
|
||||
targets = set(t for t in targets if "without-%s" % t.lower() not in flags)
|
||||
targets = set(t for t in targets if t.lower() not in DYNAMIC_ARGS.without)
|
||||
if not explicit_targets and not BUILD_BONSAIVIEWER:
|
||||
targets.difference_update({"BonsaiViewer", "qt6"})
|
||||
if BUILD_BONSAIVIEWER:
|
||||
@@ -511,7 +628,7 @@ def restore_env(var_name: str, old_value: str | None) -> None:
|
||||
os.environ[var_name] = old_value
|
||||
|
||||
|
||||
def run(cmds: Sequence[str], cwd: str | None = None, can_fail: bool = False) -> str:
|
||||
def run(cmds: Sequence[str], cwd: str | None = None, can_fail: bool = False, env: dict[str, str] | None = None) -> str:
|
||||
"""
|
||||
Wraps `subprocess.Popen.communicate()` and logs the command being executed,
|
||||
sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout
|
||||
@@ -535,7 +652,7 @@ def run(cmds: Sequence[str], cwd: str | None = None, can_fail: bool = False) ->
|
||||
# Ensure both live logs available in the log file
|
||||
# and the putput.
|
||||
with open(LOG_FILE, "a", encoding="utf-8") as log_file_handle:
|
||||
proc = sp.Popen(cmds, cwd=cwd, stdout=sp.PIPE, stderr=sp.PIPE, encoding="utf-8")
|
||||
proc = sp.Popen(cmds, cwd=cwd, stdout=sp.PIPE, stderr=sp.PIPE, encoding="utf-8", env=env)
|
||||
assert proc.stdout and proc.stderr
|
||||
|
||||
t_out = threading.Thread(target=stream_reader, args=(proc.stdout, stdout, log_file_handle))
|
||||
@@ -821,7 +938,7 @@ def build_dependency(
|
||||
)
|
||||
logger.info(f"\rInstalled {name} \n")
|
||||
|
||||
if DISK_CLEANUP:
|
||||
if ARGS.diskcleanup:
|
||||
shutil.rmtree(build_dir, ignore_errors=True)
|
||||
|
||||
|
||||
@@ -927,6 +1044,8 @@ ADDITIONAL_ARGS_STR = " ".join(ADDITIONAL_ARGS)
|
||||
|
||||
CXXFLAGS_MINIMAL = f"{CXXFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
|
||||
CFLAGS_MINIMAL = f"{CFLAGS} {PIC} {ADDITIONAL_ARGS_STR}"
|
||||
CXXFLAGS_SHARED = CXXFLAGS_MINIMAL
|
||||
CFLAGS_SHARED = CFLAGS_MINIMAL
|
||||
if WASM:
|
||||
# WASM `SIDE_MODULE_` are absorbed by `emcmake` automatically.
|
||||
CXXFLAGS = CXXFLAGS_MINIMAL
|
||||
@@ -936,19 +1055,19 @@ elif sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev
|
||||
CXXFLAGS = f"{CXXFLAGS} {PIC} -fdata-sections -ffunction-sections -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
|
||||
CFLAGS = f"{CFLAGS} {PIC} -fdata-sections -ffunction-sections -fvisibility=hidden {ADDITIONAL_ARGS_STR}"
|
||||
else:
|
||||
CXXFLAGS = CXXFLAGS_MINIMAL
|
||||
CFLAGS = CFLAGS_MINIMAL
|
||||
CXXFLAGS = CXXFLAGS_SHARED
|
||||
CFLAGS = CFLAGS_SHARED
|
||||
LDFLAGS = f"{LDFLAGS} -Wl,--gc-sections {ADDITIONAL_ARGS_STR}"
|
||||
else:
|
||||
if BUILD_STATIC:
|
||||
CXXFLAGS = f"{CXXFLAGS} {PIC} -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
|
||||
CFLAGS = f"{CFLAGS} {PIC} -fvisibility=hidden -fvisibility-inlines-hidden {ADDITIONAL_ARGS_STR}"
|
||||
else:
|
||||
CXXFLAGS = CXXFLAGS_MINIMAL
|
||||
CFLAGS = CFLAGS_MINIMAL
|
||||
CXXFLAGS = CXXFLAGS_SHARED
|
||||
CFLAGS = CFLAGS_SHARED
|
||||
LDFLAGS = f"{LDFLAGS} {ADDITIONAL_ARGS_STR}"
|
||||
|
||||
if LTO:
|
||||
if ARGS.lto:
|
||||
for f in compiler_flags:
|
||||
locals()[f] += f" -flto={IFCOS_NUM_BUILD_PROCS}"
|
||||
|
||||
@@ -1031,6 +1150,9 @@ if "swig" in targets:
|
||||
if USE_OCCT and "occ" in targets:
|
||||
occt_args: list[str] = []
|
||||
patches: list[str] = []
|
||||
occt_link_type = "Shared" if ARGS.occt_shared else "Static"
|
||||
occt_name = f"occt-shared-{OCCT_VERSION}" if ARGS.occt_shared else f"occt-{OCCT_VERSION}"
|
||||
OCCT_INSTALL_PATH = f"{DEPS_DIR}/install/{occt_name}"
|
||||
if OCCT_VERSION < "7.4":
|
||||
patches.append("./patches/occt/enable-exception-handling.patch")
|
||||
|
||||
@@ -1045,12 +1167,23 @@ if USE_OCCT and "occ" in targets:
|
||||
if WASM:
|
||||
patches.append("./patches/occt/no_em_js.patch")
|
||||
|
||||
if ARGS.occt_shared:
|
||||
# Using static flags for shared builds break it
|
||||
# (e.g. `-fvisibility=hidden` hides many symbols).
|
||||
# So we temporarily override flags.
|
||||
OLD_CPP_FLAGS = os.environ["CPPFLAGS"]
|
||||
OLD_CXX_FLAGS = os.environ["CXXFLAGS"]
|
||||
OLD_C_FLAGS = os.environ["CFLAGS"]
|
||||
os.environ["CXXFLAGS"] = CXXFLAGS_SHARED
|
||||
os.environ["CPPFLAGS"] = CXXFLAGS_SHARED
|
||||
os.environ["CFLAGS"] = CFLAGS_SHARED
|
||||
|
||||
build_dependency(
|
||||
name=f"occt-{OCCT_VERSION}",
|
||||
name=occt_name,
|
||||
mode="cmake",
|
||||
build_tool_args=[
|
||||
f"-DINSTALL_DIR={DEPS_DIR}/install/occt-{OCCT_VERSION}",
|
||||
f"-DBUILD_LIBRARY_TYPE={LINK_TYPE_UCFIRST}",
|
||||
f"-DINSTALL_DIR={OCCT_INSTALL_PATH}",
|
||||
f"-DBUILD_LIBRARY_TYPE={occt_link_type}",
|
||||
f"-DBUILD_MODULE_Draw=0",
|
||||
f"-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off",
|
||||
# Disable xlib explicitly, as it tries to use it on Desktop Ubuntu, adding unnecessary dependency.
|
||||
@@ -1069,6 +1202,11 @@ if USE_OCCT and "occ" in targets:
|
||||
patch=patches,
|
||||
revision="V" + OCCT_VERSION.replace(".", "_"),
|
||||
)
|
||||
|
||||
if ARGS.occt_shared:
|
||||
restore_env("CPPFLAGS", OLD_CPP_FLAGS)
|
||||
restore_env("CXXFLAGS", OLD_CXX_FLAGS)
|
||||
restore_env("CFLAGS", OLD_C_FLAGS)
|
||||
elif "occ" in targets:
|
||||
build_dependency(
|
||||
name=f"oce-{OCE_VERSION}",
|
||||
@@ -1431,7 +1569,7 @@ if "qt6" in targets:
|
||||
cecho("Building IfcOpenShell:", GREEN)
|
||||
|
||||
IFCOS_DIR = os.path.join(DEPS_DIR, "build", "ifcopenshell")
|
||||
if os.environ.get("NO_CLEAN", "").lower() not in {"1", "on", "true"}:
|
||||
if not is_on_off(os.getenv("NO_CLEAN"), default=False):
|
||||
if os.path.exists(IFCOS_DIR):
|
||||
shutil.rmtree(IFCOS_DIR)
|
||||
os.makedirs(IFCOS_DIR, exist_ok=True)
|
||||
@@ -1441,8 +1579,8 @@ os.makedirs(ifcos_build_dir, exist_ok=True)
|
||||
|
||||
cmake_args = [
|
||||
"-DUSE_MMAP=OFF",
|
||||
f"-DBUILD_EXAMPLES={OFF_ON[BUILD_EXAMPLES]}",
|
||||
"-DBUILD_SHARED_LIBS=" + OFF_ON[not IFCOPENSHELL_STATIC],
|
||||
f"-DBUILD_EXAMPLES={OFF_ON[ARGS.build_examples]}",
|
||||
"-DBUILD_SHARED_LIBS=" + OFF_ON[ARGS.ifcopenshell_shared],
|
||||
"-DGLTF_SUPPORT=ON",
|
||||
"-DBoost_NO_BOOST_CMAKE=On",
|
||||
"-DCREATE_BUNDLE=On",
|
||||
@@ -1487,7 +1625,7 @@ if "cgal" in targets:
|
||||
cmake_args.append(f"-DCGAL_WITH_GMPXX=Off")
|
||||
|
||||
if "occ" in targets and USE_OCCT:
|
||||
cmake_args_prefix_path.append(f"{DEPS_DIR}/install/occt-{OCCT_VERSION}")
|
||||
cmake_args_prefix_path.append(OCCT_INSTALL_PATH)
|
||||
|
||||
elif "occ" in targets:
|
||||
# We don't support find_package for OCE.
|
||||
@@ -1573,6 +1711,42 @@ if not WASM and (
|
||||
run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "VERBOSE=1"], cwd=ifcos_build_dir)
|
||||
run([make, "install/strip" if BUILD_CFG == "Release" else "install"], cwd=ifcos_build_dir)
|
||||
|
||||
def test_examples() -> None:
|
||||
cecho("Running examples...", GREEN)
|
||||
examples_bin_dir = Path(DEPS_DIR) / "install" / "ifcopenshell" / "bin"
|
||||
|
||||
examples_env = os.environ.copy()
|
||||
ld_library_paths = ["../lib"]
|
||||
if ARGS.occt_shared:
|
||||
ld_library_paths.append(f"{OCCT_INSTALL_PATH}/lib")
|
||||
examples_env["LD_LIBRARY_PATH"] = os.pathsep.join(ld_library_paths)
|
||||
|
||||
examples: dict[tuple[str, ...], str | None] = {
|
||||
("./IfcOpenHouse",): "IfcOpenHouse.ifc",
|
||||
("./IfcParseExamples", "IfcOpenHouse.ifc"): None,
|
||||
("./IfcAdvancedHouse",): "IfcAdvancedHouse.ifc",
|
||||
}
|
||||
# Only for ifc4x3 schema.
|
||||
if (examples_bin_dir / "IfcAlignment").is_file():
|
||||
examples[("./IfcAlignment",)] = "FHWA_Bridge_Geometry_Alignment_Example.ifc"
|
||||
examples[("./IfcSimplifiedAlignment",)] = "FHWA_Bridge_Geometry_Alignment_Example_Simplified.ifc"
|
||||
|
||||
produced_files: set[str] = set()
|
||||
try:
|
||||
for cmd, expected_file in examples.items():
|
||||
run(cmd, cwd=str(examples_bin_dir), env=examples_env)
|
||||
if expected_file is None:
|
||||
continue
|
||||
if not (examples_bin_dir / expected_file).is_file():
|
||||
raise RuntimeError(f"Example `{' '.join(cmd)}` did not produce expected file '{expected_file}'.")
|
||||
produced_files.add(expected_file)
|
||||
finally:
|
||||
for produced_file in produced_files:
|
||||
(examples_bin_dir / produced_file).unlink(missing_ok=True)
|
||||
|
||||
if ARGS.build_examples:
|
||||
test_examples()
|
||||
|
||||
if "IfcOpenShell-Python" in targets:
|
||||
wrapper_ldflags = ""
|
||||
if platform.system() == "Darwin":
|
||||
@@ -1624,8 +1798,7 @@ if "IfcOpenShell-Python" in targets:
|
||||
*([f"-DPYTHON_MODULE_INSTALL_DIR={REPO_PATH}"] * WASM),
|
||||
f"-DPYTHON_INCLUDE_DIR={python_include}",
|
||||
f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/ifcopenshell/tmp",
|
||||
"-DUSERSPACE_PYTHON_PREFIX="
|
||||
+ ["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}],
|
||||
"-DUSERSPACE_PYTHON_PREFIX=" + OFF_ON[PYTHON_USER_SITE],
|
||||
],
|
||||
cmake_dir=CMAKE_DIR,
|
||||
cwd=ifcos_build_dir,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Intended to be run after nix/build-all.py has finished the wasm build."""
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
+13
-5
@@ -6,11 +6,6 @@ version = "0.0.0"
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
include = '''
|
||||
src/.*.pyi?$
|
||||
|nix/.*.pyi?$
|
||||
|pyodide/.*.pyi?$
|
||||
'''
|
||||
extend-exclude = '''
|
||||
src/ifcopenshell-python/ifcopenshell/express/rules/*
|
||||
|src/ifcopenshell-python/ifcopenshell/express/express_parser.py
|
||||
@@ -19,6 +14,15 @@ extend-exclude = '''
|
||||
|src/ifc2ca/templates/*
|
||||
|src/svgfill
|
||||
|src/exterior-shell-extractor
|
||||
|choco/bonsai/tools/enable_blenderbim_addon.py
|
||||
|choco/bonsai/tools/disable_blenderbim_addon.py
|
||||
|docs/conf.py
|
||||
|docs/generate_docs.py
|
||||
|aws/lambda/example_handler/__init__.py
|
||||
|conda/update_version_init.py
|
||||
|test/bpy.py
|
||||
|test/tests.py
|
||||
|test/run.py
|
||||
'''
|
||||
|
||||
[tool.pyright]
|
||||
@@ -112,7 +116,9 @@ invalid-assignment = "ignore"
|
||||
invalid-parameter-default = "ignore"
|
||||
missing-override-decorator = "ignore"
|
||||
invalid-yield = "ignore"
|
||||
unsound-yield = "ignore"
|
||||
invalid-return-type = "ignore"
|
||||
unsound-return-statement = "ignore"
|
||||
non-callable-init-subclass = "ignore"
|
||||
not-iterable = "ignore"
|
||||
possibly-missing-attribute = "ignore"
|
||||
@@ -181,6 +187,8 @@ dev-setup.help = "Install repo packages in editable mode"
|
||||
|
||||
ruff = "ruff check"
|
||||
|
||||
check-whitespace = "uv run .github/scripts/check-whitespace.py"
|
||||
|
||||
black = "black ."
|
||||
|
||||
ty.sequence = ["ty-bonsai", "ty-ios"]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
black==26.3.1
|
||||
ruff==0.16.0
|
||||
poethepoet
|
||||
ty==0.0.63
|
||||
ty==0.0.72
|
||||
gersemi==0.28.0
|
||||
|
||||
+4
-23
@@ -69,32 +69,17 @@ endif # def PYVERSION
|
||||
IFCMERGE_VERSION:=2026-04-07
|
||||
|
||||
ifdef PLATFORM
|
||||
SUPPORTED_PLATFORMS := linux macos macosm1 win
|
||||
SUPPORTED_PLATFORMS := linux macosm1 win
|
||||
|
||||
ifeq ($(filter $(PLATFORM),$(SUPPORTED_PLATFORMS)),)
|
||||
$(error Unsupported PLATFORM=$(PLATFORM). Must be one of $(SUPPORTED_PLATFORMS))
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM),macos)
|
||||
ifeq ($(PYVERSION),py313)
|
||||
$(error Blender 5.1 with Python 3.13 doesn't support intel macOS.)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), linux)
|
||||
PYPI_PLATFORM:=--platform manylinux_2_17_x86_64
|
||||
BLENDER_PLATFORM:=linux-x64
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macos)
|
||||
ifeq ($(PYVERSION), py311)
|
||||
PYPI_PLATFORM:=--platform macosx_10_10_x86_64
|
||||
else
|
||||
PYPI_PLATFORM:=--platform macosx_10_13_x86_64
|
||||
endif
|
||||
BLENDER_PLATFORM:=macos-x64
|
||||
endif
|
||||
|
||||
ifeq ($(PLATFORM), macosm1)
|
||||
PYPI_PLATFORM:=--platform macosx_11_0_arm64
|
||||
BLENDER_PLATFORM:=macos-arm64
|
||||
@@ -108,7 +93,7 @@ endif
|
||||
endif # def PLATFORM
|
||||
|
||||
# Current build commit hash.
|
||||
OLD:=3e7b739
|
||||
OLD:=ad113e1
|
||||
.PHONY: bump
|
||||
bump:
|
||||
ifndef NEW
|
||||
@@ -194,10 +179,8 @@ endif
|
||||
# Provides networkx graph analysis for project dependency calculations
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download networkx --dest=./wheels
|
||||
# Required by IFCDiff
|
||||
# Pinned <9.1: deepdiff 9.1.0 adds cachebox<6,>=5.2 which only ships macOS x86_64
|
||||
# wheels for macosx_10_12+ and is incompatible with our macos py311 --platform
|
||||
# macosx_10_10_x86_64 target. Revisit once the macos py311 platform tag is bumped
|
||||
# to 10_13 (matching py312/py313).
|
||||
# Pinned <9.1: deepdiff 9.1.0 adds the compiled dependency cachebox<6,>=5.2,
|
||||
# which this platformless download cannot provide for every target platform.
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download "deepdiff<9.1" --dest=./wheels
|
||||
# Required by IFCCSV and ifcopenshell.util.selector
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download lark --dest=./wheels
|
||||
@@ -215,8 +198,6 @@ endif
|
||||
# pyradiance is using different platform versions than defaults in our makefile.
|
||||
ifeq ($(PLATFORM), linux)
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pyradiance --platform manylinux_2_28_x86_64 --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
else ifeq ($(PLATFORM), macos)
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pyradiance --platform macosx_10_13_x86_64 --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
else
|
||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download pyradiance $(PYPI_PLATFORM) --python-version $(PYPI_VERSION) --implementation $(PYPI_IMP) --only-binary=:all: --dest=./wheels
|
||||
endif
|
||||
|
||||
@@ -428,7 +428,8 @@ void MainWindow::setupPanels() {
|
||||
spatial_panel_ = new modules::spatial_hierarchy::SpatialHierarchyPanel(this);
|
||||
properties_panel_ = new modules::properties::PropertiesPanel(this);
|
||||
|
||||
models_view_ = new modules::models::ModelsPanelView(models_panel_, session_state_, this);
|
||||
models_view_ = new modules::models::ModelsPanelView(
|
||||
models_panel_, session_state_, viewport_widget_->viewport(), this);
|
||||
spatial_view_ = new modules::spatial_hierarchy::SpatialHierarchyPanelView(spatial_panel_, session_state_, this);
|
||||
properties_view_ = new modules::properties::PropertiesPanelView(properties_panel_, session_state_, this);
|
||||
|
||||
@@ -481,6 +482,13 @@ void MainWindow::setupStatus() {
|
||||
status_mode_label_ = new QLabel("Ready", this);
|
||||
status_selection_label_ = new QLabel("No selection", this);
|
||||
status_perf_label_ = new QLabel(this);
|
||||
status_memory_label_ = new QLabel(this);
|
||||
status_memory_label_->setVisible(false);
|
||||
status_memory_label_->setToolTip(
|
||||
"The geometry in view needs more GPU memory than is available, so the "
|
||||
"viewer keeps the largest on-screen parts resident and streams the rest "
|
||||
"as you move. Right-click a model in the Models panel and choose "
|
||||
"\"Unload Model\" to free its GPU memory for the others.");
|
||||
status_progress_bar_ = new QProgressBar(this);
|
||||
status_perf_label_->setVisible(AppSettings::instance().showStats());
|
||||
status_progress_bar_->setMaximumWidth(200);
|
||||
@@ -489,6 +497,7 @@ void MainWindow::setupStatus() {
|
||||
statusBar()->setSizeGripEnabled(false);
|
||||
statusBar()->addWidget(status_mode_label_);
|
||||
statusBar()->addWidget(status_selection_label_, 1);
|
||||
statusBar()->addPermanentWidget(status_memory_label_);
|
||||
statusBar()->addPermanentWidget(status_perf_label_);
|
||||
statusBar()->addPermanentWidget(status_progress_bar_);
|
||||
|
||||
@@ -554,16 +563,59 @@ void MainWindow::setupLoader() {
|
||||
|
||||
connect(viewport_widget_->viewport(), &ViewportWindow::frameStatsUpdated, this,
|
||||
[this](const ViewportWindow::FrameStats& stats) {
|
||||
const double mb = 1.0 / (1024.0 * 1024.0);
|
||||
|
||||
// Missing chunks are normal for a moment after every camera move
|
||||
// while streaming catches up; only a shortfall that persists means
|
||||
// the view does not fit, and only that is worth telling the user.
|
||||
constexpr qint64 kShortfallNoticeMs = 3000;
|
||||
if (stats.chunks_wanted_missing == 0) {
|
||||
memory_shortfall_since_.invalidate();
|
||||
status_memory_label_->setVisible(false);
|
||||
} else {
|
||||
if (!memory_shortfall_since_.isValid()) memory_shortfall_since_.start();
|
||||
if (memory_shortfall_since_.elapsed() >= kShortfallNoticeMs) {
|
||||
status_memory_label_->setText(
|
||||
QString("GPU memory full: %1 of %2 visible chunks (%3 MB) not loaded")
|
||||
.arg(stats.chunks_wanted_missing)
|
||||
.arg(stats.chunks_wanted)
|
||||
.arg(double(stats.wanted_missing_bytes) * mb, 0, 'f', 0));
|
||||
status_memory_label_->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!status_perf_label_->isVisible()) return;
|
||||
status_perf_label_->setText(
|
||||
QString("%1 fps | %2 ms | %3/%4 obj | %5/%6 tri | %7 draws")
|
||||
QString text =
|
||||
QString("%1 fps | %2 ms | %3/%4 obj | %5/%6 tri | %7 draws | VRAM %8/%9 MB")
|
||||
.arg(stats.fps, 0, 'f', 1)
|
||||
.arg(stats.frame_time_ms, 0, 'f', 1)
|
||||
.arg(stats.visible_objects)
|
||||
.arg(stats.total_objects)
|
||||
.arg(stats.visible_triangles)
|
||||
.arg(stats.total_triangles)
|
||||
.arg(stats.gl_draw_calls));
|
||||
.arg(stats.gl_draw_calls)
|
||||
.arg(double(stats.vram_used_bytes) * mb, 0, 'f', 0)
|
||||
.arg(double(stats.vram_capacity_bytes) * mb, 0, 'f', 0);
|
||||
// The budget is where the pool may grow to; the pool can also sit
|
||||
// a sub-buffer above it (a release would undershoot). Show it
|
||||
// only when it tells the user something capacity does not.
|
||||
if (stats.vram_budget_bytes > 0
|
||||
&& stats.vram_budget_bytes != stats.vram_capacity_bytes) {
|
||||
text += QString(" (budget %1)")
|
||||
.arg(double(stats.vram_budget_bytes) * mb, 0, 'f', 0);
|
||||
}
|
||||
// Device total is only known when a driver backend answered.
|
||||
if (stats.device_vram_total_bytes > 0) {
|
||||
text += QString(" | Device %1/%2 MB")
|
||||
.arg(double(stats.device_vram_used_bytes) * mb, 0, 'f', 0)
|
||||
.arg(double(stats.device_vram_total_bytes) * mb, 0, 'f', 0);
|
||||
}
|
||||
if (stats.chunks_wanted_missing > 0) {
|
||||
text += QString(" | %1/%2 chunks waiting")
|
||||
.arg(stats.chunks_wanted_missing)
|
||||
.arg(stats.chunks_wanted);
|
||||
}
|
||||
status_perf_label_->setText(text);
|
||||
});
|
||||
connect(viewport_widget_->viewport(), &ViewportWindow::objectPicked,
|
||||
this, [this](uint32_t object_id) {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QStringList>
|
||||
|
||||
class QLabel;
|
||||
#include <QElapsedTimer>
|
||||
class QDockWidget;
|
||||
class QMenu;
|
||||
class QProgressBar;
|
||||
@@ -70,6 +71,10 @@ private:
|
||||
QLabel* status_mode_label_ = nullptr;
|
||||
QLabel* status_selection_label_ = nullptr;
|
||||
QLabel* status_perf_label_ = nullptr;
|
||||
// Shown while the visible geometry persistently exceeds what fits in
|
||||
// GPU memory (see onFrameStats): the user's cue to unload models.
|
||||
QLabel* status_memory_label_ = nullptr;
|
||||
QElapsedTimer memory_shortfall_since_;
|
||||
QProgressBar* status_progress_bar_ = nullptr;
|
||||
bonsaiviewer::components::TabBar* ribbon_tabs_ = nullptr;
|
||||
QStackedWidget* ribbon_pages_ = nullptr;
|
||||
|
||||
@@ -199,6 +199,10 @@ void SessionState::notifyModelGeometryReady(uint32_t session_model_id) {
|
||||
emit modelGeometryReady(session_model_id);
|
||||
}
|
||||
|
||||
void SessionState::notifyModelLoadStateChanged(const QString& model_id) {
|
||||
emit modelLoadStateChanged(model_id);
|
||||
}
|
||||
|
||||
void SessionState::notifyProjectOpened(const QString& path) {
|
||||
emit projectOpened(path);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ public:
|
||||
void notifyFederationChanged();
|
||||
void notifyVisibilityChanged();
|
||||
void notifyModelGeometryReady(uint32_t session_model_id);
|
||||
void notifyModelLoadStateChanged(const QString& model_id);
|
||||
void notifyProjectOpened(const QString& path);
|
||||
void notifyProjectSaved(const QString& path);
|
||||
void notifyProjectReset();
|
||||
@@ -107,6 +108,10 @@ signals:
|
||||
// for both sidecar-cache and stream loads; subscribers that just need to
|
||||
// re-derive view state (e.g. ViewportView::refresh) listen to this.
|
||||
void modelGeometryReady(uint32_t session_model_id);
|
||||
// Fires when a model was unloaded from, or loaded back onto, the GPU
|
||||
// (commands::unloadModel / loadModel). The viewport is the authority
|
||||
// for the state itself — ViewportWindow::isModelUnloaded.
|
||||
void modelLoadStateChanged(const QString& model_id);
|
||||
// Fires when a model's live IFC data source (the .ifc/.rdb, opened in the
|
||||
// background after a sidecar-cache hit) becomes available for queries —
|
||||
// e.g. so the spatial hierarchy can be built once the file is loaded.
|
||||
|
||||
@@ -262,6 +262,28 @@ void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host,
|
||||
session.setStatusMessage("Models", "Model removed");
|
||||
}
|
||||
|
||||
void unloadModel(SessionState& session, ViewportWindow& viewport, const QString& model_id) {
|
||||
const uint32_t session_model_id = session.sessionModelIdForModelId(model_id);
|
||||
if (session_model_id == 0) return;
|
||||
if (session.loader()->isLoadingModel(session_model_id)) return;
|
||||
const double freed_mb = double(viewport.modelVramBytes(session_model_id)) / (1024.0 * 1024.0);
|
||||
viewport.unloadModel(session_model_id);
|
||||
session.notifyModelLoadStateChanged(model_id);
|
||||
session.setStatusMessage("Models", QString("Model unloaded (freed %1 MB of GPU memory)")
|
||||
.arg(freed_mb, 0, 'f', 0));
|
||||
}
|
||||
|
||||
void loadModel(SessionState& session, ViewportWindow& viewport, const QString& model_id) {
|
||||
const uint32_t session_model_id = session.sessionModelIdForModelId(model_id);
|
||||
if (session_model_id == 0) return;
|
||||
if (!viewport.loadModel(session_model_id)) {
|
||||
session.setStatusMessage("Models", "Not enough GPU memory to load this model");
|
||||
return;
|
||||
}
|
||||
session.notifyModelLoadStateChanged(model_id);
|
||||
session.setStatusMessage("Models", "Model loaded");
|
||||
}
|
||||
|
||||
void viewModels(SessionState& session, ViewportWindow& viewport, const QStringList& model_ids) {
|
||||
// Federation ids are the panel's currency; the viewport speaks session
|
||||
// model ids. sessionModelIdForModelId returns 0 for a model the viewport
|
||||
|
||||
@@ -60,6 +60,12 @@ void moveGroup(SessionState& session, const QString& id, const QString& parent_g
|
||||
void moveModels(SessionState& session, const QStringList& ids, const QString& parent_group_id);
|
||||
void removeGroup(SessionState& session, QWidget& host, const QString& group_id);
|
||||
void removeModel(SessionState& session, ViewportWindow& viewport, QWidget& host, const QString& model_id);
|
||||
// GPU residency, distinct from visibility (hide) and from membership
|
||||
// (remove): unloadModel frees everything the model holds on the device
|
||||
// while it stays in the federation; loadModel brings it back. Both emit
|
||||
// modelLoadStateChanged.
|
||||
void unloadModel(SessionState& session, ViewportWindow& viewport, const QString& model_id);
|
||||
void loadModel(SessionState& session, ViewportWindow& viewport, const QString& model_id);
|
||||
// "View Selected Model" — frame the camera on just these models' geometry, the
|
||||
// way View All frames the whole federation. Models that carry no loaded
|
||||
// geometry (never loaded, or still streaming their metadata) contribute
|
||||
|
||||
@@ -25,16 +25,25 @@
|
||||
#include "../../../ifcviewer/Federation.h"
|
||||
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
#include <QColor>
|
||||
|
||||
namespace bonsaiviewer::modules::models {
|
||||
|
||||
namespace {
|
||||
|
||||
QStandardItem* siblingVisibilityItem(QStandardItem* name_item) {
|
||||
QStandardItem* siblingItem(QStandardItem* name_item, Column column) {
|
||||
QStandardItem* parent = name_item->parent();
|
||||
if (!parent) parent = name_item->model()->invisibleRootItem();
|
||||
return parent->child(name_item->row(), 1);
|
||||
return parent->child(name_item->row(), int(column));
|
||||
}
|
||||
|
||||
QStandardItem* siblingVisibilityItem(QStandardItem* name_item) {
|
||||
return siblingItem(name_item, VisibilityColumn);
|
||||
}
|
||||
|
||||
QString formatMegabytes(quint64 bytes) {
|
||||
return QString("%1 MB").arg(double(bytes) / (1024.0 * 1024.0), 0, 'f', 0);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
@@ -51,7 +60,7 @@ FederationItemModel::FederationItemModel(Federation* federation, QObject* parent
|
||||
: QStandardItemModel(parent)
|
||||
, federation_(federation)
|
||||
{
|
||||
setColumnCount(2);
|
||||
setColumnCount(ColumnCount);
|
||||
rebuildAll();
|
||||
|
||||
connect(federation_, &Federation::groupAdded, this, &FederationItemModel::onGroupAdded);
|
||||
@@ -67,7 +76,7 @@ FederationItemModel::FederationItemModel(Federation* federation, QObject* parent
|
||||
|
||||
void FederationItemModel::rebuildAll() {
|
||||
clear();
|
||||
setColumnCount(2);
|
||||
setColumnCount(ColumnCount);
|
||||
id_to_name_item_.clear();
|
||||
|
||||
for (const auto& root_group : federation_->rootGroups()) {
|
||||
@@ -121,6 +130,14 @@ QStandardItem* FederationItemModel::makeVisibilityItem(ItemKind kind, bool visib
|
||||
return item;
|
||||
}
|
||||
|
||||
QStandardItem* FederationItemModel::makeMemoryItem() const {
|
||||
auto* item = new QStandardItem(QString());
|
||||
item->setEditable(false);
|
||||
item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
item->setForeground(QBrush(QColor(bonsaiviewer::ViewerSettings::instance().color("disabled_text"))));
|
||||
return item;
|
||||
}
|
||||
|
||||
void FederationItemModel::styleRowVisibility(QStandardItem* name_item, bool visible) const {
|
||||
QStandardItem* vis_item = siblingVisibilityItem(name_item);
|
||||
if (visible) {
|
||||
@@ -133,6 +150,22 @@ void FederationItemModel::styleRowVisibility(QStandardItem* name_item, bool visi
|
||||
}
|
||||
}
|
||||
|
||||
void FederationItemModel::setModelResidency(const QString& model_id, bool unloaded, quint64 vram_bytes) {
|
||||
QStandardItem* name_item = findItem(model_id);
|
||||
if (!name_item) return;
|
||||
QStandardItem* memory_item = siblingItem(name_item, MemoryColumn);
|
||||
if (!memory_item) return;
|
||||
const QString text = unloaded ? QStringLiteral("unloaded")
|
||||
: vram_bytes > 0 ? formatMegabytes(vram_bytes)
|
||||
: QString();
|
||||
if (memory_item->text() != text) memory_item->setText(text);
|
||||
QFont font = name_item->font();
|
||||
if (font.italic() != unloaded) {
|
||||
font.setItalic(unloaded);
|
||||
name_item->setFont(font);
|
||||
}
|
||||
}
|
||||
|
||||
QStandardItem* FederationItemModel::findItem(const QString& id) const {
|
||||
return id_to_name_item_.value(id, nullptr);
|
||||
}
|
||||
@@ -148,7 +181,7 @@ void FederationItemModel::appendModelTo(QStandardItem* parent_item, const QStrin
|
||||
if (!model) return;
|
||||
auto* name_item = makeModelNameItem(model_id, model->display_name);
|
||||
auto* vis_item = makeVisibilityItem(ItemKind::Model, federation_->isModelEffectivelyVisible(model_id));
|
||||
parent_item->appendRow({name_item, vis_item});
|
||||
parent_item->appendRow({name_item, makeMemoryItem(), vis_item});
|
||||
id_to_name_item_.insert(model_id, name_item);
|
||||
styleRowVisibility(name_item, federation_->isModelEffectivelyVisible(model_id));
|
||||
}
|
||||
@@ -158,7 +191,7 @@ void FederationItemModel::appendGroupSubtreeTo(QStandardItem* parent_item, const
|
||||
if (!group) return;
|
||||
auto* name_item = makeGroupNameItem(group_id, group->display_name);
|
||||
auto* vis_item = makeVisibilityItem(ItemKind::Group, group->visible);
|
||||
parent_item->appendRow({name_item, vis_item});
|
||||
parent_item->appendRow({name_item, makeMemoryItem(), vis_item});
|
||||
id_to_name_item_.insert(group_id, name_item);
|
||||
styleRowVisibility(name_item, group->visible);
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class Federation;
|
||||
namespace bonsaiviewer::modules::models {
|
||||
|
||||
// QStandardItemModel that mirrors the Federation tree (groups + models in
|
||||
// two columns: name + visibility icon). Subscribes directly to Federation's
|
||||
// three columns: name, GPU memory, visibility icon). Subscribes directly to Federation's
|
||||
// granular signals so each mutation only touches the affected rows — view
|
||||
// state (expansion, selection, scroll) is preserved automatically.
|
||||
//
|
||||
@@ -57,6 +57,12 @@ public:
|
||||
// previously- and newly-active model rows.
|
||||
void setActiveModelId(const QString& model_id);
|
||||
|
||||
// GPU residency is viewport state, not Federation state, so it is pushed
|
||||
// in by the owning View: the memory column shows `vram_bytes` for a
|
||||
// loaded model and "unloaded" for one the user unloaded (which is also
|
||||
// drawn in italics). Models the viewport knows nothing about show blank.
|
||||
void setModelResidency(const QString& model_id, bool unloaded, quint64 vram_bytes);
|
||||
|
||||
private slots:
|
||||
void onGroupAdded(const QString& group_id);
|
||||
void onGroupRemoved(const QString& group_id);
|
||||
@@ -72,6 +78,7 @@ private:
|
||||
QStandardItem* makeGroupNameItem(const QString& group_id, const QString& display_name) const;
|
||||
QStandardItem* makeModelNameItem(const QString& model_id, const QString& display_name) const;
|
||||
QStandardItem* makeVisibilityItem(ItemKind kind, bool visible) const;
|
||||
QStandardItem* makeMemoryItem() const;
|
||||
void styleRowVisibility(QStandardItem* name_item, bool visible) const;
|
||||
|
||||
QStandardItem* findItem(const QString& id) const;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "../../components/Section.h"
|
||||
#include "../../components/SvgIcon.h"
|
||||
#include "../../../ifcviewer/Federation.h"
|
||||
#include "../../../ifcviewer/ViewportWindow.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDrag>
|
||||
@@ -79,6 +80,7 @@ QStringList selectedModelIdsAt(QTreeView* tree, const QModelIndex& clicked_index
|
||||
}
|
||||
|
||||
constexpr int kVisibilityColumnWidth = 28;
|
||||
constexpr int kMemoryColumnWidth = 72; // "1234 MB" / "unloaded"
|
||||
|
||||
// QTreeView subclass that handles drag-and-drop. Drop logic dispatches
|
||||
// through commands (not directly into the model) so notifications + status
|
||||
@@ -250,7 +252,7 @@ ModelsPanel::ModelsPanel(bonsaiviewer::SessionState* session_state,
|
||||
|
||||
connect(tree_, &QTreeView::clicked, this, [this](const QModelIndex& index) {
|
||||
if (!index.isValid()) return;
|
||||
if (index.column() == 1) {
|
||||
if (index.column() == VisibilityColumn) {
|
||||
commands::toggleVisibility(*session_state_, kindOf(index), idOf(index));
|
||||
return;
|
||||
}
|
||||
@@ -380,6 +382,24 @@ ModelsPanel::ModelsPanel(bonsaiviewer::SessionState* session_state,
|
||||
commands::saveModelAsToCloud(*session_state_, *this, id);
|
||||
});
|
||||
|
||||
// GPU residency. Unload keeps the model in the federation (and
|
||||
// its visibility) but frees everything it holds on the GPU — the
|
||||
// lever when the scene does not fit in VRAM. Load brings it back.
|
||||
menu.addSeparator();
|
||||
const uint32_t session_model_id = session_state_->sessionModelIdForModelId(id);
|
||||
const bool unloaded = session_model_id != 0 && viewport_->isModelUnloaded(session_model_id);
|
||||
QAction* residency = menu.addAction(
|
||||
components::icons::makeSvgIcon(":/icons/cube.svg"),
|
||||
unloaded ? "Load Model" : "Unload Model");
|
||||
residency->setEnabled(session_model_id != 0);
|
||||
residency->setToolTip(unloaded
|
||||
? "Allocate GPU memory for this model again and stream its geometry back in."
|
||||
: "Free this model's GPU memory while keeping it in the federation.");
|
||||
connect(residency, &QAction::triggered, this, [this, id, unloaded]() {
|
||||
if (unloaded) commands::loadModel(*session_state_, *viewport_, id);
|
||||
else commands::unloadModel(*session_state_, *viewport_, id);
|
||||
});
|
||||
|
||||
menu.addSeparator();
|
||||
QAction* remove = menu.addAction(
|
||||
components::icons::makeSvgIcon(":/icons/minus-square.svg"), "Remove Model");
|
||||
@@ -409,14 +429,16 @@ void ModelsPanel::setModel(FederationItemModel* model) {
|
||||
}
|
||||
|
||||
void ModelsPanel::applyColumnLayout() {
|
||||
// Column 0 (name) stretches to fill; column 1 (visibility icon) is fixed.
|
||||
// The name stretches to fill; memory and visibility are fixed.
|
||||
QHeaderView* header = tree_->header();
|
||||
if (header->count() < 2) return;
|
||||
if (header->count() < ColumnCount) return;
|
||||
header->setStretchLastSection(false);
|
||||
header->setMinimumSectionSize(kVisibilityColumnWidth);
|
||||
header->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
header->setSectionResizeMode(1, QHeaderView::Fixed);
|
||||
header->resizeSection(1, kVisibilityColumnWidth);
|
||||
header->setSectionResizeMode(NameColumn, QHeaderView::Stretch);
|
||||
header->setSectionResizeMode(MemoryColumn, QHeaderView::Fixed);
|
||||
header->resizeSection(MemoryColumn, kMemoryColumnWidth);
|
||||
header->setSectionResizeMode(VisibilityColumn, QHeaderView::Fixed);
|
||||
header->resizeSection(VisibilityColumn, kVisibilityColumnWidth);
|
||||
}
|
||||
|
||||
} // namespace bonsaiviewer::modules::models
|
||||
|
||||
@@ -31,6 +31,14 @@ enum class ItemKind {
|
||||
Model,
|
||||
};
|
||||
|
||||
// Columns of the models tree: name | GPU memory | visibility eye.
|
||||
enum Column : int {
|
||||
NameColumn = 0,
|
||||
MemoryColumn = 1,
|
||||
VisibilityColumn = 2,
|
||||
ColumnCount = 3,
|
||||
};
|
||||
|
||||
struct TreeNode {
|
||||
QString id;
|
||||
QString name;
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#include "../../ViewerSettings.h"
|
||||
#include "../../SessionState.h"
|
||||
#include "../../../ifcviewer/Federation.h"
|
||||
#include "../../../ifcviewer/ViewportWindow.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
namespace bonsaiviewer::modules::models {
|
||||
|
||||
@@ -54,17 +57,19 @@ QList<GroupOption> validMoveTargets(const Federation& federation,
|
||||
|
||||
ModelsPanelView::ModelsPanelView(ModelsPanel* widget,
|
||||
bonsaiviewer::SessionState* session_state,
|
||||
ViewportWindow* viewport,
|
||||
QObject* parent)
|
||||
: QObject(parent)
|
||||
, widget_(widget)
|
||||
, session_state_(session_state)
|
||||
, viewport_(viewport)
|
||||
, model_(new FederationItemModel(session_state->federation(), this))
|
||||
{
|
||||
widget_->setModel(model_);
|
||||
|
||||
// Coarse signals: full rebuild + re-style. The granular Federation
|
||||
// signals are handled inside FederationItemModel and don't reach here.
|
||||
auto rebuild = [this]() { model_->rebuildAll(); };
|
||||
auto rebuild = [this]() { model_->rebuildAll(); refreshResidency(); };
|
||||
connect(session_state_, &SessionState::projectReset, this, rebuild);
|
||||
connect(session_state_, &SessionState::projectOpened, this, rebuild);
|
||||
connect(&bonsaiviewer::ViewerSettings::instance(),
|
||||
@@ -73,6 +78,30 @@ ModelsPanelView::ModelsPanelView(ModelsPanel* widget,
|
||||
connect(session_state_, &SessionState::activeModelChanged, this, [this](const QString& model_id) {
|
||||
model_->setActiveModelId(model_id);
|
||||
});
|
||||
|
||||
// Residency: immediately on the events that change it, and on a slow
|
||||
// tick for the memory figures, which move as chunks stream.
|
||||
auto refresh = [this]() { refreshResidency(); };
|
||||
connect(session_state_, &SessionState::modelLoadStateChanged, this, refresh);
|
||||
connect(session_state_, &SessionState::modelGeometryReady, this, refresh);
|
||||
connect(session_state_, &SessionState::modelsChanged, this, refresh);
|
||||
auto* tick = new QTimer(this);
|
||||
tick->setInterval(1000);
|
||||
connect(tick, &QTimer::timeout, this, refresh);
|
||||
tick->start();
|
||||
}
|
||||
|
||||
void ModelsPanelView::refreshResidency() {
|
||||
for (const auto& model : session_state_->federation()->models()) {
|
||||
const uint32_t session_model_id = session_state_->sessionModelIdForModelId(model.id);
|
||||
if (session_model_id == 0) {
|
||||
model_->setModelResidency(model.id, false, 0);
|
||||
continue;
|
||||
}
|
||||
model_->setModelResidency(model.id,
|
||||
viewport_->isModelUnloaded(session_model_id),
|
||||
viewport_->modelVramBytes(session_model_id));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace bonsaiviewer::modules::models
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QObject>
|
||||
|
||||
class Federation;
|
||||
class ViewportWindow;
|
||||
namespace bonsaiviewer { class SessionState; }
|
||||
|
||||
namespace bonsaiviewer::modules::models {
|
||||
@@ -45,16 +46,25 @@ QList<GroupOption> validMoveTargets(const Federation& federation,
|
||||
// coarse session signals (project open/reset, theme change) — those are the
|
||||
// "rebuild from scratch" cases the model itself doesn't subscribe to.
|
||||
// Granular Federation events are handled inside the model.
|
||||
//
|
||||
// Also the bridge for the one thing the tree shows that is not Federation
|
||||
// state: each model's GPU residency (memory column, unloaded styling). The
|
||||
// viewport owns that state, so this view polls it once a second — the
|
||||
// numbers move continuously while geometry streams — and pushes it in.
|
||||
class ModelsPanelView : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModelsPanelView(ModelsPanel* widget,
|
||||
bonsaiviewer::SessionState* session_state,
|
||||
ViewportWindow* viewport,
|
||||
QObject* parent = nullptr);
|
||||
|
||||
private:
|
||||
void refreshResidency();
|
||||
|
||||
ModelsPanel* widget_ = nullptr;
|
||||
bonsaiviewer::SessionState* session_state_ = nullptr;
|
||||
ViewportWindow* viewport_ = nullptr;
|
||||
FederationItemModel* model_ = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -76,12 +76,12 @@ int main() {
|
||||
// By adding a building, a hierarchy has been automatically created that consists of the following
|
||||
// structure: IfcProject > IfcSite > IfcBuilding
|
||||
|
||||
// Lateron changing the name of the IfcProject can be done by obtaining a reference to the
|
||||
// Lateron changing the name of the IfcProject can be done by obtaining a reference to the
|
||||
// project, which has been created automatically.
|
||||
file.getSingle<IfcSchema::IfcProject>().setName("IfcAdvancedHouse"s);
|
||||
|
||||
// To demonstrate the ability to serialize arbitrary opencascade solids a building envelope is
|
||||
// constructed by applying boolean operations. Naturally, in IFC, building elements should be
|
||||
// constructed by applying boolean operations. Naturally, in IFC, building elements should be
|
||||
// modeled separately, with rich parametric and relational semantics. Creating geometry in this
|
||||
// way does not preserve any history and is merely a demonstration of technical capabilities.
|
||||
TopoDS_Shape outer = BRepPrimAPI_MakeBox(gp_Pnt(-5000., -180., -2000.), gp_Pnt(5000., 5180., 3000.)).Shape();
|
||||
@@ -101,8 +101,13 @@ int main() {
|
||||
// IfcFacetedBRep. If it would not be a polyhedron, serialise() can only be successful when linked
|
||||
// to the IFC4 model and with `advanced` set to `true` which introduces IfcAdvancedFace. It would
|
||||
// return `0` otherwise.
|
||||
auto building_shape = ifcopenshell::geom::serialise(file, building_shell, false).as<IfcSchema::IfcProductDefinitionShape>();
|
||||
|
||||
auto building_shape_result = ifcopenshell::geom::serialise(file, building_shell, false);
|
||||
if (!building_shape_result) {
|
||||
std::cerr << "Failed to serialize building shell." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
auto building_shape = building_shape_result.as<IfcSchema::IfcProductDefinitionShape>();
|
||||
|
||||
file.add_entity(building_shape);
|
||||
auto building_representations = building_shape.Representations();
|
||||
building_representations.front().setContextOfItems(file.getRepresentationContext("model"));
|
||||
@@ -122,7 +127,7 @@ int main() {
|
||||
ground_representation = ifcopenshell::geom::tesselate(file, shape, 100.);
|
||||
}
|
||||
file.getSingle<IfcSchema::IfcSite>().setRepresentation(ground_representation.as<IfcSchema::IfcProductDefinitionShape>());
|
||||
|
||||
|
||||
auto ground_reps = file.getSingle<IfcSchema::IfcSite>().Representation().Representations();
|
||||
for (auto& rep : ground_reps) {
|
||||
rep.setContextOfItems(file.getRepresentationContext("Model"));
|
||||
@@ -175,10 +180,10 @@ void createGroundShape(TopoDS_Shape& shape) {
|
||||
cv.SetValue(4, 4, gp_Pnt( 10000, 10000, -8130));
|
||||
TColStd_Array1OfReal knots(0, 1);
|
||||
knots(0) = 0;
|
||||
knots(1) = 1;
|
||||
knots(1) = 1;
|
||||
TColStd_Array1OfInteger mult(0, 1);
|
||||
mult(0) = 5;
|
||||
mult(1) = 5;
|
||||
mult(1) = 5;
|
||||
Handle(Geom_BSplineSurface) surf = new Geom_BSplineSurface(cv, knots, knots, mult, mult, 4, 4);
|
||||
#if OCC_VERSION_HEX < 0x60502
|
||||
shape = BRepBuilderAPI_MakeFace(surf);
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
// alignment explicitly
|
||||
|
||||
// Disable warnings coming from IfcOpenShell
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable : 4018 4267 4250 4984 4985)
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/schemas/Ifc4x3_add2.h"
|
||||
#include "../ifcparse/hierarchy_helper.h"
|
||||
@@ -70,7 +72,7 @@ Schema::IfcProject setup_project(hierarchy_helper<Schema>& file) {
|
||||
dimensions.setThermodynamicTemperatureExponent(0);
|
||||
dimensions.setAmountOfSubstanceExponent(0);
|
||||
dimensions.setLuminousIntensityExponent(0);
|
||||
|
||||
|
||||
auto conversion_factor = file.create<Schema::IfcMeasureWithUnit>();
|
||||
auto length = file.create<Schema::IfcLengthMeasure>();
|
||||
length.set_attribute_value(0, 304.80);
|
||||
@@ -82,7 +84,7 @@ Schema::IfcProject setup_project(hierarchy_helper<Schema>& file) {
|
||||
conversion_based_unit.setUnitType(Schema::IfcUnitEnum::IfcUnit_LENGTHUNIT);
|
||||
conversion_based_unit.setName("FEET");
|
||||
conversion_based_unit.setConversionFactor(conversion_factor);
|
||||
|
||||
|
||||
units.erase(std::remove(units.begin(), units.end(), unit)); // remove the millimeter unit
|
||||
units.push_back(conversion_based_unit); // add the feet unit
|
||||
units_in_context.setUnits(units); // update the UnitsInContext
|
||||
@@ -386,7 +388,7 @@ int main() {
|
||||
nests_horizontal_segments.setName("Nests horizontal alignment segments with horizontal alignment");
|
||||
nests_horizontal_segments.setRelatingObject(horizontal_alignment);
|
||||
nests_horizontal_segments.setRelatedObjects(horizontal_segments);
|
||||
|
||||
|
||||
//
|
||||
// Create plan view footprint model representation for the horizontal alignment
|
||||
//
|
||||
@@ -403,7 +405,7 @@ int main() {
|
||||
footprint_shape_representation.setRepresentationType("Curve2D");
|
||||
// the composite curve is a representation item
|
||||
footprint_shape_representation.setItems({composite_curve});
|
||||
|
||||
|
||||
//
|
||||
// Define vertical profile segments
|
||||
//
|
||||
@@ -539,7 +541,7 @@ int main() {
|
||||
nests_alignment_layouts.setName("Nest horizontal and vertical alignment layouts with the alignment");
|
||||
nests_alignment_layouts.setRelatingObject(alignment);
|
||||
nests_alignment_layouts.setRelatedObjects({horizontal_alignment, vertical_profile});
|
||||
|
||||
|
||||
// Define the relationship with the project
|
||||
|
||||
// IFC 4.1.4.1.1 "Every IfcAlignment must be related to IfcProject using the IfcRelAggregates relationship"
|
||||
@@ -550,7 +552,7 @@ int main() {
|
||||
aggregate_alignments_with_project.setName("Alignments in project");
|
||||
aggregate_alignments_with_project.setRelatingObject(project);
|
||||
aggregate_alignments_with_project.setRelatedObjects({alignment});
|
||||
|
||||
|
||||
// Define the spatial structure of the alignment with respect to the site
|
||||
|
||||
// IFC 4.1.5.1 alignment is referenced in spatial structure of an IfcSpatialElement. In this case IfcSite is the highest level IfcSpatialElement
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
// Some convenience typedefs and definitions.
|
||||
// Some convenience typedefs and definitions.
|
||||
typedef ifcopenshell::global_id guid;
|
||||
typedef std::pair<double, double> XY;
|
||||
#ifdef SCHEMA_HAS_IfcPresentationStyleAssignment
|
||||
@@ -295,9 +295,9 @@ int main() {
|
||||
west_void.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
west_void.setRelatingBuildingElement(west_wall);
|
||||
west_void.setRelatedOpeningElement(west_opening_copy);
|
||||
|
||||
// Up until now we have only used simple extrusions for the creation of the geometry. For the
|
||||
// ground mesh of the IfcSite we will use a Nurbs surface created in Open Cascade. The surface
|
||||
|
||||
// Up until now we have only used simple extrusions for the creation of the geometry. For the
|
||||
// ground mesh of the IfcSite we will use a Nurbs surface created in Open Cascade. The surface
|
||||
// will be tessellated using the deflection specified.
|
||||
TopoDS_Shape shape;
|
||||
createGroundShape(shape);
|
||||
@@ -325,7 +325,7 @@ int main() {
|
||||
site_prop.setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());
|
||||
site_prop.setRelatedObjects({file.getSingle<IfcSchema::IfcSite>()});
|
||||
site_prop.setRelatingPropertyDefinition(pset);
|
||||
|
||||
|
||||
auto ground_reps = file.getSingle<IfcSchema::IfcSite>().Representation().Representations();
|
||||
for (auto& rep : ground_reps) {
|
||||
rep.setContextOfItems(file.getRepresentationContext("Model"));
|
||||
@@ -334,11 +334,11 @@ int main() {
|
||||
setSurfaceColour(file,ground_representation, 0.15, 0.25, 0.05);
|
||||
|
||||
// According to the Ifc2x3 schema an IfcWallStandardCase needs to have an IfcMaterialLayerSet
|
||||
// assigned. Note that this material definition is independent of the surface styles we have
|
||||
// been assigning to the walls already. The surface styles determine the colour in the
|
||||
// assigned. Note that this material definition is independent of the surface styles we have
|
||||
// been assigning to the walls already. The surface styles determine the colour in the
|
||||
// '3D viewport' of most applications.
|
||||
// Some BIM authoring applications, such as Autodesk Revit, ignore the geometrical representation
|
||||
// by and large and construct native walls using the layer thickness and reference line offset
|
||||
// by and large and construct native walls using the layer thickness and reference line offset
|
||||
// provided here.
|
||||
auto material = file.create<IfcSchema::IfcMaterial>();
|
||||
material.setName("Brick");
|
||||
@@ -422,7 +422,7 @@ int main() {
|
||||
#endif
|
||||
|
||||
door.setRepresentation(file.addBox(80, 80, 2120, IfcSchema::IfcAxis2Placement2D{}, file.addPlacement3d(460, 0, 0)));
|
||||
|
||||
|
||||
auto door_representations = door.Representation().Representations();
|
||||
IfcSchema::IfcShapeRepresentation door_body;
|
||||
for (auto& rep : door_representations) {
|
||||
@@ -465,9 +465,9 @@ int main() {
|
||||
#endif
|
||||
|
||||
// Surface styles are assigned to representation items, hence there is no real limitation to
|
||||
// assign different colours within the same representation. However, some viewers have
|
||||
// difficulties rendering products with representation items with different surface styles.
|
||||
// Therefore we will construct the window as a decomposition of beams and a plate, in which
|
||||
// assign different colours within the same representation. However, some viewers have
|
||||
// difficulties rendering products with representation items with different surface styles.
|
||||
// Therefore we will construct the window as a decomposition of beams and a plate, in which
|
||||
// only the plate will have a transparent material assigned.
|
||||
|
||||
// The window frame will consists of four separate beams.
|
||||
@@ -476,7 +476,7 @@ int main() {
|
||||
// match the bounding box of the representation. Furthermore, the window placement needs
|
||||
// to align with the lowerleft corner of the constituent parts.
|
||||
std::vector<IfcSchema::IfcShapeRepresentation> frame_representations;
|
||||
|
||||
|
||||
auto horizontal_bar = file.addEmptyRepresentation();
|
||||
auto vertical_bar = file.addEmptyRepresentation();
|
||||
file.addBox(horizontal_bar, 1860, 90, 90);
|
||||
@@ -498,7 +498,7 @@ int main() {
|
||||
// Because of the duplication the iterator is incremented twice
|
||||
}
|
||||
|
||||
// This window will be placed at five locations within the building. A list of placements is
|
||||
// This window will be placed at five locations within the building. A list of placements is
|
||||
// created and is iterated over to create all window instances.
|
||||
std::vector<IfcSchema::IfcLocalPlacement> window_placements;
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, 2*-1770-430-930, -45, 400));
|
||||
@@ -506,7 +506,7 @@ int main() {
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, -430-930, -45, 400));
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, 3000-930, -45, 400));
|
||||
window_placements.push_back(file.addLocalPlacement(storey_placement, -4855+45, 885-930, 400, 0, 0, 1, 0, 1, 0));
|
||||
|
||||
|
||||
for (auto& place : window_placements) {
|
||||
|
||||
// Create the window at the current location
|
||||
@@ -520,7 +520,7 @@ int main() {
|
||||
window.setPredefinedType(IfcSchema::IfcWindowTypeEnum::IfcWindowType_WINDOW);
|
||||
window.setPartitioningType(IfcSchema::IfcWindowTypePartitioningEnum::IfcWindowTypePartitioning_SINGLE_PANEL);
|
||||
#endif
|
||||
file.addBuildingProduct(window);
|
||||
file.addBuildingProduct(window);
|
||||
|
||||
// Initialize a list of parts for the window to be composed of
|
||||
std::vector<IfcSchema::IfcObjectDefinition> window_parts;
|
||||
@@ -532,7 +532,7 @@ int main() {
|
||||
frame_placements.push_back(file.addLocalPlacement(storey_placement, 930, 45, 1510));
|
||||
frame_placements.push_back(file.addLocalPlacement(storey_placement, -885+930, 45, 90));
|
||||
frame_placements.push_back(file.addLocalPlacement(storey_placement, 885+930, 45, 90));
|
||||
|
||||
|
||||
// Now iterate over the placements and representations of the beam and add them to list of parts
|
||||
std::vector<IfcSchema::IfcLocalPlacement>::const_iterator frame_placement;
|
||||
std::vector<IfcSchema::IfcShapeRepresentation>::const_iterator frame_representation;
|
||||
@@ -565,7 +565,7 @@ int main() {
|
||||
window_parts.push_back(glass_part);
|
||||
file.relatePlacements(window, glass_part);
|
||||
setSurfaceColour(file, glass_part.Representation(), 0.6, 0.7, 0.75, 0.1);
|
||||
|
||||
|
||||
// Now create a decomposition relation between the window and the parts. Most viewers and authoring
|
||||
// tools will consider the window a single entity that can be selected as a whole.
|
||||
{
|
||||
@@ -612,10 +612,10 @@ void createGroundShape(TopoDS_Shape& shape) {
|
||||
cv.SetValue(4, 4, gp_Pnt( 10000, 10000, -8130));
|
||||
TColStd_Array1OfReal knots(0, 1);
|
||||
knots(0) = 0;
|
||||
knots(1) = 1;
|
||||
knots(1) = 1;
|
||||
TColStd_Array1OfInteger mult(0, 1);
|
||||
mult(0) = 5;
|
||||
mult(1) = 5;
|
||||
mult(1) = 5;
|
||||
Handle(Geom_BSplineSurface) surf = new Geom_BSplineSurface(cv, knots, knots, mult, mult, 4, 4);
|
||||
#if OCC_VERSION_HEX < 0x60502
|
||||
shape = BRepBuilderAPI_MakeFace(surf);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include INCLUDE_SCHEMA(ifcparse/schemas, IfcSchema)
|
||||
#include INCLUDE_SCHEMA_DEFINITIONS(ifcparse/schemas, IfcSchema)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifdef _MSC_VER
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
|
||||
@@ -151,7 +151,7 @@ void process_pset(element_properties& props, const T& inst) {
|
||||
|
||||
template <typename Schema>
|
||||
void get_psets_s(element_properties& props, const typename Schema::IfcObjectDefinition& inst) {
|
||||
// Extracts the property definitions for an IFC instance.
|
||||
// Extracts the property definitions for an IFC instance.
|
||||
if (auto tyob = inst.template as<typename Schema::IfcTypeObject>()) {
|
||||
if (tyob.HasPropertySets()) {
|
||||
auto defs = *tyob.HasPropertySets();
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
// to simplify alignment construction
|
||||
|
||||
// Disable warnings coming from IfcOpenShell
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable : 4018 4267 4250 4984 4985)
|
||||
#endif
|
||||
|
||||
#include "../ifcparse/schemas/Ifc4x3_add2.h"
|
||||
#include "../ifcparse/alignment_helper.h"
|
||||
|
||||
@@ -336,7 +336,7 @@ int main(int argc, char** argv) {
|
||||
std::string exterior_only_algo;
|
||||
|
||||
ifcopenshell::geom::settings settings;
|
||||
|
||||
|
||||
po::options_description geom_options("Geometry options");
|
||||
geom_options.add_options()
|
||||
("kernel", po::value<std::string>(&geometry_kernel)->default_value(default_kernel),
|
||||
@@ -389,7 +389,7 @@ int main(int argc, char** argv) {
|
||||
("model", "Specifies whether to include surfaces and solids in the output result. "
|
||||
"Typically these are representations of type Body or Facetation. ")
|
||||
;
|
||||
|
||||
|
||||
settings.define_options(geom_options);
|
||||
|
||||
std::string bounds;
|
||||
@@ -466,7 +466,7 @@ int main(int argc, char** argv) {
|
||||
num_threads = std::thread::hardware_concurrency();
|
||||
logger.notice("SYS", 7, "Using " + std::to_string(num_threads) + " threads");
|
||||
}
|
||||
|
||||
|
||||
if (vmap.count("log-format") == 1) {
|
||||
boost::to_lower(log_format);
|
||||
if (log_format == "plain") {
|
||||
@@ -479,7 +479,7 @@ int main(int argc, char** argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!filter_filename.empty()) {
|
||||
size_t num_filters = read_filters_from_file(ifcopenshell::path::to_utf8(filter_filename), include_filter, include_traverse_filter, exclude_filter, exclude_traverse_filter);
|
||||
if (num_filters) {
|
||||
@@ -524,10 +524,10 @@ int main(int argc, char** argv) {
|
||||
|
||||
// If no output filename is specified a Wavefront OBJ file will be output
|
||||
// to maintain backwards compatibility with the obsolete IfcObj executable.
|
||||
const path_t output_filename = vmap.count("output-file") == 1
|
||||
const path_t output_filename = vmap.count("output-file") == 1
|
||||
? vmap["output-file"].as<path_t>()
|
||||
: change_extension(input_filename, ifcopenshell::path::from_utf8(DEFAULT_EXTENSION));
|
||||
|
||||
|
||||
if (output_filename.size() < 5) {
|
||||
cerr_ << "[error] Invalid or unsupported output file '" << output_filename << "' given" << std::endl;
|
||||
print_usage();
|
||||
@@ -572,13 +572,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
path_t output_temp_filename = output_filename + ifcopenshell::path::from_utf8(TEMP_FILE_EXTENSION);
|
||||
|
||||
|
||||
std::vector<path_t> tokens;
|
||||
split(tokens, output_filename, boost::is_any_of("."));
|
||||
std::vector<path_t>::iterator tok_iter;
|
||||
path_t ext = *(tokens.end() - 1);
|
||||
path_t dot;
|
||||
dot = '.';
|
||||
dot = '.';
|
||||
path_t output_extension = dot + ext;
|
||||
|
||||
boost::to_lower(output_extension);
|
||||
@@ -785,7 +785,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
time_t start,end;
|
||||
time(&start);
|
||||
|
||||
|
||||
// @nb last argument true -> bypass_properties which are not read by any of the geometry serializers
|
||||
// Document serializers and IFC are already special-cased above
|
||||
// SVG requires properties for IfcAnnotation/DRAWING properties
|
||||
@@ -839,12 +839,12 @@ int main(int argc, char** argv) {
|
||||
|
||||
settings.get<ifcopenshell::geom::settings::ModelOffset>().value = offset;
|
||||
}
|
||||
|
||||
|
||||
if (is_tesselated && (center_model || center_model_geometry)) {
|
||||
std::vector<double> offset(3);
|
||||
|
||||
ifcopenshell::geom::iterator tmp_context_iterator(ifcopenshell::geom::kernels::construct(ifc_file, geometry_kernel, settings, logger), settings, ifc_file, filter_funcs, num_threads, logger);
|
||||
|
||||
|
||||
time_t bounds_start, bounds_end;
|
||||
time(&bounds_start);
|
||||
if (!quiet) logger.status("Computing bounds...");
|
||||
@@ -860,7 +860,7 @@ int main(int argc, char** argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tmp_context_iterator.compute_bounds(center_model_geometry);
|
||||
|
||||
time(&bounds_end);
|
||||
@@ -919,19 +919,19 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// The functions ifcopenshell::geom::iterator::get() and ifcopenshell::geom::iterator::next()
|
||||
// wrap an iterator of all geometrical products in the Ifc file.
|
||||
// wrap an iterator of all geometrical products in the Ifc file.
|
||||
// ifcopenshell::geom::iterator::get() returns an ifcopenshell::geom::triangulation_element or
|
||||
// -native_element pointer, based on current settings. (see iterator.h
|
||||
// for definition) ifcopenshell::geom::iterator::next() is used to poll whether more
|
||||
// geometrical entities are available. None of these functions throw
|
||||
// exceptions, neither for parsing errors or geometrical errors. Upon
|
||||
// calling next() the entity to be returned has already been processed, a
|
||||
// non-null return value guarantees that a successfully processed product is
|
||||
// available.
|
||||
// geometrical entities are available. None of these functions throw
|
||||
// exceptions, neither for parsing errors or geometrical errors. Upon
|
||||
// calling next() the entity to be returned has already been processed, a
|
||||
// non-null return value guarantees that a successfully processed product is
|
||||
// available.
|
||||
size_t num_created = 0;
|
||||
|
||||
while (true) {
|
||||
|
||||
|
||||
auto geom_object = context_iterator->get();
|
||||
|
||||
if (is_tesselated)
|
||||
@@ -967,7 +967,7 @@ int main(int argc, char** argv) {
|
||||
if (!context_iterator->next()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!no_progress && quiet) {
|
||||
for (; old_progress < 100; ++old_progress) {
|
||||
cout_ << ".";
|
||||
@@ -1086,7 +1086,7 @@ bool init_input_file(const std::string& filename, ifcopenshell::file*& ifc_file,
|
||||
ifc_file->bypass_type("IfcProfileProperties");
|
||||
ifc_file->bypass_type("IfcPhysicalQuantity");
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_MMAP
|
||||
if (mmap) {
|
||||
ifc_file->initialize(filename, mmap);
|
||||
@@ -1382,20 +1382,20 @@ void fix_quantities(ifcopenshell::file& f, bool no_progress, bool quiet, bool st
|
||||
auto person = latebound_access::create(f, "IfcPerson");
|
||||
latebound_access::set(person, "FamilyName", std::string("IfcOpenShell"));
|
||||
latebound_access::set(person, "GivenName", std::string("IfcOpenShell"));
|
||||
|
||||
|
||||
auto org = latebound_access::create(f, "IfcOrganization");
|
||||
latebound_access::set(org, "Name", std::string("IfcOpenShell"));
|
||||
|
||||
|
||||
auto pando = latebound_access::create(f, "IfcPersonAndOrganization");
|
||||
latebound_access::set(pando, "ThePerson", person);
|
||||
latebound_access::set(pando, "TheOrganization", org);
|
||||
|
||||
|
||||
auto application = latebound_access::create(f, "IfcApplication");
|
||||
latebound_access::set(application, "ApplicationDeveloper", org);
|
||||
latebound_access::set(application, "Version", std::string(IFCOPENSHELL_VERSION));
|
||||
latebound_access::set(application, "ApplicationFullName", std::string("IfcConvert"));
|
||||
latebound_access::set(application, "ApplicationIdentifier", std::string("IfcConvert") + IFCOPENSHELL_VERSION);
|
||||
|
||||
|
||||
auto ownerhist = latebound_access::create(f, "IfcOwnerHistory");
|
||||
latebound_access::set(ownerhist, "OwningUser", pando);
|
||||
latebound_access::set(ownerhist, "OwningApplication", application);
|
||||
@@ -1440,7 +1440,7 @@ void fix_quantities(ifcopenshell::file& f, bool no_progress, bool quiet, bool st
|
||||
latebound_access::set(quantity_area, "AreaValue", a);
|
||||
quantities.push_back(quantity_area);
|
||||
}
|
||||
|
||||
|
||||
if (geom_object->geometry().calculate_volume(a)) {
|
||||
auto quantity_volume = latebound_access::create(f, "IfcQuantityVolume");
|
||||
latebound_access::set(quantity_volume, "Name", std::string("Volume"));
|
||||
@@ -1461,13 +1461,13 @@ void fix_quantities(ifcopenshell::file& f, bool no_progress, bool quiet, bool st
|
||||
|
||||
std::vector<express::base> quantities_2;
|
||||
|
||||
for (auto& part : geom_object->geometry()) {
|
||||
for (auto& part : geom_object->geometry()) {
|
||||
auto quantity_count = latebound_access::create(f, "IfcQuantityCount");
|
||||
latebound_access::set(quantity_count, "Name", std::string("Surface Genus"));
|
||||
latebound_access::set(quantity_count, "Description", '#' + boost::lexical_cast<std::string>(part.ItemId()));
|
||||
latebound_access::set(quantity_count, "CountValue", (int64_t) part.shape()->surface_genus());
|
||||
|
||||
quantities_2.push_back(quantity_count);
|
||||
quantities_2.push_back(quantity_count);
|
||||
}
|
||||
|
||||
latebound_access::set(quantity_complex, "HasQuantities", quantities_2);
|
||||
|
||||
@@ -41,7 +41,7 @@ void fix_storeycontainment(ifcopenshell::file& f, bool no_progress, bool quiet,
|
||||
elem_to_storey[*it] = storey;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto storeys = f.instances_by_type("IfcBuildingStorey");
|
||||
std::vector<const ifcopenshell::IfcBaseClass*> storeys_sorted(storeys->begin(), storeys->end());
|
||||
@@ -98,7 +98,7 @@ void fix_storeycontainment(ifcopenshell::file& f, bool no_progress, bool quiet,
|
||||
std::wcout << "---" << std::endl;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
if (!context_iterator.initialize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ void fix_wallconnectivity(ifcopenshell::file& f, bool no_progress, bool quiet, b
|
||||
settings.get<ifcopenshell::geom::settings::DisableOpeningSubtractions>().value = true;
|
||||
|
||||
settings.get<ifcopenshell::geom::settings::OutputDimensionality>().value = ifcopenshell::geom::settings::CURVES;
|
||||
|
||||
|
||||
ifcopenshell::geom::converter c(ifcopenshell::geom::kernels::construct(&f, "cgal", settings, logger), &f, settings, logger);
|
||||
|
||||
auto rels = f.instances_by_type("IfcRelConnectsPathElements");
|
||||
@@ -54,7 +54,7 @@ void fix_wallconnectivity(ifcopenshell::file& f, bool no_progress, bool quiet, b
|
||||
if (!a_is_relating) {
|
||||
std::swap(a_type, b_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
auto a_poly = ifcopenshell::geom::utils::create_polyhedron(a.handle()->second);
|
||||
@@ -123,7 +123,7 @@ void fix_wallconnectivity(ifcopenshell::file& f, bool no_progress, bool quiet, b
|
||||
} else {
|
||||
auto p0 = boost::get<taxonomy::point3::ptr>(first_vertex);
|
||||
auto p1 = boost::get<taxonomy::point3::ptr>(last_vertex);
|
||||
|
||||
|
||||
auto v0 = taxonomy::cast<taxonomy::geom_item>(item)->matrix->ccomponents() * p0->ccomponents().homogeneous();
|
||||
auto v1 = taxonomy::cast<taxonomy::geom_item>(item)->matrix->ccomponents() * p1->ccomponents().homogeneous();
|
||||
|
||||
@@ -142,7 +142,7 @@ void fix_wallconnectivity(ifcopenshell::file& f, bool no_progress, bool quiet, b
|
||||
|
||||
auto pit = std::minmax_element(parameters.begin(), parameters.end());
|
||||
return std::make_pair(len, std::make_pair(CGAL::to_double(*pit.first), CGAL::to_double(*pit.second)));
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto& nan = std::numeric_limits<double>::quiet_NaN();
|
||||
return std::make_pair(nan, std::make_pair(nan, nan));
|
||||
|
||||
@@ -37,7 +37,7 @@ inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance = AL
|
||||
return fabs(a - b) < tolerance;
|
||||
}
|
||||
|
||||
namespace ifcopenshell {
|
||||
namespace ifcopenshell {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
@@ -70,7 +70,7 @@ namespace ifcopenshell {
|
||||
public:
|
||||
bool propagate_exceptions = false;
|
||||
bool partial_success_is_success = true;
|
||||
|
||||
|
||||
abstract_kernel(const std::string& geometry_library, const ifcopenshell::geom::settings& settings, ifcopenshell::logger& logger = ifcopenshell::logger::root())
|
||||
: geometry_library_(geometry_library)
|
||||
, settings_(settings)
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace geom {
|
||||
/// should return true if the geometry for the product is wanted to be included in the output.
|
||||
/// http://www.boost.org/doc/libs/1_62_0/doc/html/function/tutorial.html
|
||||
typedef std::function<bool(const express::base&)> filter_function;
|
||||
|
||||
|
||||
class IFC_GEOM_API abstract_mapping {
|
||||
protected:
|
||||
ifcopenshell::geom::settings settings_;
|
||||
@@ -106,7 +106,7 @@ namespace geom {
|
||||
|
||||
IFC_GEOM_API mapping_factory_implementation& mapping_implementations();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -510,20 +510,20 @@ namespace ifcopenshell::geom {
|
||||
virtual void triangulate(ifcopenshell::geom::settings settings, const ifcopenshell::geom::taxonomy::matrix4& place, triangulation* t, int item_id, int surface_style_id, ifcopenshell::logger& logger = ifcopenshell::logger::root()) const = 0;
|
||||
ifcopenshell::geom::triangulation* triangulate(const ifcopenshell::geom::settings& settings, ifcopenshell::logger& logger = ifcopenshell::logger::root()) const;
|
||||
virtual void serialize(const ifcopenshell::geom::taxonomy::matrix4& place, std::string&) const = 0;
|
||||
|
||||
|
||||
virtual int surface_genus() const = 0;
|
||||
virtual bool is_manifold() const = 0;
|
||||
|
||||
|
||||
virtual int num_vertices() const = 0;
|
||||
virtual int num_edges() const = 0;
|
||||
virtual int num_faces() const = 0;
|
||||
|
||||
|
||||
// @todo choose one prototype
|
||||
virtual double bounding_box(void*&) const = 0;
|
||||
// @todo this must be something with a virtual dtor so that we can delete it.
|
||||
virtual std::pair<opaque_coordinate<3>, opaque_coordinate<3>> bounding_box() const = 0;
|
||||
virtual void set_box(void* b) = 0;
|
||||
|
||||
|
||||
virtual opaque_number length() = 0;
|
||||
virtual opaque_number area() = 0;
|
||||
virtual opaque_number volume() = 0;
|
||||
@@ -550,11 +550,11 @@ namespace ifcopenshell::geom {
|
||||
virtual std::size_t map(opaque_coordinate<4>& from, opaque_coordinate<4>& to) = 0;
|
||||
virtual std::size_t map(const std::vector<opaque_coordinate<4>>& from, const std::vector<opaque_coordinate<4>>& to) = 0;
|
||||
virtual conversion_result_shape* moved(ifcopenshell::geom::taxonomy::matrix4::ptr) const = 0;
|
||||
|
||||
|
||||
virtual bool surface_area_along_direction(double tol, const ifcopenshell::geom::taxonomy::matrix4::ptr&, double& along_x, double& along_y, double& along_z) const = 0;
|
||||
|
||||
virtual ~conversion_result_shape() {}
|
||||
|
||||
|
||||
};
|
||||
|
||||
class IFC_GEOM_API conversion_result {
|
||||
|
||||
@@ -498,7 +498,7 @@ namespace ifcopenshell {
|
||||
static constexpr const char* const description = "Slight variation of --model-offset where large offsets are applied by negating existing large offsets to retain maximum precision. Requires --no-parallel-mapping.";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
namespace impl {
|
||||
template <typename T>
|
||||
struct readable_name {
|
||||
|
||||
@@ -19,7 +19,7 @@ ifcopenshell::geom::converter::~converter() {
|
||||
|
||||
ifcopenshell::geom::native_element* ifcopenshell::geom::converter::create_brep_for_representation_and_product(taxonomy::ptr representation_node, const express::base product_, const taxonomy::matrix4::ptr& place_) {
|
||||
auto product = product_.as<express::entity>();
|
||||
|
||||
|
||||
std::stringstream representation_id_builder;
|
||||
|
||||
auto place = place_;
|
||||
@@ -32,7 +32,7 @@ ifcopenshell::geom::native_element* ifcopenshell::geom::converter::create_brep_f
|
||||
if (!kernel_->convert(representation_node, shapes)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (settings_.get<ifcopenshell::geom::settings::ApplyLayerSets>().get()) {
|
||||
ifcopenshell::geom::layerset_information layerinfo;
|
||||
std::vector<ifcopenshell::geom::endpoint_connection> neighbours;
|
||||
@@ -55,7 +55,7 @@ ifcopenshell::geom::native_element* ifcopenshell::geom::converter::create_brep_f
|
||||
/*
|
||||
if (util::flatten_shape_list(shapes, merge, false, getValue(GV_PRECISION))) {
|
||||
if (util::count(merge, TopAbs_FACE) > 0) {
|
||||
|
||||
|
||||
if (convert_layerset(product, layers, styles, thickness)) {
|
||||
|
||||
IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations();
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace ifcopenshell { namespace geom {
|
||||
ifcopenshell::geom::kernels::abstract_kernel* kernel() { return &*kernel_; }
|
||||
|
||||
converter(std::unique_ptr<ifcopenshell::geom::kernels::abstract_kernel>&& geometry_library, ifcopenshell::file* file, ifcopenshell::geom::settings& settings, ifcopenshell::logger& logger = ifcopenshell::logger::root());
|
||||
|
||||
|
||||
~converter();
|
||||
|
||||
ifcopenshell::geom::abstract_mapping* mapping() const { return mapping_; }
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace ifcopenshell::geom {
|
||||
}
|
||||
}
|
||||
const ifcopenshell::geom::taxonomy::matrix4::ptr& data() const {
|
||||
if (matrix_orig_units_) {
|
||||
if (matrix_orig_units_) {
|
||||
return matrix_orig_units_;
|
||||
}
|
||||
if (matrix_) {
|
||||
@@ -131,7 +131,7 @@ namespace ifcopenshell::geom {
|
||||
const std::string& guid, const std::string& context, const ifcopenshell::geom::taxonomy::matrix4::ptr& trsf, const express::entity& product)
|
||||
: _id(id), _parent_id(parent_id), _name(name), _type(type), _guid(guid), _context(context), _transformation(settings, trsf)
|
||||
, product_(product)
|
||||
{
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
if (type == "IfcProject") {
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
// A purposely empty file so that the unrolled loop
|
||||
// can overflow into an existing empty include file.
|
||||
// can overflow into an existing empty include file.
|
||||
|
||||
@@ -135,7 +135,7 @@ struct cant_fn_evaluator : public fn_evaluator {
|
||||
auto g = gradient_evaluator_.evaluate(u);
|
||||
auto c = cant_evaluator_.evaluate(u);
|
||||
|
||||
|
||||
|
||||
// curvature is stored in row 3 - capture it and remove it from the xy and uz matrices
|
||||
// so the matrix operations (ie multiplication) works correctly
|
||||
auto gradient_curvature = g.row(3);
|
||||
|
||||
@@ -13,8 +13,8 @@ IFC_GEOM_API std::vector<double> helmert_curve_point(double A0, double A1, doubl
|
||||
/// This is intended to be used from python side. Polylines are mapped to a loop, but when
|
||||
/// representing an alignment they need to be a function_item so the can be evaluated by function_item_evaluator.
|
||||
/// On the C++ side, the dcast operator take care of this, but dcast is not accessible on the python side.
|
||||
/// @param loop
|
||||
/// @return
|
||||
/// @param loop
|
||||
/// @return
|
||||
inline taxonomy::function_item::ptr convert_loop_to_function_item(taxonomy::loop::ptr loop) {
|
||||
return ifcopenshell::geom::taxonomy::dcast<taxonomy::function_item>(loop);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ taxonomy::loft::ptr ifcopenshell::geom::make_loft(const ifcopenshell::geom::sett
|
||||
while (dist_along > *(profile_index + 1)) {
|
||||
profile_index++;
|
||||
if (profile_index == longitudes.end()) {
|
||||
// @todo handle this?
|
||||
// @todo handle this?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ taxonomy::loft::ptr ifcopenshell::geom::make_loft(const ifcopenshell::geom::sett
|
||||
}
|
||||
interpolated->matrix->components() = lerp(m4a, m4b, relative_dist_along);
|
||||
}
|
||||
|
||||
|
||||
auto interpolated_offset = lerp(offset_a, offset_b, relative_dist_along);
|
||||
if (rotation_a == rotation_b && rotation_a) {
|
||||
// @todo we don't support an overridden rotation on only one of the placements
|
||||
@@ -215,7 +215,7 @@ taxonomy::loft::ptr ifcopenshell::geom::make_loft(const ifcopenshell::geom::sett
|
||||
std::vector<taxonomy::point3::ptr> points;
|
||||
std::vector<std::set<std::string>> tags;
|
||||
std::vector<std::string>::const_iterator tag_it;
|
||||
|
||||
|
||||
if (!loop->closed.value_or(false)) {
|
||||
points = {std::get<taxonomy::point3::ptr>(loop->children[0]->start)};
|
||||
if (input_tags) {
|
||||
@@ -352,7 +352,7 @@ taxonomy::loft::ptr ifcopenshell::geom::make_loft(const ifcopenshell::geom::sett
|
||||
for (auto& x : tags_for_this_point_on_subsequent_profile) {
|
||||
points.push_back(taxonomy::make<taxonomy::point3>(p3));
|
||||
common_tags_vec.push_back(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto tmp__ : boost::combine(w1_points, w2_points)) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace ifcopenshell {
|
||||
|
||||
namespace geom {
|
||||
|
||||
|
||||
struct IFC_GEOM_API cross_section {
|
||||
double dist_along;
|
||||
taxonomy::geom_item::ptr section_geometry;
|
||||
|
||||
@@ -624,7 +624,7 @@ std::unique_ptr<ifcopenshell::geom::element> ifcopenshell::geom::iterator::get()
|
||||
hasParent = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add the previously found parent to the vector
|
||||
hasParent = hasParent && parent_object->parent_id() != -1;
|
||||
}
|
||||
@@ -688,13 +688,13 @@ express::base ifcopenshell::geom::iterator::create() {
|
||||
}
|
||||
|
||||
ifcopenshell::geom::taxonomy::direction3::ptr ifcopenshell::geom::iterator::remove_offset_() {
|
||||
|
||||
|
||||
using namespace ifcopenshell::geom::taxonomy;
|
||||
|
||||
|
||||
if (!settings_.get<ifcopenshell::geom::settings::MaxOffset>().has()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
if (!settings_.get<ifcopenshell::geom::settings::NoParallelMapping>().get()) {
|
||||
throw std::runtime_error("remove_offset() can only be called with defer-processing-first-element and no-parallel-mapping settings");
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace ifcopenshell::geom {
|
||||
bool task_result_ptr_initialized = false;
|
||||
bool task_result_ptr_exhausted = false;
|
||||
size_t async_elements_returned_ = 0;
|
||||
|
||||
|
||||
ifcopenshell::geom::settings settings_;
|
||||
ifcopenshell::file* ifc_file;
|
||||
std::vector<ifcopenshell::geom::filter_function> filters_;
|
||||
@@ -138,7 +138,7 @@ namespace ifcopenshell::geom {
|
||||
|
||||
// When single-threaded
|
||||
ifcopenshell::geom::converter* converter_;
|
||||
|
||||
|
||||
// When multi-threaded
|
||||
std::vector<ifcopenshell::geom::converter*> kernel_pool;
|
||||
std::vector<std::unique_ptr<ifcopenshell::logger>> worker_loggers_;
|
||||
|
||||
@@ -296,7 +296,7 @@ ifcopenshell::geom::cgal_shape::cgal_shape(const cgal_polyhedron& shape, bool co
|
||||
};
|
||||
|
||||
std::vector<CGAL::Point_2<kernel_>> ps;
|
||||
|
||||
|
||||
for (auto& he1 : CGAL::halfedges_around_face(face->halfedge(), poly)) {
|
||||
const auto& source = he1->vertex()->point();
|
||||
ps.push_back(transform_point(source));
|
||||
@@ -345,7 +345,7 @@ void ifcopenshell::geom::cgal_shape::to_poly() const {
|
||||
CGAL::Polygon_mesh_processing::orient_to_bound_a_volume(poly);
|
||||
}
|
||||
shape_ = poly;
|
||||
|
||||
|
||||
// nef_->convert_to_polyhedron(*shape_);
|
||||
}
|
||||
}
|
||||
@@ -384,7 +384,7 @@ void ifcopenshell::geom::cgal_shape::triangulate(ifcopenshell::geom::settings se
|
||||
}
|
||||
|
||||
const bool setting_use_original_edges = settings.get<ifcopenshell::geom::settings::CgalEmitOriginalEdges>().get();
|
||||
|
||||
|
||||
std::set<std::set<kernel_::Point_3>> original_edges;
|
||||
if (setting_use_original_edges) {
|
||||
for (auto it = shape_to_use->edges_begin(); it != shape_to_use->edges_end(); ++it) {
|
||||
@@ -457,7 +457,7 @@ void ifcopenshell::geom::cgal_shape::triangulate(ifcopenshell::geom::settings se
|
||||
|
||||
// std::map<cgal_vertex_descriptor, kernel_::Vector_3> vertex_normals;
|
||||
// boost::associative_property_map<std::map<cgal_vertex_descriptor, kernel_::Vector_3>> vertex_normals_map(vertex_normals);
|
||||
|
||||
|
||||
// triangulate the shape and compute the normals
|
||||
std::map<facet_const_handle, kernel_::Vector_3> face_normals;
|
||||
boost::associative_property_map<std::map<facet_const_handle, kernel_::Vector_3>> face_normals_map(face_normals);
|
||||
@@ -556,7 +556,7 @@ void ifcopenshell::geom::cgal_shape::triangulate(ifcopenshell::geom::settings se
|
||||
is_face_boundary[i] = setting_use_original_edges
|
||||
? original_edges.find({ current_halfedge->vertex()->point(), current_halfedge->prev()->vertex()->point() }) != original_edges.end()
|
||||
: facet_to_component[face] != facet_to_component[current_halfedge->opposite()->face()];
|
||||
|
||||
|
||||
++i;
|
||||
++num_vertices;
|
||||
++current_halfedge;
|
||||
@@ -764,7 +764,7 @@ opaque_coordinate<3> ifcopenshell::geom::cgal_shape::position()
|
||||
for (auto it = shp.points_begin(); it != shp.points_end(); ++it) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
p[i] += it->cartesian(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
kernel_::FT N(static_cast<double>(std::distance(shp.points_begin(), shp.points_end())));
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
|
||||
@@ -84,7 +84,7 @@ CGAL::Polyhedron_3<kernel_> ifcopenshell::geom::utils::create_polyhedron(std::li
|
||||
// fresult.close();
|
||||
return CGAL::Polyhedron_3<kernel_>();
|
||||
}
|
||||
|
||||
|
||||
// std::cout << "After: " << polyhedron.size_of_vertices() << " vertices and " << polyhedron.size_of_facets() << " facets" << std::endl;
|
||||
|
||||
return polyhedron;
|
||||
@@ -223,7 +223,7 @@ bool cgal_kernel::convert(const taxonomy::shell::ptr l, cgal_polyhedron& shape)
|
||||
} else {
|
||||
logger().message(ifcopenshell::logger::LOG_ERROR, "Failed to convert face:", f->instance);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// std::cout << "Face in ConnectedFaceSet: " << std::endl;
|
||||
@@ -673,9 +673,9 @@ namespace {
|
||||
|
||||
namespace {
|
||||
void face_to_poly_with_holes(const cgal_face& face, CGAL::Polygon_with_holes_2<kernel_>& pwh, CGAL::Aff_transformation_3<kernel_>& place) {
|
||||
// static
|
||||
// static
|
||||
kernel_::Vector_3 Z(0, 0, 1);
|
||||
// static
|
||||
// static
|
||||
kernel_::Vector_3 X(1, 0, 0);
|
||||
|
||||
auto refz = newell(face.outer);
|
||||
@@ -912,7 +912,7 @@ bool ifcopenshell::geom::kernels::cgal_kernel::convert_openings(const express::b
|
||||
#else
|
||||
CGAL::Nef_nary_union_3<CGAL::Nef_polyhedron_3<kernel_>> second_operand_collector;
|
||||
size_t second_operand_collector_size = 0;
|
||||
|
||||
|
||||
std::list<std::pair<express::base, std::list<cgal_polyhedron>>> operands;
|
||||
|
||||
std::list<express::base> second_operand_instances;
|
||||
@@ -1483,7 +1483,7 @@ bool cgal_kernel::preprocess_boolean_operand(const express::base& log_reference,
|
||||
for (auto& nef : first_operands_nef) {
|
||||
// @todo eliminate this copy (= to remove const)
|
||||
auto nef_copy = nef;
|
||||
auto tree = build_halfspace_tree_decomposed(nef_copy, planes_fixed);
|
||||
auto tree = build_halfspace_tree_decomposed(nef_copy, planes_fixed);
|
||||
}
|
||||
{
|
||||
// @nb we snap internally as well...
|
||||
@@ -1551,7 +1551,7 @@ bool cgal_kernel::preprocess_boolean_operand(const express::base& log_reference,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
{
|
||||
@@ -2026,7 +2026,7 @@ bool cgal_kernel::convert_impl(const taxonomy::boolean_result::ptr br, std::vect
|
||||
if (!convert(face, fs) || fs.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
auto& w = fs.front().outer;
|
||||
CGAL::Polygon_2<kernel_> ps;
|
||||
for (auto& wire_point : w) {
|
||||
@@ -2037,7 +2037,7 @@ bool cgal_kernel::convert_impl(const taxonomy::boolean_result::ptr br, std::vect
|
||||
continue;
|
||||
}
|
||||
|
||||
// static
|
||||
// static
|
||||
auto z = taxonomy::make<taxonomy::direction3>(0, 0, 1);
|
||||
cgal_polyhedron poly;
|
||||
process_extrusion(fs.front(), z, 200, poly);
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace ifcopenshell {
|
||||
class IFC_GEOMLIBRARY_API cgal_kernel : public abstract_kernel {
|
||||
private:
|
||||
#ifndef IFOPSH_SIMPLE_KERNEL
|
||||
enum boolean_operand_preprocess {
|
||||
enum boolean_operand_preprocess {
|
||||
PP_MINKOWSKY_DILATE,
|
||||
PP_SNAP_POINTS_TO_FIRST_OPERAND,
|
||||
PP_SNAP_PLANES_TO_FIRST_OPERAND,
|
||||
|
||||
@@ -95,7 +95,7 @@ std::string dump_facet(typename CGAL::Nef_polyhedron_3<Kernel>::Halffacet_const_
|
||||
|
||||
const auto& p = h->plane();
|
||||
oss << "Facet plane=" << p << std::endl;
|
||||
|
||||
|
||||
auto fc = h->facet_cycles_begin();
|
||||
auto se = shalfedge_const_handle(fc);
|
||||
CGAL_assertion(se != 0);
|
||||
@@ -187,7 +187,7 @@ plane_map<Kernel> snap_halfspaces(const std::list<CGAL::Plane_3<Kernel>>& planes
|
||||
|
||||
fuzzy_sphere fs(query, search_radius, 0.);
|
||||
// std::cout << "q " << query << std::endl;
|
||||
|
||||
|
||||
std::list<point_d> results_pos, results_neg;
|
||||
kdtree.search(std::back_inserter(results_pos), fs);
|
||||
|
||||
@@ -801,7 +801,7 @@ void bfs(graph<Kernel>& g, size_t start_vertex, Fn& fn) {
|
||||
for (boost::tie(ei, ei_end) = boost::out_edges(cur, g); ei != ei_end; ++ei) {
|
||||
auto s = boost::source(*ei, g);
|
||||
auto t = boost::target(*ei, g);
|
||||
|
||||
|
||||
// @todo is this necessary?
|
||||
if (cur == t) {
|
||||
std::swap(s, t);
|
||||
@@ -863,10 +863,10 @@ std::unique_ptr<halfspace_tree<TreeKernel>> build_halfspace_tree(graph<Kernel>&
|
||||
int largest_component_idx = -1;
|
||||
|
||||
int num_components = 0;
|
||||
|
||||
|
||||
// @nb we don't just randomly start from an arbitrary seed, but we sort planes by d / | abc |
|
||||
// for (size_t i = 0; i < boost::num_vertices(sub_graph_0); ++i) {
|
||||
|
||||
|
||||
std::vector<size_t> sorted_verts;
|
||||
for (size_t i = 0; i < boost::num_vertices(sub_graph_0); ++i) {
|
||||
sorted_verts.push_back(i);
|
||||
@@ -1232,7 +1232,7 @@ std::unique_ptr<halfspace_tree<TreeKernel>> build_halfspace_tree_decomposed(cons
|
||||
// directly, so for now we need to isolate the individual volumes.
|
||||
CGAL::Polyhedron_3<Kernel> P;
|
||||
poly.convert_inner_shell_to_polyhedron(ci->shells_begin(), P);
|
||||
CGAL::Nef_polyhedron_3<Kernel> Pnef(P);
|
||||
CGAL::Nef_polyhedron_3<Kernel> Pnef(P);
|
||||
|
||||
for (auto it = Pnef.halffacets_begin(); it != Pnef.halffacets_end(); ++it) {
|
||||
if (it->incident_volume()->mark()) {
|
||||
@@ -1315,7 +1315,7 @@ size_t edge_contract(graph<Kernel>& G) {
|
||||
bool exists = boost::edge(srcid, tt, G).second;
|
||||
if (!exists) {
|
||||
boost::add_edge(srcid, tt, G);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++n;
|
||||
|
||||
@@ -19,7 +19,7 @@ struct IFC_GEOMLIBRARY_API manifold_part {
|
||||
auto copy = s;
|
||||
copy.CalculateNormals(3);
|
||||
mesh = copy.GetMeshGL64();
|
||||
solid = s;
|
||||
solid = s;
|
||||
}
|
||||
|
||||
manifold_part(const manifold::MeshGL64& s) : mesh(s) {}
|
||||
|
||||
@@ -114,10 +114,10 @@ namespace {
|
||||
mesh_type build() const {
|
||||
mesh_type mesh;
|
||||
mesh.numProp = 3;
|
||||
|
||||
|
||||
std::vector<size_t> vertex_use_count(vertices.size(), 0);
|
||||
std::vector<Eigen::Vector3d> vertex_normals(vertices.size(), Eigen::Vector3d::Zero());
|
||||
|
||||
|
||||
for (size_t i = 0; i < tri_verts.size(); i += 3) {
|
||||
for (size_t j = 0; j < 3; ++j) {
|
||||
vertex_use_count[tri_verts[i + j]]++;
|
||||
@@ -1242,13 +1242,13 @@ namespace {
|
||||
}
|
||||
|
||||
std::optional<part> part_from_halfspace_solid(halfspace_build_state& state, const taxonomy::solid::ptr& solid, const taxonomy::face::ptr& face,const manifold::Box& reference_box, double precision, double dilation) {
|
||||
|
||||
|
||||
auto plane = taxonomy::cast<taxonomy::plane>(face->basis);
|
||||
|
||||
// @todo verify order
|
||||
const auto transform = matrix_or_identity(solid->matrix) * matrix_or_identity(plane->matrix);
|
||||
const auto extrusion_dir = matrix_or_identity(solid->matrix).col(2).head<3>().eval();
|
||||
|
||||
|
||||
Eigen::Vector3d x = transform.col(0).head<3>();
|
||||
Eigen::Vector3d y = transform.col(1).head<3>();
|
||||
Eigen::Vector3d normal = transform.col(2).head<3>();
|
||||
@@ -1281,7 +1281,7 @@ namespace {
|
||||
const auto delta = corner - origin;
|
||||
const auto u = delta.dot(x);
|
||||
const auto v = delta.dot(y);
|
||||
|
||||
|
||||
u_min = std::min(u_min, u);
|
||||
u_max = std::max(u_max, u);
|
||||
v_min = std::min(v_min, v);
|
||||
|
||||
@@ -58,7 +58,7 @@ double ifcopenshell::geom::util::min_edge_length(const TopoDS_Shape & a) {
|
||||
TopExp_Explorer exp(a, TopAbs_EDGE);
|
||||
for (; exp.More(); exp.Next()) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(exp.Current());
|
||||
|
||||
|
||||
TopoDS_Vertex v0, v1;
|
||||
TopExp::Vertices(e, v0, v1);
|
||||
if (!v0.IsNull() && !v1.IsNull() && v0.IsSame(v1)) {
|
||||
|
||||
@@ -43,8 +43,8 @@ bool is_intersect_ray_box(const struct ray *ray, const struct box *box) {
|
||||
// https://github.com/NVIDIA-Omniverse/PhysX/blob/main/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h
|
||||
// With minor modifications to use gp_Vec type.
|
||||
// More reading: https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
|
||||
bool intersectRayTriangle( const gp_Vec& orig, const gp_Vec& dir,
|
||||
const gp_Vec& vert0, const gp_Vec& vert1, const gp_Vec& vert2,
|
||||
bool intersectRayTriangle( const gp_Vec& orig, const gp_Vec& dir,
|
||||
const gp_Vec& vert0, const gp_Vec& vert1, const gp_Vec& vert2,
|
||||
double& at, double& au, double& av,
|
||||
bool cull, float enlarge) {
|
||||
// Find vectors for two edges sharing vert0
|
||||
@@ -147,7 +147,7 @@ void edgeEdgeDist(gp_Vec& x, gp_Vec& y, // closest points
|
||||
const double Denom = ADotA*BDotB - ADotB*ADotB;
|
||||
|
||||
double t; // We will clamp result so t is on the segment (p, a)
|
||||
if(Denom!=0.0)
|
||||
if(Denom!=0.0)
|
||||
t = ios_clamp((ADotT*BDotB - BDotT*ADotB) / Denom, 0.0, 1.0);
|
||||
else
|
||||
t = 0.0;
|
||||
@@ -268,7 +268,7 @@ double distanceTriangleTriangleSquared(gp_Vec& cp, gp_Vec& cq, const std::array<
|
||||
if(Tp[2]>Tp[index]) index = 2;
|
||||
}
|
||||
|
||||
if(index >= 0)
|
||||
if(index >= 0)
|
||||
{
|
||||
shown_disjoint = true;
|
||||
|
||||
@@ -297,7 +297,7 @@ double distanceTriangleTriangleSquared(gp_Vec& cp, gp_Vec& cq, const std::array<
|
||||
|
||||
gp_Vec Tn = Tv[0].Crossed(Tv[1]);
|
||||
double Tnl = Tn.Dot(Tn);
|
||||
|
||||
|
||||
if(Tnl>1e-15f)
|
||||
{
|
||||
const std::array<double, 3> Sp = {(q[0] - p[0]).Dot(Tn),
|
||||
@@ -317,7 +317,7 @@ double distanceTriangleTriangleSquared(gp_Vec& cp, gp_Vec& cq, const std::array<
|
||||
}
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
{
|
||||
shown_disjoint = true;
|
||||
|
||||
const gp_Vec& pIndex = p[index];
|
||||
@@ -525,11 +525,11 @@ bool trianglesIntersectCoplanar(const gp_Vec& p1_n, const gp_Vec& a1, const gp_V
|
||||
|
||||
const double third = (1.0 / 3.0);
|
||||
|
||||
//A bit of the computations done inside the following functions could be shared but it's kept simple since the
|
||||
//A bit of the computations done inside the following functions could be shared but it's kept simple since the
|
||||
//difference is not very big and the coplanar case is not expected to be the most common case
|
||||
if (linesIntersect(a1, b1, a2, b2, x, y) || linesIntersect(a1, b1, b2, c2, x, y) || linesIntersect(a1, b1, c2, a2, x, y) ||
|
||||
linesIntersect(b1, c1, a2, b2, x, y) || linesIntersect(b1, c1, b2, c2, x, y) || linesIntersect(b1, c1, c2, a2, x, y) ||
|
||||
linesIntersect(c1, a1, a2, b2, x, y) || linesIntersect(c1, a1, b2, c2, x, y) || linesIntersect(c1, a1, c2, a2, x, y) ||
|
||||
linesIntersect(c1, a1, a2, b2, x, y) || linesIntersect(c1, a1, b2, c2, x, y) || linesIntersect(c1, a1, c2, a2, x, y) ||
|
||||
pointInTriangle(a1, b1, c1, third * (a2 + b2 + c2), x, y) || pointInTriangle(a2, b2, c2, third * (a1 + b1 + c1), x, y))
|
||||
return true;
|
||||
|
||||
@@ -557,7 +557,7 @@ bool trianglesIntersect(const gp_Vec& a1, const gp_Vec& b1, const gp_Vec& c1, co
|
||||
|
||||
if ((p1ToA > 0) == (p1ToB > 0) && (p1ToA > 0) == (p1ToC > 0))
|
||||
return false; //All points of triangle 2 on same side of triangle 1 -> no intersection
|
||||
|
||||
|
||||
gp_Dir p2_n((b2 - a2).Crossed(c2 - a2).Normalized());
|
||||
double p2_d = -a2.Dot(p2_n);
|
||||
// const PxPlane p2(a2, b2, c2);
|
||||
@@ -566,7 +566,7 @@ bool trianglesIntersect(const gp_Vec& a1, const gp_Vec& b1, const gp_Vec& c1, co
|
||||
const double p2ToC = c1.Dot(p2_n) + p2_d;
|
||||
|
||||
if ((p2ToA > 0) == (p2ToB > 0) && (p2ToA > 0) == (p2ToC > 0))
|
||||
return false; //All points of triangle 1 on same side of triangle 2 -> no intersection
|
||||
return false; //All points of triangle 1 on same side of triangle 2 -> no intersection
|
||||
|
||||
gp_Vec intersectionDirection = p1_n.Crossed(p2_n);
|
||||
const double l2 = intersectionDirection.SquareMagnitude();
|
||||
|
||||
@@ -20,7 +20,7 @@ struct IFC_GEOMLIBRARY_API box {
|
||||
IFC_GEOMLIBRARY_API bool is_intersect_ray_box(const struct ray *ray, const struct box *box);
|
||||
|
||||
IFC_GEOMLIBRARY_API bool intersectRayTriangle( const gp_Vec& orig, const gp_Vec& dir,
|
||||
const gp_Vec& vert0, const gp_Vec& vert1, const gp_Vec& vert2,
|
||||
const gp_Vec& vert0, const gp_Vec& vert1, const gp_Vec& vert2,
|
||||
double& at, double& au, double& av,
|
||||
bool cull, float enlarge=0.0f);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ bool open_cascade_kernel::convert(const taxonomy::extrusion::ptr extrusion, Topo
|
||||
|
||||
if (face.ShapeType() == TopAbs_COMPOUND) {
|
||||
|
||||
// For compounds (most likely the result of a IfcCompositeProfileDef)
|
||||
// For compounds (most likely the result of a IfcCompositeProfileDef)
|
||||
// create a compound solid shape.
|
||||
|
||||
TopExp_Explorer exp(face, TopAbs_FACE);
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace {
|
||||
|
||||
auto crv = get_curve(e->basis);
|
||||
result = Handle(Geom_Surface)(new Geom_SurfaceOfRevolution(
|
||||
crv, ax
|
||||
crv, ax
|
||||
));
|
||||
|
||||
result->Transform(tr);
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace ifcopenshell::geom {
|
||||
std::pair<wire_it, wire_it> inner_wires() const {
|
||||
return { wires_.begin() + 1, wires_.end() };
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ bool open_cascade_kernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape&
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NCollection_List<TopoDS_Shape> faces;
|
||||
TopoDS_Compound comp;
|
||||
BRep_Builder BB;
|
||||
@@ -330,7 +330,7 @@ bool open_cascade_kernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape&
|
||||
std::array<std::vector<std::vector<std::set<std::string>>>::const_iterator, 2> tag_pairs = {
|
||||
all_tags.begin() + std::distance(shps.begin(), it),
|
||||
all_tags.begin() + std::distance(shps.begin(), jt)};
|
||||
|
||||
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> ancestors;
|
||||
const auto& wire = wp[i];
|
||||
@@ -357,7 +357,7 @@ bool open_cascade_kernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape&
|
||||
|
||||
for (NCollection_List<TopoDS_Shape>::Iterator edge_it(incidentEdges); edge_it.More(); edge_it.Next()) {
|
||||
const TopoDS_Edge& e = TopoDS::Edge(edge_it.Value());
|
||||
|
||||
|
||||
TopoDS_Vertex ev0, ev1;
|
||||
TopExp::Vertices(e, ev0, ev1);
|
||||
|
||||
@@ -405,11 +405,11 @@ bool open_cascade_kernel::convert(const taxonomy::loft::ptr loft, TopoDS_Shape&
|
||||
++d;
|
||||
} else {
|
||||
throw std::runtime_error("Unable to construct surface");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& wp : ws) {
|
||||
BRepTools_WireExplorer a(wp[0]);
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace {
|
||||
BRep_Tool::Pnt(v1).DumpJson(oss);
|
||||
auto osss = oss.str();
|
||||
std::wcout << osss.c_str() << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
BRep_Builder B;
|
||||
TopoDS_Wire W;
|
||||
|
||||
@@ -75,7 +75,7 @@ void ifcopenshell::geom::open_cascade_shape::triangulate(ifcopenshell::geom::set
|
||||
|
||||
// A 3x3 matrix to rotate the vertex normals
|
||||
std::optional<gp_Mat> rotation_matrix;
|
||||
|
||||
|
||||
if (place.components_) {
|
||||
const auto& m = *place.components_;
|
||||
rotation_matrix.emplace(
|
||||
@@ -84,7 +84,7 @@ void ifcopenshell::geom::open_cascade_shape::triangulate(ifcopenshell::geom::set
|
||||
m(2, 0), m(2, 1), m(2, 2)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// When welding vertices, vertex coords will be shared among faces so we need to per-shape set
|
||||
// to keep track of which edges were already emitted.
|
||||
std::set<std::pair<int, int>> emitted_edges;
|
||||
@@ -119,9 +119,9 @@ void ifcopenshell::geom::open_cascade_shape::triangulate(ifcopenshell::geom::set
|
||||
for (exp.Init(shape_, TopAbs_FACE); exp.More(); exp.Next(), ++num_faces) {
|
||||
TopoDS_Face face = TopoDS::Face(exp.Current());
|
||||
|
||||
size_t num_bounds = 0;
|
||||
size_t num_bounds = 0;
|
||||
for (TopoDS_Iterator it(face); it.More(); it.Next(), ++num_bounds) {}
|
||||
|
||||
|
||||
const bool is_planar = BRep_Tool::Surface(face) && BRep_Tool::Surface(face)->DynamicType() == STANDARD_TYPE(Geom_Plane);
|
||||
const bool has_inner_bounds = num_bounds > 1;
|
||||
|
||||
@@ -314,7 +314,7 @@ void ifcopenshell::geom::open_cascade_shape::triangulate(ifcopenshell::geom::set
|
||||
} else {
|
||||
p = tessellater.Value(i).XYZ();
|
||||
}
|
||||
|
||||
|
||||
auto p_local = p;
|
||||
taxonomy_transform(place.components_, p);
|
||||
|
||||
@@ -582,7 +582,7 @@ conversion_result_shape* ifcopenshell::geom::open_cascade_shape::concat(conversi
|
||||
{
|
||||
TopoDS_Compound compound;
|
||||
BRep_Builder builder;
|
||||
|
||||
|
||||
auto& left = shape_;
|
||||
auto& right = ((ifcopenshell::geom::open_cascade_shape*)other)->shape_;
|
||||
|
||||
@@ -594,7 +594,7 @@ conversion_result_shape* ifcopenshell::geom::open_cascade_shape::concat(conversi
|
||||
builder.MakeCompound(compound);
|
||||
builder.Add(compound, left);
|
||||
}
|
||||
|
||||
|
||||
builder.Add(compound, right);
|
||||
|
||||
return new open_cascade_shape(std::move(compound));
|
||||
|
||||
@@ -84,7 +84,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_openings(const express::ba
|
||||
// opening_trsf = relative;
|
||||
|
||||
std::vector<ifcopenshell::geom::conversion_result> opening_shapes;
|
||||
|
||||
|
||||
// @todo
|
||||
abstract_kernel::convert(op.first, opening_shapes);
|
||||
|
||||
@@ -309,13 +309,13 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
|
||||
// IfcSchema::IfcRelVoidsElement::list::ptr ifcopenshell::geom::Kernel::find_openings(IfcSchema::IfcProduct* product) {
|
||||
// std::vector<IfcSchema::IfcRelVoidsElement*> rs;
|
||||
//
|
||||
//
|
||||
// if (product->declaration().is(IfcSchema::IfcElement::Class()) && !product->declaration().is(IfcSchema::IfcOpeningElement::Class())) {
|
||||
// IfcSchema::IfcElement* element = (IfcSchema::IfcElement*)product;
|
||||
// auto rels = element->HasOpenings();
|
||||
// rs.insert(rs.end(), rels->begin(), rels->end());
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Is the IfcElement a decomposition of an IfcElement with any IfcOpeningElements?
|
||||
// IfcSchema::IfcObjectDefinition* obdef = product->as<IfcSchema::IfcObjectDefinition>();
|
||||
// for (;;) {
|
||||
@@ -327,10 +327,10 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// auto rels = element->HasOpenings();
|
||||
// rs.insert(rs.end(), rels->begin(), rels->end());
|
||||
// }
|
||||
//
|
||||
//
|
||||
// obdef = rel_obdef;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Filter openings in Reference view, solely marked as Reference.
|
||||
// IfcSchema::IfcRelVoidsElement::list::ptr openings(new IfcSchema::IfcRelVoidsElement::list);
|
||||
// std::for_each(rs.begin(), rs.end(), [&openings](IfcSchema::IfcRelVoidsElement* rel) {
|
||||
@@ -341,17 +341,17 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
//
|
||||
// return openings;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// const IfcSchema::IfcMaterial* ifcopenshell::geom::Kernel::get_single_material_association(const IfcSchema::IfcProduct* product) {
|
||||
// IfcSchema::IfcMaterial* single_material = 0;
|
||||
// IfcSchema::IfcRelAssociatesMaterial::list::ptr associated_materials = product->HasAssociations()->as<IfcSchema::IfcRelAssociatesMaterial>();
|
||||
// if (associated_materials->size() == 1) {
|
||||
// IfcSchema::IfcMaterialSelect* associated_material = (*associated_materials->begin())->RelatingMaterial();
|
||||
// single_material = associated_material->as<IfcSchema::IfcMaterial>();
|
||||
//
|
||||
//
|
||||
// // NB: IfcMaterialLayerSets are also considered, regardless of --enable-layerset-slicing. Picking
|
||||
// // the first material (in accordance with other viewers) when layerset-slicing is disabled.
|
||||
// if (!single_material && associated_material->as<IfcSchema::IfcMaterialLayerSetUsage>()) {
|
||||
@@ -366,21 +366,21 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// return single_material;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// ifcopenshell::geom::native_element* ifcopenshell::geom::Kernel::create_brep_for_representation_and_product(
|
||||
// const IteratorSettings& settings, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product)
|
||||
// {
|
||||
// std::stringstream representation_id_builder;
|
||||
//
|
||||
//
|
||||
// representation_id_builder << representation->data().id();
|
||||
//
|
||||
//
|
||||
// ifcopenshell::geom::native* shape;
|
||||
// std::vector<ifcopenshell::geom::conversion_result> shapes, shapes2;
|
||||
//
|
||||
//
|
||||
// if (!convert_shapes(representation, shapes)) {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (settings.get(IteratorSettings::APPLY_LAYERSETS)) {
|
||||
// TopoDS_Shape merge;
|
||||
// if (util::flatten_shape_list(shapes, merge, false, getValue(GV_PRECISION))) {
|
||||
@@ -390,7 +390,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// std::vector< std::vector<Handle_Geom_Surface> > folded_layers;
|
||||
// std::vector<std::shared_ptr<const SurfaceStyle>> styles;
|
||||
// if (convert_layerset(product, layers, styles, thickness)) {
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcRelAssociates::list::ptr associations = product->HasAssociations();
|
||||
// for (IfcSchema::IfcRelAssociates::list::it it = associations->begin(); it != associations->end(); ++it) {
|
||||
// IfcSchema::IfcRelAssociatesMaterial* associates_material = (**it).as<IfcSchema::IfcRelAssociatesMaterial>();
|
||||
@@ -400,7 +400,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (styles.size() > 1) {
|
||||
// // If there's only a single layer there is no need to manipulate geometries.
|
||||
// bool success = true;
|
||||
@@ -415,7 +415,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// success = true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (!success) {
|
||||
// ifcopenshell::logger::root().error("Failed processing layerset");
|
||||
// }
|
||||
@@ -424,9 +424,9 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool material_style_applied = false;
|
||||
//
|
||||
//
|
||||
// const IfcSchema::IfcMaterial* single_material = get_single_material_association(product);
|
||||
// if (single_material) {
|
||||
// auto s = get_style(single_material);
|
||||
@@ -448,11 +448,11 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// ifcopenshell::logger::root().warning("No material and surface styles for:", product);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (material_style_applied) {
|
||||
// representation_id_builder << "-material-" << single_material->data().id();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (settings.force_space_transparency() >= 0. && product->declaration().is("IfcSpace")) {
|
||||
// for (auto& s : shapes) {
|
||||
// if (s.hasStyle()) {
|
||||
@@ -464,7 +464,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// int parent_id = -1;
|
||||
// try {
|
||||
// express::entity* parent_object = get_decomposing_entity(product);
|
||||
@@ -474,10 +474,10 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// } catch (const std::exception& e) {
|
||||
// ifcopenshell::logger::root().error(e);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// const std::string name = product->Name().value_or("");
|
||||
// const std::string guid = product->GlobalId();
|
||||
//
|
||||
//
|
||||
// gp_Trsf trsf;
|
||||
// try {
|
||||
// if (product->ObjectPlacement()) {
|
||||
@@ -488,20 +488,20 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// } catch (...) {
|
||||
// ifcopenshell::logger::root().error("Failed to construct placement");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Does the IfcElement have any IfcOpenings?
|
||||
// // Note that openings for IfcOpeningElements are not processed
|
||||
// IfcSchema::IfcRelVoidsElement::list::ptr openings = find_openings(product);
|
||||
//
|
||||
//
|
||||
// const std::string product_type = product->declaration().name();
|
||||
// ElementSettings element_settings(settings, getValue(GV_LENGTH_UNIT), product_type);
|
||||
//
|
||||
//
|
||||
// if (!settings.get(ifcopenshell::geom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && openings && openings->size()) {
|
||||
// representation_id_builder << "-openings";
|
||||
// for (IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++it) {
|
||||
// representation_id_builder << "-" << (*it)->data().id();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::vector<ifcopenshell::geom::conversion_result> opened_shapes;
|
||||
// bool caught_error = false;
|
||||
// try {
|
||||
@@ -512,11 +512,11 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// } catch (...) {
|
||||
// ifcopenshell::logger::root().message(ifcopenshell::logger::LOG_ERROR, "error processing openings for:", product);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (caught_error && opened_shapes.size() < shapes.size()) {
|
||||
// opened_shapes = shapes;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (settings.get(IteratorSettings::USE_WORLD_COORDS)) {
|
||||
// for (std::vector<ifcopenshell::geom::conversion_result>::iterator it = opened_shapes.begin(); it != opened_shapes.end(); ++it) {
|
||||
// it->prepend(trsf);
|
||||
@@ -535,14 +535,14 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// } else {
|
||||
// shape = new ifcopenshell::geom::native(element_settings, representation_id_builder.str(), shapes);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::string context_string = "";
|
||||
// if (representation->RepresentationIdentifier()) {
|
||||
// context_string = *representation->RepresentationIdentifier();
|
||||
// } else if (representation->ContextOfItems()->ContextType()) {
|
||||
// context_string = *representation->ContextOfItems()->ContextType();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// auto elem = new native_element(
|
||||
// product->data().id(),
|
||||
// parent_id,
|
||||
@@ -554,7 +554,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// std::shared_ptr<ifcopenshell::geom::native>(shape),
|
||||
// product
|
||||
// );
|
||||
//
|
||||
//
|
||||
// if (settings.get(IteratorSettings::VALIDATE_QUANTITIES)) {
|
||||
// auto rels = product->IsDefinedBy();
|
||||
// for (auto& rel : *rels) {
|
||||
@@ -623,10 +623,10 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// return elem;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcRepresentation* ifcopenshell::geom::Kernel::representation_mapped_to(const IfcSchema::IfcRepresentation* representation) {
|
||||
// IfcSchema::IfcRepresentation* representation_mapped_to = 0;
|
||||
// try {
|
||||
@@ -651,36 +651,36 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// return representation_mapped_to;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcProduct::list::ptr ifcopenshell::geom::Kernel::products_represented_by(const IfcSchema::IfcRepresentation* representation) {
|
||||
// IfcSchema::IfcProduct::list::ptr products(new IfcSchema::IfcProduct::list);
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcProductRepresentation::list::ptr prodreps = representation->OfProductRepresentation();
|
||||
//
|
||||
//
|
||||
// for (IfcSchema::IfcProductRepresentation::list::it it = prodreps->begin(); it != prodreps->end(); ++it) {
|
||||
// // http://buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcrepresentationresource/lexical/ifcproductrepresentation.htm
|
||||
// // IFC2x Edition 3 NOTE Users should not instantiate the entity IfcProductRepresentation from IFC2x Edition 3 onwards.
|
||||
// // It will be changed into an ABSTRACT supertype in future releases of IFC.
|
||||
//
|
||||
//
|
||||
// // IfcProductRepresentation also lacks the INVERSE relation to IfcProduct
|
||||
// // Let's find the IfcProducts that reference the IfcProductRepresentation anyway
|
||||
// products->push((*it)->data().get_inverse((&IfcSchema::IfcProduct::Class()), -1)->as<IfcSchema::IfcProduct>());
|
||||
// }
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcRepresentationMap::list::ptr maps = representation->RepresentationMap();
|
||||
//
|
||||
//
|
||||
// if (products->size() && maps->size()) {
|
||||
// ifcopenshell::logger::root().warning("Representation used by IfcRepresentationMap and IfcProductDefinitionShape", representation);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (prodreps->size() > 1) {
|
||||
// ifcopenshell::logger::root().warning("Multiple IfcProductDefinitionShapes for representation", representation);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (maps->size() > 1) {
|
||||
// ifcopenshell::logger::root().warning("Multiple IfcRepresentationMaps for representation", representation);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (maps->size() == 1) {
|
||||
// IfcSchema::IfcRepresentationMap* map = *maps->begin();
|
||||
// if (is_identity_transform(map->MappingOrigin())) {
|
||||
@@ -688,11 +688,11 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// for (IfcSchema::IfcMappedItem::list::it it = items->begin(); it != items->end(); ++it) {
|
||||
// IfcSchema::IfcMappedptr item = *it;
|
||||
// if (item->StyledByItem()->size() != 0) continue;
|
||||
//
|
||||
//
|
||||
// if (!is_identity_transform(item->MappingTarget())) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcRepresentation::list::ptr reps = item->data().get_inverse((&IfcSchema::IfcRepresentation::Class()), -1)->as<IfcSchema::IfcRepresentation>();
|
||||
// for (IfcSchema::IfcRepresentation::list::it jt = reps->begin(); jt != reps->end(); ++jt) {
|
||||
// IfcSchema::IfcRepresentation* rep = *jt;
|
||||
@@ -706,10 +706,10 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// return products;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// ifcopenshell::geom::native_element* ifcopenshell::geom::Kernel::create_brep_for_processed_representation(
|
||||
// const IteratorSettings& /*settings*/, IfcSchema::IfcRepresentation* representation, IfcSchema::IfcProduct* product,
|
||||
// ifcopenshell::geom::native_element* brep)
|
||||
@@ -723,10 +723,10 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// } catch (const std::exception& e) {
|
||||
// ifcopenshell::logger::root().error(e);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// const std::string name = product->Name().value_or("");
|
||||
// const std::string guid = product->GlobalId();
|
||||
//
|
||||
//
|
||||
// gp_Trsf trsf;
|
||||
// try {
|
||||
// if (product->ObjectPlacement()) {
|
||||
@@ -737,16 +737,16 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// } catch (...) {
|
||||
// ifcopenshell::logger::root().error("Failed to construct placement");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::string context_string = "";
|
||||
// if (representation->RepresentationIdentifier()) {
|
||||
// context_string = *representation->RepresentationIdentifier();
|
||||
// } else if (representation->ContextOfItems()->ContextType()) {
|
||||
// context_string = *representation->ContextOfItems()->ContextType();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// const std::string product_type = product->declaration().name();
|
||||
//
|
||||
//
|
||||
// return new native_element(
|
||||
// product->data().id(),
|
||||
// parent_id,
|
||||
@@ -759,24 +759,24 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// product
|
||||
// );
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool ifcopenshell::geom::Kernel::convert_layerset(const IfcSchema::IfcProduct* product, std::vector<Handle_Geom_Surface>& surfaces, std::vector<std::shared_ptr<const SurfaceStyle>>& styles, std::vector<double>& thicknesses) {
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool ifcopenshell::geom::Kernel::find_wall_end_points(const IfcSchema::IfcWall* wall, gp_Pnt& start, gp_Pnt& end) {
|
||||
// IfcSchema::IfcRepresentation* axis_representation = find_representation(wall, "Axis");
|
||||
// if (!axis_representation) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::vector<conversion_result> items;
|
||||
// {
|
||||
// Kernel temp = *this;
|
||||
// temp.setValue(GV_DIMENSIONALITY, -1.);
|
||||
// temp.convert_shapes(axis_representation, items);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// TopoDS_Vertex a, b;
|
||||
// for (std::vector<conversion_result>::const_iterator it = items.begin(); it != items.end(); ++it) {
|
||||
// TopExp_Explorer exp(it->shape(), TopAbs_VERTEX);
|
||||
@@ -787,36 +787,36 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (a.IsNull() || b.IsNull()) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// start = BRep_Tool::Pnt(a);
|
||||
// end = BRep_Tool::Pnt(b);
|
||||
//
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool ifcopenshell::geom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const std::vector<conversion_result>& items, const std::vector<Handle_Geom_Surface>& surfaces, const std::vector<double>& thicknesses, std::vector< std::vector<Handle_Geom_Surface> >& result) {
|
||||
// /*
|
||||
// * @todo isn't it easier to do this based on the non-folded surfaces of
|
||||
// * the connected walls and fold both pairs of layersets simultaneously?
|
||||
// */
|
||||
//
|
||||
//
|
||||
// bool folds_made = false;
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcRelConnectsPathElements::list::ptr connections(new IfcSchema::IfcRelConnectsPathElements::list);
|
||||
// connections->push(wall->ConnectedFrom()->as<IfcSchema::IfcRelConnectsPathElements>());
|
||||
// connections->push(wall->ConnectedTo()->as<IfcSchema::IfcRelConnectsPathElements>());
|
||||
//
|
||||
//
|
||||
// typedef std::vector<Handle_Geom_Surface> surfaces_t;
|
||||
// typedef std::pair<Handle_Geom_Surface, Handle_Geom_Curve> curve_on_surface;
|
||||
// typedef std::vector<curve_on_surface> curves_on_surfaces_t;
|
||||
// typedef std::vector< std::pair< std::pair<IfcSchema::IfcConnectionTypeEnum::Value, IfcSchema::IfcConnectionTypeEnum::Value>, const IfcSchema::IfcProduct*> > endpoint_connections_t;
|
||||
// typedef std::vector< std::vector<Handle_Geom_Surface> > result_t;
|
||||
// endpoint_connections_t endpoint_connections;
|
||||
//
|
||||
//
|
||||
// // Find the semantic connections to other wall elements when they are not connected 'AT_PATH' because
|
||||
// // in that latter case no folds need to be made.
|
||||
// for (IfcSchema::IfcRelConnectsPathElements::list::it it = connections->begin(); it != connections->end(); ++it) {
|
||||
@@ -838,18 +838,18 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (endpoint_connections.size() == 0) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Count how many connections are made AT_START and AT_END respectively
|
||||
// int connection_type_count[2] = { 0,0 };
|
||||
// for (endpoint_connections_t::const_iterator it = endpoint_connections.begin(); it != endpoint_connections.end(); ++it) {
|
||||
// const int idx = it->first.first == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART;
|
||||
// connection_type_count[idx] ++;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// gp_Trsf local;
|
||||
// if (wall->ObjectPlacement()) {
|
||||
// if (!convert(wall->ObjectPlacement(), local)) {
|
||||
@@ -857,7 +857,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// local.Invert();
|
||||
//
|
||||
//
|
||||
// {
|
||||
// // Copy the unfolded surfaces
|
||||
// result.resize(surfaces.size());
|
||||
@@ -867,25 +867,25 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// result_it->push_back(*input_it);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// const double total_thickness = std::accumulate(thicknesses.begin(), thicknesses.end(), 0.);
|
||||
//
|
||||
//
|
||||
// gp_Pnt own_axis_start, own_axis_end;
|
||||
// find_wall_end_points(wall, own_axis_start, own_axis_end);
|
||||
//
|
||||
//
|
||||
// // Sometimes duplicate IfcRelConnectsPathElements exist. These are detected
|
||||
// // and the counts of connections are decremented accordingly.
|
||||
// for (int idx = 0; idx < 2; ++idx) {
|
||||
// if (connection_type_count[idx] <= 1) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /*
|
||||
// IfcSchema::IfcConnectionTypeEnum::Value connection_type = idx == 1
|
||||
// ? IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART
|
||||
// : IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATEND;
|
||||
// */
|
||||
//
|
||||
//
|
||||
// std::set<const IfcSchema::IfcProduct*> others;
|
||||
// endpoint_connections_t::iterator it = endpoint_connections.begin();
|
||||
// while (it != endpoint_connections.end()) {
|
||||
@@ -899,38 +899,38 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Check whether the end points are of the wall are really ~1 LayerThickness away from each other
|
||||
// /*
|
||||
// for (endpoint_connections_t::const_iterator it = endpoint_connections.begin(); it != endpoint_connections.end(); ++it) {
|
||||
// IfcSchema::IfcConnectionTypeEnum::Value own_type = it->first.first;
|
||||
// IfcSchema::IfcConnectionTypeEnum::Value other_type = it->first.second;
|
||||
//
|
||||
//
|
||||
// gp_Pnt other_axis_start, other_axis_end;
|
||||
// find_wall_end_points(it->second->as<IfcSchema::IfcWall>(), other_axis_start, other_axis_end);
|
||||
//
|
||||
//
|
||||
// gp_Trsf other;
|
||||
// if (!convert(it->second->ObjectPlacement(), other)) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// other.Transforms(other_axis_start.ChangeCoord());
|
||||
// local.Transforms(other_axis_start.ChangeCoord());
|
||||
// other.Transforms(other_axis_end.ChangeCoord());
|
||||
// local.Transforms(other_axis_end.ChangeCoord());
|
||||
//
|
||||
//
|
||||
// const gp_Pnt& a = own_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART
|
||||
// ? own_axis_start
|
||||
// : own_axis_end;
|
||||
//
|
||||
//
|
||||
// const gp_Pnt& b = other_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART
|
||||
// ? other_axis_start
|
||||
// : other_axis_end;
|
||||
//
|
||||
//
|
||||
// const double d = a.Distance(b);
|
||||
// }
|
||||
// */
|
||||
//
|
||||
//
|
||||
// const double length_required = endpoint_connections.size() * total_thickness;
|
||||
// // @todo this is not precisely the distance in case of curved walls. Also, it's safer
|
||||
// // to first reproject the body onto the axis to get the precise curve parametrization
|
||||
@@ -940,20 +940,20 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// ifcopenshell::logger::root().warning("The wall axis is not long enough to accommodate the fold points");
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// for (endpoint_connections_t::const_iterator it = endpoint_connections.begin(); it != endpoint_connections.end(); ++it) {
|
||||
// IfcSchema::IfcConnectionTypeEnum::Value connection_type = it->first.first;
|
||||
//
|
||||
//
|
||||
// // If more than one wall connects to this start/end -point assume layers do not need to be folded
|
||||
// const int idx = connection_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATSTART;
|
||||
// if (connection_type_count[idx] > 1) continue;
|
||||
//
|
||||
//
|
||||
// // Pick the corresponding point from the axis
|
||||
// const gp_Pnt& own_end_point = connection_type == IfcSchema::IfcConnectionTypeEnum::IfcConnectionType_ATEND
|
||||
// ? own_axis_end
|
||||
// : own_axis_start;
|
||||
// const IfcSchema::IfcProduct* other_wall = it->second;
|
||||
//
|
||||
//
|
||||
// gp_Trsf other;
|
||||
// if (other_wall->ObjectPlacement()) {
|
||||
// if (!convert(other_wall->ObjectPlacement(), other)) {
|
||||
@@ -961,32 +961,32 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcRepresentation* axis_representation = find_representation(other_wall, "Axis");
|
||||
//
|
||||
//
|
||||
// if (!axis_representation) {
|
||||
// ifcopenshell::logger::root().warning("Joined wall has no axis representation", other_wall);
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::vector<conversion_result> axis_items;
|
||||
// {
|
||||
// Kernel temp = *this;
|
||||
// temp.setValue(GV_DIMENSIONALITY, -1.);
|
||||
// temp.convert_shapes(axis_representation, axis_items);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// TopoDS_Shape axis_shape;
|
||||
// util::flatten_shape_list(axis_items, axis_shape, false, getValue(GV_PRECISION));
|
||||
//
|
||||
//
|
||||
// // local and other are IfcLocalPlacements and therefore have a unit
|
||||
// // scale factor that can be applied by means of TopoDS_Shape::Move()
|
||||
// axis_shape.Move(other);
|
||||
// axis_shape.Move(local);
|
||||
//
|
||||
//
|
||||
// TopoDS_Shape body_shape;
|
||||
// util::flatten_shape_list(items, body_shape, false, getValue(GV_PRECISION));
|
||||
//
|
||||
//
|
||||
// // Create a single paremetric range over a single curve
|
||||
// // that represents the entire 1d domain of the other wall
|
||||
// // Sometimes there are multiple edges in the Axis shape
|
||||
@@ -998,19 +998,19 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// if (!exp.More()) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// TopoDS_Edge axis_edge = TopoDS::Edge(exp.Current());
|
||||
// other_axis_curve = BRep_Tool::Curve(axis_edge, axis_u1, axis_u2);
|
||||
//
|
||||
//
|
||||
// gp_Pnt other_a_1, other_a_2;
|
||||
// other_axis_curve->D0(axis_u1, other_a_1);
|
||||
// other_axis_curve->D0(axis_u2, other_a_2);
|
||||
//
|
||||
//
|
||||
// if (axis_u2 < axis_u1) {
|
||||
// std::swap(axis_u1, axis_u2);
|
||||
// }
|
||||
// exp.Next();
|
||||
//
|
||||
//
|
||||
// for (; exp.More(); exp.Next()) {
|
||||
// TopoDS_Edge axis_edge2 = TopoDS::Edge(exp.Current());
|
||||
// TopExp_Explorer exp2(axis_edge2, TopAbs_VERTEX);
|
||||
@@ -1025,22 +1025,22 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// double layer_offset = 0;
|
||||
//
|
||||
//
|
||||
// std::vector<double>::const_iterator thickness = thicknesses.begin();
|
||||
// result_t::iterator result_vector = result.begin() + 1;
|
||||
//
|
||||
//
|
||||
// // nb The first layer is never folded, because it corresponds
|
||||
// // to one of the longitudinal faces of the wall. Hence the +1
|
||||
// for (surfaces_t::const_iterator jt = surfaces.begin() + 1; jt != surfaces.end() - 1; ++jt, ++result_vector) {
|
||||
// layer_offset += *thickness++;
|
||||
//
|
||||
//
|
||||
// bool found_intersection = false, parallel = false;
|
||||
// std::optional<gp_Pnt> point_outside_param_range;
|
||||
//
|
||||
//
|
||||
// const Handle_Geom_Surface& surface = *jt;
|
||||
//
|
||||
//
|
||||
// // Find the intersection point between the layerset surface
|
||||
// // and the other axis curve. If it's within the parametric
|
||||
// // range of the other wall it means the walls are connected
|
||||
@@ -1048,16 +1048,16 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// GeomAPI_IntCS intersections(other_axis_curve, surface);
|
||||
// if (intersections.IsDone() && intersections.NbPoints() == 1) {
|
||||
// const gp_Pnt& p = intersections.Point(1);
|
||||
//
|
||||
//
|
||||
// double u, v, w;
|
||||
// intersections.Parameters(1, u, v, w);
|
||||
//
|
||||
//
|
||||
// gp_Pnt Pc, Ps;
|
||||
// gp_Vec Vc, Vs1, Vs2;
|
||||
// other_axis_curve->D1(w, Pc, Vc);
|
||||
// surface->D1(u, v, Ps, Vs1, Vs2);
|
||||
// Vs1.Cross(Vs2);
|
||||
//
|
||||
//
|
||||
// if (Vs1.IsNormal(Vc, 1.e-5)) {
|
||||
// ifcopenshell::logger::root().warning("Connected walls are parallel");
|
||||
// parallel = true;
|
||||
@@ -1069,9 +1069,9 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (!parallel && !found_intersection && point_outside_param_range) {
|
||||
//
|
||||
//
|
||||
// /*
|
||||
// Is there a bug in Open Cascade related to the intersection
|
||||
// of offset surfaces constructed from linear extrusions?
|
||||
@@ -1083,13 +1083,13 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// Handle_Geom_Surface yz2 = new Geom_OffsetSurface(yz, 1.);
|
||||
// intersect(xy, yz2);
|
||||
// */
|
||||
//
|
||||
//
|
||||
// Handle_Geom_Surface plane = new Geom_Plane(*point_outside_param_range, gp::DZ());
|
||||
//
|
||||
//
|
||||
// // vertical edges at wall end point face.
|
||||
// curves_on_surfaces_t layer_ends;
|
||||
// util::intersect(surface, body_shape, layer_ends);
|
||||
//
|
||||
//
|
||||
// Handle_Geom_Curve layer_body_intersection;
|
||||
// Handle_Geom_Surface body_surface;
|
||||
// double mind = std::numeric_limits<double>::infinity();
|
||||
@@ -1111,9 +1111,9 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// if (d < total_thickness * 3 && d < mind) {
|
||||
// GeomAdaptor_Curve GAC(other_axis_curve);
|
||||
// GeomAdaptor_Surface GAS(kt->first);
|
||||
//
|
||||
//
|
||||
// Extrema_ExtCS x(GAC, GAS, getValue(GV_PRECISION), getValue(GV_PRECISION));
|
||||
//
|
||||
//
|
||||
// if (x.IsParallel()) {
|
||||
// body_surface = kt->first;
|
||||
// layer_body_intersection = kt->second;
|
||||
@@ -1122,16 +1122,16 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (body_surface.IsNull()) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Intersect vertical edge with ground plane for point.
|
||||
// GeomAPI_IntCS intersection2(layer_body_intersection, plane);
|
||||
// if (intersection2.IsDone() && intersection2.NbPoints() == 1) {
|
||||
// const gp_Pnt& layer_end_point = intersection2.Point(1);
|
||||
//
|
||||
//
|
||||
// // Intersect layerset surface with ground plane
|
||||
// GeomAPI_IntSS intersection3(surface, plane, 1.e-7);
|
||||
// if (intersection3.IsDone() && intersection3.NbLines() == 1) {
|
||||
@@ -1140,14 +1140,14 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// ShapeAnalysis_Curve sac;
|
||||
// gp_Pnt layer_end_point_projected; double layer_end_point_param;
|
||||
// sac.Project(layer_line, layer_end_point, 1e-3, layer_end_point_projected, layer_end_point_param, false);
|
||||
//
|
||||
//
|
||||
// // Move point inwards by distance from other layerset
|
||||
// GCPnts_AbscissaPoint dst(layer_line_adaptor, layer_offset, layer_end_point_param);
|
||||
// if (dst.IsDone()) {
|
||||
// // Convert parameter to point
|
||||
// gp_Pnt layer_fold_point;
|
||||
// layer_line->D0(dst.Parameter(), layer_fold_point);
|
||||
//
|
||||
//
|
||||
// GeomAPI_IntSS intersection4(body_surface, plane, 1.e-7);
|
||||
// if (intersection4.IsDone() && intersection4.NbLines() == 1) {
|
||||
// Handle_Geom_Curve body_trim_curve = intersection4.Line(1);
|
||||
@@ -1155,7 +1155,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// gp_Pnt layer_fold_point_projected; double layer_fold_point_param;
|
||||
// sac2.Project(body_trim_curve, layer_fold_point, 1.e-7, layer_fold_point_projected, layer_fold_point_param, false);
|
||||
// Handle_Geom_Curve fold_curve = new Geom_OffsetCurve(body_trim_curve->Reversed(), layer_fold_point_projected.Distance(layer_fold_point), gp::DZ());
|
||||
//
|
||||
//
|
||||
// Handle_Geom_Surface fold_surface = new Geom_SurfaceOfLinearExtrusion(fold_curve, gp::DZ());
|
||||
// result_vector->push_back(fold_surface);
|
||||
// folds_made = true;
|
||||
@@ -1163,15 +1163,15 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// return folds_made;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcRepresentation* ifcopenshell::geom::Kernel::find_representation(const IfcSchema::IfcProduct* product, const std::string& identifier) {
|
||||
// if (!product->Representation()) return 0;
|
||||
// IfcSchema::IfcProductRepresentation* prod_rep = product->Representation();
|
||||
@@ -1183,12 +1183,12 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// const IfcSchema::IfcRepresentationptr ifcopenshell::geom::Kernel::find_item_carrying_style(const IfcSchema::IfcRepresentationptr item) {
|
||||
// if (item->StyledByItem()->size()) {
|
||||
// return item;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// while (item->declaration().is(IfcSchema::IfcBooleanResult::Class())) {
|
||||
// // All instantiations of IfcBooleanOperand (type of FirstOperand) are subtypes of
|
||||
// // IfcGeometricRepresentationItem
|
||||
@@ -1197,24 +1197,24 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// return item;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // TODO: Ideally this would be done for other entities (such as IfcCsgSolid) as well.
|
||||
// // But neither are these very prevalent, nor does the current IfcOpenShell style
|
||||
// // mechanism enable to conveniently style subshapes, which would be necessary for
|
||||
// // distinctly styled union operands.
|
||||
//
|
||||
//
|
||||
// return item;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool ifcopenshell::geom::Kernel::is_identity_transform(ifcopenshell::IfcBaseInterface* l) {
|
||||
// IfcSchema::IfcAxis2Placement2D* ax2d;
|
||||
// IfcSchema::IfcAxis2Placement3D* ax3d;
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcCartesianTransformationOperator2D* op2d;
|
||||
// IfcSchema::IfcCartesianTransformationOperator3D* op3d;
|
||||
// IfcSchema::IfcCartesianTransformationOperator2DnonUniform* op2dnonu;
|
||||
// IfcSchema::IfcCartesianTransformationOperator3DnonUniform* op3dnonu;
|
||||
//
|
||||
//
|
||||
// if ((op2dnonu = l->as<IfcSchema::IfcCartesianTransformationOperator2DnonUniform>()) != 0) {
|
||||
// gp_GTrsf2d gtrsf2d;
|
||||
// convert(op2dnonu, gtrsf2d);
|
||||
@@ -1243,18 +1243,18 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// throw ifcopenshell::exception("Invalid valuation for IfcAxis2Placement / IfcCartesianTransformationOperator");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// void ifcopenshell::geom::Kernel::set_conversion_placement_rel_to_type(const ifcopenshell::declaration* type) {
|
||||
// placement_rel_to_type_ = type;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// void ifcopenshell::geom::Kernel::set_conversion_placement_rel_to_instance(const express::entity* instance) {
|
||||
// placement_rel_to_instance_ = instance;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// namespace {
|
||||
//
|
||||
//
|
||||
// bool process_colour(IfcSchema::IfcColourRgb* colour, double* rgb) {
|
||||
// if (colour != 0) {
|
||||
// rgb[0] = colour->Red();
|
||||
@@ -1263,7 +1263,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// return colour != 0;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool process_colour(IfcSchema::IfcNormalisedRatioMeasure* factor, double* rgb) {
|
||||
// if (factor != 0) {
|
||||
// const double f = *factor;
|
||||
@@ -1271,7 +1271,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// return factor != 0;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool process_colour(IfcSchema::IfcColourOrFactor* colour_or_factor, double* rgb) {
|
||||
// if (colour_or_factor == 0) {
|
||||
// return false;
|
||||
@@ -1283,11 +1283,11 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// #define Kernel POSTFIX_SCHEMA(Kernel)
|
||||
//
|
||||
//
|
||||
// std::shared_ptr<const ifcopenshell::geom::SurfaceStyle> ifcopenshell::geom::Kernel::internalize_surface_style(const std::pair<express::base, express::base>& shading_styles) {
|
||||
// if (shading_styles.second == 0) {
|
||||
// return 0;
|
||||
@@ -1297,22 +1297,22 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// if (it != style_cache.end()) {
|
||||
// return it->second;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// IfcSchema::IfcSurfaceStyle* style = shading_styles.first->as<IfcSchema::IfcSurfaceStyle>();
|
||||
// IfcSchema::IfcSurfaceStyleShading* shading = shading_styles.second->as<IfcSchema::IfcSurfaceStyleShading>();
|
||||
//
|
||||
//
|
||||
// std::shared_ptr<SurfaceStyle> surface_style_ptr;
|
||||
//
|
||||
//
|
||||
// if (style->Name()) {
|
||||
// surface_style_ptr.reset(new SurfaceStyle(surface_style_id, *style->Name()));
|
||||
// } else {
|
||||
// surface_style_ptr.reset(new SurfaceStyle(surface_style_id));
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::shared_ptr<const SurfaceStyle> surface_style_ptr_const = std::const_pointer_cast<const SurfaceStyle>(surface_style_ptr);
|
||||
// SurfaceStyle& surface_style = *surface_style_ptr;
|
||||
//
|
||||
//
|
||||
// double rgb[3];
|
||||
// if (process_colour(shading->SurfaceColour(), rgb)) {
|
||||
// surface_style.Diffuse().reset(SurfaceStyle::ColorComponent(rgb[0], rgb[1], rgb[2]));
|
||||
@@ -1353,11 +1353,11 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// }
|
||||
// return style_cache[surface_style_id] = surface_style_ptr_const;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::shared_ptr<const ifcopenshell::geom::SurfaceStyle> ifcopenshell::geom::Kernel::get_style(const IfcSchema::IfcRepresentationptr item) {
|
||||
// return internalize_surface_style(get_surface_style<IfcSchema::IfcSurfaceStyleShading>(item));
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::shared_ptr<const ifcopenshell::geom::SurfaceStyle> ifcopenshell::geom::Kernel::get_style(const IfcSchema::IfcMaterial* material) {
|
||||
// IfcSchema::IfcMaterialDefinitionRepresentation::list::ptr defs = material->HasRepresentation();
|
||||
// for (IfcSchema::IfcMaterialDefinitionRepresentation::list::it jt = defs->begin(); jt != defs->end(); ++jt) {
|
||||
@@ -1376,14 +1376,14 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// auto material_style = std::make_shared<ifcopenshell::geom::SurfaceStyle>(material->data().id(), material->Name());
|
||||
// return style_cache[material->data().id()] = material_style;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// void ifcopenshell::geom::Kernel::apply_layerset(std::vector<ifcopenshell::geom::conversion_result>& r, const ifcopenshell::geom::layerset_information& info) {
|
||||
// convert(info.layers);
|
||||
//
|
||||
//
|
||||
// if (info.layers.empty()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if (axis_curve->DynamicType() == STANDARD_TYPE(Geom_Line)) {
|
||||
// Handle_Geom_Line axis_line = Handle_Geom_Line::DownCast(axis_curve);
|
||||
// // @todo note that this creates an offset into the wrong order, the cross product arguments should be
|
||||
@@ -1397,7 +1397,7 @@ bool ifcopenshell::geom::open_cascade_kernel::convert_impl(const taxonomy::revol
|
||||
// ifcopenshell::logger::root().message(ifcopenshell::logger::LOG_ERROR, "Unsupported underlying curve of Axis representation:", product);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// std::vector<ifcopenshell::geom::conversion_result> r2;
|
||||
// if (ifcopenshell::geom::util::apply_layerset(r, const std::vector<ifcopenshell::geom::taxonomy::style>&, std::vector<conversion_result>& r2, double tol)) {
|
||||
// std::swap(r, r2)
|
||||
|
||||
@@ -90,7 +90,7 @@ private:
|
||||
std::map<std::pair<int, int>, TopoDS_Edge> edges_;
|
||||
double eps_;
|
||||
bool non_manifold_;
|
||||
|
||||
|
||||
void loop_(const ifcopenshell::geom::taxonomy::loop::ptr ps, const std::function<void(int, int, bool)>& callback);
|
||||
public:
|
||||
faceset_helper(open_cascade_kernel* kernel, const ifcopenshell::geom::taxonomy::shell::ptr l);
|
||||
@@ -99,7 +99,7 @@ private:
|
||||
bool non_manifold() const { return non_manifold_; }
|
||||
bool& non_manifold() { return non_manifold_; }
|
||||
double epsilon() const { return eps_; }
|
||||
|
||||
|
||||
bool edge(int A, int B, TopoDS_Edge& e);
|
||||
|
||||
bool wire(const ifcopenshell::geom::taxonomy::loop::ptr loop, TopoDS_Wire& wire);
|
||||
|
||||
@@ -64,8 +64,8 @@ bool open_cascade_kernel::convert(const taxonomy::solid::ptr solid, TopoDS_Shape
|
||||
|
||||
result = halfspace;
|
||||
return true;
|
||||
} else if (solid->children.size() == 1
|
||||
&& solid->children[0]->children.size() == 1
|
||||
} else if (solid->children.size() == 1
|
||||
&& solid->children[0]->children.size() == 1
|
||||
&& solid->children[0]->children[0]->basis
|
||||
&& solid->children[0]->children[0]->basis->kind() == taxonomy::SPHERE)
|
||||
{
|
||||
|
||||
@@ -126,7 +126,7 @@ bool open_cascade_kernel::convert(const taxonomy::sweep_along_curve::ptr scs, To
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Build the wire from curve, which is the directrix offset toward the origin
|
||||
// when applied_temporary_offset is set. Using scs->curve here left the wire
|
||||
// far from the origin yet still translated the result back by +mean, which
|
||||
@@ -155,11 +155,11 @@ bool open_cascade_kernel::convert(const taxonomy::sweep_along_curve::ptr scs, To
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Handle(Geom_Surface) surface;
|
||||
if (scs->surface) {
|
||||
surface = convert_surface(scs->surface);
|
||||
}
|
||||
}
|
||||
|
||||
gp_Trsf directrix;
|
||||
TopoDS_Wire wire = std::get<TopoDS_Wire>(w);
|
||||
|
||||
@@ -201,12 +201,12 @@ namespace ifcopenshell::geom {
|
||||
box.corners[1][1] = static_cast<float>(max_point[1] + 1e-5);
|
||||
box.corners[1][2] = static_cast<float>(max_point[2] + 1e-5);
|
||||
/*
|
||||
std::cout << "Ray "
|
||||
std::cout << "Ray "
|
||||
<< v_ray.origin[0] << " "
|
||||
<< v_ray.origin[1] << " "
|
||||
<< v_ray.origin[2] << " "
|
||||
<< std::endl;
|
||||
std::cout << "Box "
|
||||
std::cout << "Box "
|
||||
<< min_point[0] << " "
|
||||
<< min_point[1] << " "
|
||||
<< min_point[2] << " "
|
||||
@@ -864,7 +864,7 @@ namespace ifcopenshell::geom {
|
||||
if (dss.Value() <= extend) {
|
||||
distances_.push_back(dss.Value());
|
||||
protrusion_distances_.push_back(max_distance_inside(B, A));
|
||||
}
|
||||
}
|
||||
return dss.Value() <= extend;
|
||||
}
|
||||
} else {
|
||||
@@ -925,8 +925,8 @@ namespace ifcopenshell::geom {
|
||||
// Gap is assumed to be positive throughout the codebase,
|
||||
// but at least for IsOut() in the selector a negative
|
||||
// Gap should work as well.
|
||||
b.SetGap(b.GetGap() + extend);
|
||||
|
||||
b.SetGap(b.GetGap() + extend);
|
||||
|
||||
return select_box(b, completely_within);
|
||||
}
|
||||
|
||||
@@ -956,7 +956,7 @@ namespace ifcopenshell::geom {
|
||||
double gap = B.GetGap();
|
||||
gp_Pnt p1(x1 - gap, y1 - gap, z1 - gap);
|
||||
gp_Pnt p2(x2 + gap, y2 + gap, z2 + gap);
|
||||
|
||||
|
||||
if (!b.IsOut(p1) && !b.IsOut(p2)) {
|
||||
ts_filtered.push_back(*it);
|
||||
}
|
||||
@@ -1389,7 +1389,7 @@ namespace ifcopenshell::geom {
|
||||
if (extend > 0.0) {
|
||||
BRepExtrema_DistShapeShape dss(v, B);
|
||||
if (dss.Perform() && dss.NbSolution() >= 1 && dss.Value() <= extend) {
|
||||
distances_.push_back(dss.Value());
|
||||
distances_.push_back(dss.Value());
|
||||
protrusion_distances_.push_back(max_distance_inside(B, v));
|
||||
|
||||
ts_filtered.push_back(*it);
|
||||
@@ -1416,9 +1416,9 @@ namespace ifcopenshell::geom {
|
||||
spatial_tree tree_;
|
||||
shape_map shapes_;
|
||||
std::map<T, Bnd_Box> aabbs_;
|
||||
std::map<T, Bnd_OBB> obbs_;
|
||||
std::map<T, double> max_protrusions_;
|
||||
std::map<T, opencascade::handle<BVH_Tree<double, 3, BVH_BinaryTree>>> bvhs_;
|
||||
std::map<T, Bnd_OBB> obbs_;
|
||||
std::map<T, double> max_protrusions_;
|
||||
std::map<T, opencascade::handle<BVH_Tree<double, 3, BVH_BinaryTree>>> bvhs_;
|
||||
std::unordered_map<T, bool> is_manifold_;
|
||||
std::unordered_map<T, std::vector<std::array<int, 3>>> tris_;
|
||||
std::unordered_map<T, std::vector<gp_Pnt>> verts_;
|
||||
@@ -1433,7 +1433,7 @@ namespace ifcopenshell::geom {
|
||||
std::map<std::string, std::vector<int>> local_faces_;
|
||||
std::map<std::string, std::vector<ifcopenshell::geom::taxonomy::style::ptr>> local_materials_;
|
||||
std::map<std::string, std::vector<int>> local_material_ids_;
|
||||
|
||||
|
||||
bool enable_face_styles_ = false;
|
||||
|
||||
class selector : public spatial_tree::Selector
|
||||
@@ -1480,7 +1480,7 @@ namespace ifcopenshell::geom {
|
||||
|
||||
opencascade_tree(ifcopenshell::geom::iterator& it) {
|
||||
add_file(it);
|
||||
}
|
||||
}
|
||||
|
||||
void add_file(ifcopenshell::file& f, ifcopenshell::geom::settings settings) {
|
||||
ifcopenshell::geom::settings settings_ = settings;
|
||||
@@ -1509,7 +1509,7 @@ namespace ifcopenshell::geom {
|
||||
result.reserve(flat_list.size());
|
||||
|
||||
for (size_t i = 0; i < flat_list.size(); i += 3) {
|
||||
vin <<
|
||||
vin <<
|
||||
flat_list[i],
|
||||
flat_list[i + 1],
|
||||
flat_list[i + 2];
|
||||
@@ -1576,7 +1576,7 @@ namespace ifcopenshell::geom {
|
||||
vs_transformed.push_back(p.Transformed(tr));
|
||||
aabb.Add(vs_transformed.back());
|
||||
}
|
||||
|
||||
|
||||
std::unordered_map<std::tuple<int, int, int>, std::vector<size_t>, boost::hash<std::tuple<int, int, int>>> quantized_normal_counts;
|
||||
|
||||
std::vector<double> tri_areas;
|
||||
@@ -1631,7 +1631,7 @@ namespace ifcopenshell::geom {
|
||||
|
||||
gp_Ax3 ax3;
|
||||
gp_Trsf trsf2;
|
||||
|
||||
|
||||
for (size_t attempt = 0; attempt < 2; ++attempt) {
|
||||
|
||||
if (candidates.empty() || attempt == 1) {
|
||||
@@ -1673,7 +1673,7 @@ namespace ifcopenshell::geom {
|
||||
obb.SetZComponent(ax3.Direction(), halfsize.Z());
|
||||
obb.SetCenter(cent.Transformed(trsf2.Inverted()));
|
||||
}
|
||||
|
||||
|
||||
const auto& t = elem->product();
|
||||
const auto& matrix = elem->transformation().data();
|
||||
const std::vector<double>& elem_verts_local = elem->geometry().verts();
|
||||
@@ -1744,7 +1744,7 @@ namespace ifcopenshell::geom {
|
||||
obbs_[t] = obb;
|
||||
max_protrusions_[t] = std::min(std::min(obb.XHSize(), obb.YHSize()), obb.ZHSize()) * 2;
|
||||
}
|
||||
|
||||
|
||||
void add_element(ifcopenshell::geom::native_element* elem) {
|
||||
if (!elem) {
|
||||
return;
|
||||
@@ -1753,7 +1753,7 @@ namespace ifcopenshell::geom {
|
||||
auto compound_generic = (ifcopenshell::geom::open_cascade_shape*)elem->geometry().as_compound();
|
||||
TopoDS_Shape compound(std::move(compound_generic->shape()));
|
||||
delete compound_generic;
|
||||
|
||||
|
||||
const auto& m = elem->transformation().data()->ccomponents();
|
||||
gp_Trsf tr;
|
||||
tr.SetValues(
|
||||
|
||||
@@ -381,7 +381,7 @@ namespace {
|
||||
bool ifcopenshell::geom::util::wire_intersections(const TopoDS_Wire& wire, NCollection_List<TopoDS_Shape>& wires, const wire_tolerance_settings& settings) {
|
||||
double eps = get_wire_intersection_tolerance(settings, wire);
|
||||
double eps_real = settings.precision;
|
||||
|
||||
|
||||
if (!wire.Closed()) {
|
||||
wires.Append(wire);
|
||||
return false;
|
||||
@@ -576,7 +576,7 @@ void ifcopenshell::geom::util::select_largest(const NCollection_List<TopoDS_Shap
|
||||
BRepBndLib::AddClose(it.Value(), bb);
|
||||
double xyz_min[3], xyz_max[3];
|
||||
bb.Get(xyz_min[0], xyz_min[1], xyz_min[2], xyz_max[0], xyz_max[1], xyz_max[2]);
|
||||
|
||||
|
||||
// @todo hard coded precision.
|
||||
// @todo this is a really strange measure for wire size. Why not use newell's
|
||||
// method to project to plane and then calculate size of the 2d bbox?
|
||||
|
||||
@@ -45,7 +45,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcArbitraryClosedProfileDef& i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return face;
|
||||
} else {
|
||||
return nullptr;
|
||||
|
||||
@@ -35,7 +35,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcAxis1Placement& inst) {
|
||||
taxonomy::direction3::ptr v = taxonomy::cast<taxonomy::direction3>(map(inst.Axis()));
|
||||
axis = *v->components_;
|
||||
}
|
||||
|
||||
|
||||
// @todo not sure what to do with ref, we're probably never reading it,
|
||||
// because we just created an Axis1 again from it in the kernel, but
|
||||
// to this constructor we need to supply something valid.
|
||||
|
||||
@@ -27,12 +27,12 @@ using namespace ifcopenshell::geom;
|
||||
#ifdef SCHEMA_HAS_IfcBSplineCurveWithKnots
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcBSplineCurveWithKnots& inst) {
|
||||
auto bc = taxonomy::make<taxonomy::bspline_curve>();
|
||||
|
||||
|
||||
const std::vector<IfcSchema::IfcCartesianPoint> cps = inst.ControlPointsList();
|
||||
std::vector<taxonomy::point3::ptr> points;
|
||||
std::transform(cps.begin(), cps.end(), std::back_inserter(points), [this](const IfcSchema::IfcCartesianPoint& cp) { return taxonomy::cast<taxonomy::point3>(map(cp)); });
|
||||
bc->control_points = points;
|
||||
|
||||
|
||||
auto knot_multiplicities = inst.KnotMultiplicities();
|
||||
bc->multiplicities.assign(knot_multiplicities.begin(), knot_multiplicities.end());
|
||||
bc->knots = inst.Knots();
|
||||
|
||||
@@ -73,7 +73,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCenterLineProfileDef&) {
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(c1a, c2a));
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(c2));
|
||||
mw.Add(BRepBuilderAPI_MakeEdge(c2b, c1b));
|
||||
|
||||
|
||||
face = BRepBuilderAPI_MakeFace(mw.Wire());
|
||||
} else {
|
||||
BRepOffsetAPI_MakeOffset offset(BRepBuilderAPI_MakeFace(gp_Pln(gp::Origin(), gp::DZ())));
|
||||
|
||||
@@ -23,7 +23,7 @@ using namespace ifcopenshell::geom;
|
||||
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCircle& inst) {
|
||||
const double r = inst.Radius() * length_unit_;
|
||||
if (r < settings_.get<settings::Precision>().get()) {
|
||||
if (r < settings_.get<settings::Precision>().get()) {
|
||||
logger_.message(ifcopenshell::logger::LOG_ERROR, "GEO", 237, "Radius not greater than zero for:", inst);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCompositeCurve& inst) {
|
||||
#else
|
||||
std::vector<IfcSchema::IfcCompositeCurveSegment> segments = inst.Segments();
|
||||
#endif
|
||||
|
||||
|
||||
for (auto& segment : segments) {
|
||||
if (segment.as<IfcSchema::IfcCompositeCurveSegment>() && segment.as<IfcSchema::IfcCompositeCurveSegment>().ParentCurve().as<IfcSchema::IfcLine>()) {
|
||||
logger_.notice("GEO", 238, "Infinite IfcLine used as ParentCurve of segment, treating as a segment", segment);
|
||||
|
||||
@@ -25,7 +25,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveBoundedPlane& inst) {
|
||||
taxonomy::plane::ptr pl = taxonomy::cast<taxonomy::plane>(map(inst.BasisSurface()));
|
||||
auto f = taxonomy::make<taxonomy::face>();
|
||||
f->children.push_back(taxonomy::cast<taxonomy::loop>(map(inst.OuterBoundary())));
|
||||
|
||||
|
||||
std::vector<IfcSchema::IfcCurve> boundaries = inst.InnerBoundaries();
|
||||
|
||||
for (auto& b : boundaries) {
|
||||
|
||||
@@ -133,7 +133,7 @@ struct spiral_parent_curve : public parent_curve_function {
|
||||
|
||||
// this is the piecewise curve segment function for horizontal and vertical
|
||||
struct curve_segment_function {
|
||||
curve_segment_function(const Eigen::Matrix4d& curve_segment_placement, const Eigen::Matrix4d& parent_curve_normalization, std::shared_ptr<parent_curve_function> parent_curve_fn) :
|
||||
curve_segment_function(const Eigen::Matrix4d& curve_segment_placement, const Eigen::Matrix4d& parent_curve_normalization, std::shared_ptr<parent_curve_function> parent_curve_fn) :
|
||||
curve_segment_placement_(curve_segment_placement),
|
||||
parent_curve_normalization_(parent_curve_normalization),
|
||||
parent_curve_fn_(parent_curve_fn) {
|
||||
@@ -153,7 +153,7 @@ struct curve_segment_function {
|
||||
|
||||
// this is the piecewise curve segment function for cant
|
||||
struct cant_curve_segment_function {
|
||||
cant_curve_segment_function(const Eigen::Matrix4d& curve_segment_placement, const Eigen::Matrix4d& parent_curve_start_point, std::shared_ptr<parent_curve_function> parent_curve_fn) :
|
||||
cant_curve_segment_function(const Eigen::Matrix4d& curve_segment_placement, const Eigen::Matrix4d& parent_curve_start_point, std::shared_ptr<parent_curve_function> parent_curve_fn) :
|
||||
curve_segment_placement_(curve_segment_placement),
|
||||
parent_curve_start_point_(parent_curve_start_point),
|
||||
parent_curve_fn_(parent_curve_fn) {
|
||||
@@ -354,7 +354,7 @@ class curve_segment_evaluator {
|
||||
} else {
|
||||
// The parent curve function returns the 4x4 matrix for the parent curve.
|
||||
// Normalize the parent curve so that the trim start point and tangent direction at the start point
|
||||
// are aligned with the origin. This is accomplished with a normalization matrix that subtracts the
|
||||
// are aligned with the origin. This is accomplished with a normalization matrix that subtracts the
|
||||
// incremental parent curve start point and applies a rotation. Apply the incremental
|
||||
// translation and rotation to the curve_segment_placement to get the curve_segment_point
|
||||
|
||||
@@ -623,7 +623,7 @@ class curve_segment_evaluator {
|
||||
} else if (segment_type_ == ST_CANT) {
|
||||
std::optional<std::function<double(double)>> super, slope;
|
||||
std::tie(super, slope) = get_superelevation_functions();
|
||||
|
||||
|
||||
auto cant = [constant_term, cosine_term, L](double t) -> double {
|
||||
auto a0 = constant_term.has_value() ? 1 / constant_term.value() : 0.0;
|
||||
auto a1 = (1 / cosine_term) * cos(PI * t / L);
|
||||
@@ -690,7 +690,7 @@ class curve_segment_evaluator {
|
||||
} else if (segment_type_ == ST_CANT) {
|
||||
std::optional<std::function<double(double)>> super, slope;
|
||||
std::tie(super, slope) = get_superelevation_functions();
|
||||
|
||||
|
||||
auto cant = [constant_term, linear_term, sine_term, L](double t) -> double {
|
||||
auto a0 = constant_term.has_value() ? 1 / constant_term.value() : 0.0;
|
||||
auto a1 = linear_term.has_value() ? (linear_term.value()/fabs(linear_term.value())) * pow(1 / linear_term.value(), 2.0) * t : 0.0;
|
||||
@@ -825,7 +825,7 @@ class curve_segment_evaluator {
|
||||
#else
|
||||
A3 = c.QubicTerm();
|
||||
#endif
|
||||
|
||||
|
||||
if (segment_type_ == ST_CANT) {
|
||||
polynomial_cant_spiral(A0, A1, A2, A3, A4, A5, A6, A7);
|
||||
} else {
|
||||
@@ -983,7 +983,7 @@ class curve_segment_evaluator {
|
||||
[](double /*u*/) -> Eigen::Matrix4d { return Eigen::Matrix4d::Identity(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void operator()(const IfcSchema::IfcLine& l) {
|
||||
projected_length_ = length_;
|
||||
|
||||
@@ -995,8 +995,8 @@ class curve_segment_evaluator {
|
||||
// 8.9.3.30 IfcDirection https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/lexical/IfcDirection.htm
|
||||
// "The IfcDirection does not imply a vector length, and the direction ratios does not have to be normalized."
|
||||
//
|
||||
// Therefore, the direction ratios need to be normalized to compute points on the line.
|
||||
//
|
||||
// Therefore, the direction ratios need to be normalized to compute points on the line.
|
||||
//
|
||||
// Magnitude is not used because it relates to the parameterization of the line, which isn't currently done for IfcCurveSegment
|
||||
// @todo - parameterization was recently added so Magnitude needs to be taking into consideration
|
||||
auto dr = l.Dir().Orientation().DirectionRatios();
|
||||
@@ -1030,7 +1030,7 @@ class curve_segment_evaluator {
|
||||
|
||||
auto pcDZy = curve_segment_placement_ ? (*curve_segment_placement_)(1, 2) : 0.;
|
||||
auto pcDZz = curve_segment_placement_ ? (*curve_segment_placement_)(2, 2) : 1.;
|
||||
|
||||
|
||||
parent_curve_fn_ = std::make_shared<line_parent_curve>(
|
||||
[segment_type = segment_type_,pcX, pcY, pcDXx, pcDXy, pcDZy, pcDZz, convert_u](double u)->Eigen::Matrix4d {
|
||||
u = convert_u(u);
|
||||
|
||||
@@ -56,7 +56,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcExtrudedAreaSolidTapered& in
|
||||
if (has_position) {
|
||||
matrix = taxonomy::cast<taxonomy::matrix4>(map(inst.Position()));
|
||||
}
|
||||
|
||||
|
||||
loft->matrix = matrix;
|
||||
|
||||
return loft;
|
||||
|
||||
@@ -24,7 +24,7 @@ using namespace ifcopenshell::geom;
|
||||
#ifdef SCHEMA_HAS_IfcIndexedPolyCurve
|
||||
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcIndexedPolyCurve& inst) {
|
||||
|
||||
|
||||
auto point_list = inst.Points();
|
||||
std::vector< std::vector<double> > coordinates;
|
||||
if (point_list.as<IfcSchema::IfcCartesianPointList2D>()) {
|
||||
@@ -100,7 +100,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcIndexedPolyCurve& inst) {
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcLShapeProfileDef& inst) {
|
||||
const double x = inst.Width().value_or(inst.Depth()) / 2.0f * length_unit_;
|
||||
const double d = inst.Thickness() * length_unit_;
|
||||
const double slope = inst.LegSlope().value_or(0.) * angle_unit_;
|
||||
|
||||
|
||||
double f1 = 0.0f;
|
||||
double f2 = 0.0f;
|
||||
if (doFillet) {
|
||||
|
||||
@@ -84,11 +84,11 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances& inst
|
||||
double pz = first_offset_value.OffsetVertical().value_or(0.0);
|
||||
py *= length_unit_;
|
||||
pz *= length_unit_;
|
||||
|
||||
auto fn = [py, pz](double /*u*/) -> Eigen::Matrix4d {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.col(3)(1) = py;
|
||||
m.col(3)(2) = pz;
|
||||
|
||||
auto fn = [py, pz](double /*u*/) -> Eigen::Matrix4d {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.col(3)(1) = py;
|
||||
m.col(3)(2) = pz;
|
||||
return m; };
|
||||
offset_spans.emplace_back(taxonomy::make<taxonomy::functor_item>(first_distance, fn));
|
||||
}
|
||||
@@ -146,7 +146,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances& inst
|
||||
zn = zn_at_end;
|
||||
}
|
||||
|
||||
|
||||
|
||||
auto fn = [yp, yn, zp, zn, l](double u) -> Eigen::Matrix4d {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.col(3)(1) = (l == 0.0 ? yp : (yp + (yn - yp) * u / l));
|
||||
@@ -171,10 +171,10 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances& inst
|
||||
py *= length_unit_;
|
||||
pz *= length_unit_;
|
||||
double l = basis_curve_length - last_distance;
|
||||
auto fn = [py, pz](double /*u*/) -> Eigen::Matrix4d {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.col(3)(1) = py;
|
||||
m.col(3)(2) = pz;
|
||||
auto fn = [py, pz](double /*u*/) -> Eigen::Matrix4d {
|
||||
Eigen::Matrix4d m = Eigen::Matrix4d::Identity();
|
||||
m.col(3)(1) = py;
|
||||
m.col(3)(2) = pz;
|
||||
return m; };
|
||||
|
||||
offset_spans.emplace_back(taxonomy::make<taxonomy::functor_item>(l, fn));
|
||||
|
||||
@@ -87,7 +87,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOpenCrossProfileDef& inst) {
|
||||
auto r = taxonomy::loop::ptr((taxonomy::loop*)mapped->clone_());
|
||||
r->closed = false;
|
||||
return r;
|
||||
}*/
|
||||
}*/
|
||||
|
||||
mapped->closed = false;
|
||||
mapped->tags = tags;
|
||||
|
||||
@@ -24,5 +24,5 @@ using namespace ifcopenshell::geom;
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPlane& inst) {
|
||||
auto p = taxonomy::make<taxonomy::plane>();
|
||||
p->matrix = taxonomy::cast<taxonomy::matrix4>(map(inst.Position()));
|
||||
return p;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPointByDistanceExpression& i
|
||||
|
||||
if (inst.OffsetLateral().has_value()) {
|
||||
auto offset_lateral = inst.OffsetLateral().value() * length_unit_;
|
||||
auto y = Eigen::Vector3d(m.col(1)(0), m.col(1)(1), m.col(1)(2));
|
||||
auto y = Eigen::Vector3d(m.col(1)(0), m.col(1)(1), m.col(1)(2));
|
||||
o += offset_lateral * y;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPolyLoop& inst) {
|
||||
|
||||
int count = polygon.size();
|
||||
if (original_count - count != 0) {
|
||||
std::stringstream ss; ss << (original_count - count) << " edges removed for:";
|
||||
std::stringstream ss; ss << (original_count - count) << " edges removed for:";
|
||||
logger_.message(ifcopenshell::logger::LOG_WARNING, "GEO", 280, ss.str(), inst);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcPolygonalFaceSet& inst) {
|
||||
for (auto& f : polygonal_faces) {
|
||||
auto fa = taxonomy::make<taxonomy::face>();
|
||||
shell->children.push_back(fa);
|
||||
|
||||
|
||||
{
|
||||
auto loop = taxonomy::make<taxonomy::loop>();
|
||||
fa->children = { loop };
|
||||
|
||||
@@ -44,7 +44,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcProduct& inst) {
|
||||
}
|
||||
|
||||
if (openings->size() && !settings_.get(IteratorSettings::DISABLE_OPENING_SUBTRACTIONS) && use_body) {
|
||||
|
||||
|
||||
Eigen::Matrix4d ci;
|
||||
if (c->matrix.components_) {
|
||||
ci = c->matrix.components_->inverse();
|
||||
@@ -69,7 +69,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcProduct& inst) {
|
||||
c->children = { child };
|
||||
} else {
|
||||
delete c;
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcRectangleHollowProfileDef& i
|
||||
|
||||
const double r1 = fr1 ? (*inst.OuterFilletRadius()) * length_unit_ : 0.;
|
||||
const double r2 = fr2 ? (*inst.InnerFilletRadius()) * length_unit_ : 0.;
|
||||
|
||||
|
||||
const double tol = settings_.get<settings::Precision>().get();
|
||||
|
||||
if (x < tol || y < tol) {
|
||||
|
||||
@@ -42,7 +42,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcRectangleProfileDef& inst) {
|
||||
if (has_position) {
|
||||
m4 = taxonomy::cast<taxonomy::matrix4>(map(inst.Position()));
|
||||
}
|
||||
|
||||
|
||||
return profile_helper(m4, {
|
||||
{{-x,-y}},
|
||||
{{x,-y}},
|
||||
|
||||
@@ -27,7 +27,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcRevolvedAreaSolid& inst) {
|
||||
const double ang = inst.Angle() * angle_unit_;
|
||||
|
||||
taxonomy::cast<taxonomy::face>(map(inst.SweptArea()));
|
||||
|
||||
|
||||
std::optional<double> angle;
|
||||
|
||||
taxonomy::matrix4::ptr matrix;
|
||||
|
||||
@@ -32,7 +32,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcRightCircularCylinder&) {
|
||||
BRepPrimAPI_MakeCylinder builder(r, h);
|
||||
gp_Trsf trsf;
|
||||
ifcopenshell::geom::Kernel::convert(inst.Position(),trsf);
|
||||
|
||||
|
||||
// IfcCsgPrimitive3D.Position has unit scale factor
|
||||
shape = builder.Solid().Moved(trsf);
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal& in
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
{
|
||||
auto css = inst.CrossSections();
|
||||
auto csps = inst.CrossSectionPositions();
|
||||
std::vector<taxonomy::face::ptr> faces;
|
||||
@@ -47,7 +47,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal& in
|
||||
// reference frame along a certain curve location (b) the longitude.
|
||||
|
||||
// The longitudes determine the range of the sweep and the offsets are interpolated in between
|
||||
// sweep segments.
|
||||
// sweep segments.
|
||||
std::vector<Eigen::Vector3d> profile_offsets;
|
||||
std::vector<std::optional<Eigen::Matrix3d>> profile_rotations;
|
||||
std::vector<double> longitudes;
|
||||
|
||||
@@ -39,7 +39,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface& inst) {
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
{
|
||||
auto css = inst.CrossSections();
|
||||
auto csps = inst.CrossSectionPositions();
|
||||
std::vector<taxonomy::geom_item::ptr> faces;
|
||||
@@ -48,7 +48,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSurface& inst) {
|
||||
// reference frame along a certain curve location (b) the longitude.
|
||||
|
||||
// The longitudes determine the range of the sweep and the offsets are interpolated in between
|
||||
// sweep segments.
|
||||
// sweep segments.
|
||||
std::vector<Eigen::Vector3d> profile_offsets;
|
||||
std::vector<std::optional<Eigen::Matrix3d>> profile_rotations;
|
||||
std::vector<double> longitudes;
|
||||
|
||||
@@ -28,7 +28,7 @@ using namespace ifcopenshell::geom;
|
||||
taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve& inst) {
|
||||
if (!inst.BaseCurve().as<IfcSchema::IfcGradientCurve>())
|
||||
logger_.warning("GEO", 291, "Expected IfcSegmentedReferenceCurve.BaseCurve to be IfcGradient", inst); // CT 4.1.7.1.1.3
|
||||
|
||||
|
||||
auto segments = inst.Segments();
|
||||
|
||||
taxonomy::piecewise_function::span_list spans;
|
||||
|
||||
@@ -97,9 +97,9 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSweptDiskSolid& inst) {
|
||||
return taxonomy::make<taxonomy::sweep_along_curve>(taxonomy::make<taxonomy::matrix4>(), f, nullptr, loop);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
TopoDS_Wire wire, section1, section2;
|
||||
|
||||
bool hasInnerRadius = !!inst.InnerRadius();
|
||||
@@ -107,7 +107,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSweptDiskSolid& inst) {
|
||||
if (!convert_wire(inst.Directrix(), wire)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (util::count(wire, TopAbs_EDGE) == 1 && sp && ep) {
|
||||
@@ -119,7 +119,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSweptDiskSolid& inst) {
|
||||
double a, b;
|
||||
auto crv = BRep_Tool::Curve(e, a, b);
|
||||
if ((crv->DynamicType() == STANDARD_TYPE(Geom_Circle)) ||
|
||||
(crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse)))
|
||||
(crv->DynamicType() == STANDARD_TYPE(Geom_Ellipse)))
|
||||
{
|
||||
BRepBuilderAPI_MakeEdge me(crv, *sp, *ep);
|
||||
if (me.IsDone()) {
|
||||
@@ -227,7 +227,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSweptDiskSolid& inst) {
|
||||
j += 1;
|
||||
} else {
|
||||
ifcopenshell::logger::root().error("Unexpected amount of fillet edges generated");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ifcopenshell::logger::root().error("Unable to build fillet, probably edge too short");
|
||||
}
|
||||
@@ -258,7 +258,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSweptDiskSolid& inst) {
|
||||
// NB: Note that StartParam and EndParam param are ignored and the assumption is
|
||||
// made that the parametric range over which to be swept matches the IfcCurve in
|
||||
// its entirety.
|
||||
|
||||
|
||||
util::process_sweep(wire, inst.Radius() * length_unit_, shape);
|
||||
|
||||
if (shape.IsNull()) {
|
||||
|
||||
@@ -43,7 +43,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcTShapeProfileDef& inst) {
|
||||
logger_.message(ifcopenshell::logger::LOG_NOTICE, "GEO", 296, "Skipping zero sized profile:", inst);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
double dy1 = 0.0f;
|
||||
double dy2 = 0.0f;
|
||||
double dx1 = 0.0f;
|
||||
|
||||
@@ -29,21 +29,21 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcTrimmedCurve& inst) {
|
||||
auto basis_curve = inst.BasisCurve();
|
||||
bool isConic = basis_curve.declaration().is(IfcSchema::IfcConic::Class());
|
||||
double parameterFactor = isConic ? angle_unit_ : length_unit_;
|
||||
|
||||
|
||||
auto tc = taxonomy::make<taxonomy::edge>();
|
||||
tc->basis = map(inst.BasisCurve());
|
||||
|
||||
|
||||
bool trim_cartesian = inst.MasterRepresentation() != IfcSchema::IfcTrimmingPreference::IfcTrimmingPreference_PARAMETER;
|
||||
auto trims1 = inst.Trim1();
|
||||
auto trims2 = inst.Trim2();
|
||||
|
||||
|
||||
// reversed orientation handling happens in geometry kernel
|
||||
unsigned sense_agreement = 0;
|
||||
double flts[2];
|
||||
taxonomy::point3::ptr pnts[2];
|
||||
bool has_flts[2] = {false,false};
|
||||
bool has_pnts[2] = {false,false};
|
||||
|
||||
|
||||
tc->curve_sense = inst.SenseAgreement();
|
||||
|
||||
for (auto it = trims1.begin(); it != trims1.end(); it ++) {
|
||||
|
||||
@@ -33,7 +33,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcUShapeProfileDef& inst) {
|
||||
const double d1 = inst.WebThickness() * length_unit_;
|
||||
const double d2 = inst.FlangeThickness() * length_unit_;
|
||||
const double slope = inst.FlangeSlope().value_or(0.) * angle_unit_;
|
||||
|
||||
|
||||
double dy1 = 0.0f;
|
||||
double dy2 = 0.0f;
|
||||
double f1 = 0.0f;
|
||||
|
||||
@@ -31,7 +31,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcZShapeProfileDef& inst) {
|
||||
|
||||
bool doFillet = !!inst.FilletRadius();
|
||||
bool doEdgeFillet = !!inst.EdgeRadius();
|
||||
|
||||
|
||||
double f1 = 0.;
|
||||
double f2 = 0.;
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ std::vector<IfcSchema::IfcProduct> mapping::products_represented_by(const IfcSch
|
||||
auto invs = prodrep.file()->get_inverse(prodrep.id(), &IfcSchema::IfcProduct::Class(), -1);
|
||||
for (auto& inv : invs) {
|
||||
products.push_back(inv.as<IfcSchema::IfcProduct>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (only_direct) {
|
||||
@@ -169,7 +169,7 @@ bool mapping::reuse_ok_(const std::vector<IfcSchema::IfcProduct>& products) {
|
||||
|
||||
std::vector<express::base> mapping::find_openings(const express::base& inst) {
|
||||
std::vector<express::base> openings;
|
||||
|
||||
|
||||
if (auto rep = inst.as<IfcSchema::IfcRepresentation>()) {
|
||||
// @todo this is essentially only for hybrid kernel trying to guess
|
||||
// when not to use a simple kernel.
|
||||
@@ -246,11 +246,11 @@ void mapping::get_representations(std::vector<geometry_conversion_task>& tasks,
|
||||
int task_index = 0;
|
||||
|
||||
std::set<IfcSchema::IfcProduct> products_seen;
|
||||
|
||||
|
||||
for (auto representation : representations) {
|
||||
IfcSchema::IfcRepresentationMap rmap;
|
||||
std::vector<IfcSchema::IfcProduct> ifcproducts = filter_products(products_represented_by(representation, rmap, false), filters);
|
||||
|
||||
|
||||
if (ifcproducts.empty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -838,7 +838,7 @@ express::base mapping::get_decomposing_entity(const express::base& inst, bool in
|
||||
for (auto it = parents.begin(); it != parents.end(); ++it) {
|
||||
IfcSchema::IfcRelDecomposes decompose = (*it).as<IfcSchema::IfcRelDecomposes>();
|
||||
express::base ifc_objectdef;
|
||||
|
||||
|
||||
ifc_objectdef = get_RelatingObject(decompose);
|
||||
|
||||
if (!ifc_objectdef || product == ifc_objectdef) continue;
|
||||
@@ -871,7 +871,7 @@ void mapping::initialize_units_() {
|
||||
length_unit_ = 1.;
|
||||
angle_unit_ = -1.;
|
||||
length_unit_name_ = "METER";
|
||||
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcContext
|
||||
auto projects = file_->instances_by_type<IfcSchema::IfcContext>();
|
||||
#else
|
||||
@@ -1133,7 +1133,7 @@ bool mapping::get_layerset_information(const express::base& p, layerset_informat
|
||||
std::vector<IfcSchema::IfcExtrudedAreaSolid> extrusions;
|
||||
for (auto& r : resources) {
|
||||
if (auto ex = r.as<IfcSchema::IfcExtrudedAreaSolid>()) {
|
||||
extrusions.push_back(ex);
|
||||
extrusions.push_back(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1209,7 +1209,7 @@ bool mapping::get_layerset_information(const express::base& p, layerset_informat
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
namespace ifcopenshell {
|
||||
|
||||
namespace geom {
|
||||
|
||||
|
||||
class POSTFIX_SCHEMA(mapping) : public abstract_mapping {
|
||||
private:
|
||||
ifcopenshell::file* file_;
|
||||
@@ -30,7 +30,7 @@ namespace geom {
|
||||
const express::base placement_rel_to_instance_;
|
||||
|
||||
Eigen::Matrix4d offset_and_rotation_ = Eigen::Matrix4d::Identity();
|
||||
|
||||
|
||||
void initialize_units_();
|
||||
void addRepresentationsFromContextIds(std::vector<IfcSchema::IfcRepresentation>&);
|
||||
void addRepresentationsFromPriorities(std::vector<IfcSchema::IfcRepresentation>&);
|
||||
@@ -136,7 +136,7 @@ namespace geom {
|
||||
template <>
|
||||
struct element_type<taxonomy::solid> {
|
||||
typedef taxonomy::shell type;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename U = taxonomy::collection, typename T>
|
||||
typename U::ptr map_to_collection(POSTFIX_SCHEMA(mapping)* m, const T& ts) {
|
||||
|
||||
@@ -182,4 +182,4 @@ BIND(IfcSurfaceStyle); // -> style
|
||||
|
||||
#ifdef SCHEMA_HAS_IfcCurveSegment
|
||||
BIND(IfcCurveSegment);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -7,8 +7,8 @@ taxonomy::loop::ptr ifcopenshell::geom::fillet_loop(taxonomy::loop::ptr loop, do
|
||||
const auto child_count = static_cast<int>(loop->children.size());
|
||||
for (int b = 0; b < child_count; ++b) {
|
||||
int c = (b + child_count - 1) % child_count;
|
||||
pps[b] = {
|
||||
std::get<taxonomy::point3::ptr>(loop->children[c]->start)->ccomponents(),
|
||||
pps[b] = {
|
||||
std::get<taxonomy::point3::ptr>(loop->children[c]->start)->ccomponents(),
|
||||
radius, loop->children[c], loop->children[b]
|
||||
};
|
||||
}
|
||||
@@ -227,7 +227,7 @@ std::pair<std::vector<taxonomy::point3::ptr>, std::vector<std::set<std::string>>
|
||||
if (equal) {
|
||||
// do not remove the first or last point to
|
||||
// maintain connectivity with other wires
|
||||
|
||||
|
||||
/*
|
||||
// Only removing direct equality so does not impact connectivity
|
||||
if ((closed && j == 0) || (!closed && j == (n - 1))) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user