mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 02:23:34 +00:00
Merge branch 'v0.8.0' into tfk-unify-variant-storage
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
name: ci-black-formatting
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint-formatting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: psf/black@stable
|
||||
@@ -0,0 +1,65 @@
|
||||
name: ci-ifcconvert
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
activate:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'IfcOpenShell/IfcOpenShell'
|
||||
steps:
|
||||
- name: Set env
|
||||
run: echo ok go
|
||||
|
||||
build:
|
||||
needs: activate
|
||||
name: ${{ matrix.config.name }}-${{ matrix.pyver }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {
|
||||
name: "Windows 64bit",
|
||||
short_name: win64,
|
||||
}
|
||||
- {
|
||||
name: "Windows 32bit",
|
||||
short_name: win32,
|
||||
}
|
||||
- {
|
||||
name: "Linux 64bit",
|
||||
short_name: linux64
|
||||
}
|
||||
- {
|
||||
name: "MacOS Intel 64bit",
|
||||
short_name: macos64
|
||||
}
|
||||
- {
|
||||
name: "MacOS Silicon 64bit",
|
||||
short_name: macosm164
|
||||
}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2 # https://github.com/actions/setup-python
|
||||
with:
|
||||
python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax
|
||||
architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified
|
||||
- run: echo ${{ env.DATE }}
|
||||
- name: Get current version
|
||||
id: version
|
||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||
- name: Compile
|
||||
run: |
|
||||
cd src/ifcopenshell-python &&
|
||||
make zip-ifcconvert PLATFORM=${{ matrix.config.short_name }}
|
||||
- name: Upload zip file to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/ifcopenshell-python/dist/ifcconvert-${{ steps.version.outputs.version }}-${{ matrix.config.short_name }}.zip
|
||||
asset_name: ifcconvert-${{ steps.version.outputs.version }}-${{ matrix.config.short_name }}.zip
|
||||
release_name: "ifcconvert-${{steps.version.outputs.version}}"
|
||||
tag: "ifcconvert-${{steps.version.outputs.version}}"
|
||||
overwrite: true
|
||||
@@ -47,6 +47,7 @@ jobs:
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install xmlschema xsdata numpy lxml pytest isodate lark networkx tabulate python-dateutil shapely
|
||||
pip install mathutils
|
||||
pip install src/bcf --no-deps
|
||||
pip install https://github.com/Andrej730/aud/archive/refs/heads/master-reduced-size.zip
|
||||
|
||||
@@ -125,4 +126,9 @@ jobs:
|
||||
python tests.py
|
||||
cd ../src/ifcopenshell-python
|
||||
mv ifcopenshell ifcopenshell-local # Force testing on installed module
|
||||
make test-safe
|
||||
make test
|
||||
cd ../bcf && make test
|
||||
pip install requests
|
||||
cd ../bsdd && make test
|
||||
cd ../ifcpatch && make test
|
||||
cd ../ifctester && make test
|
||||
|
||||
@@ -46,12 +46,12 @@ Contents
|
||||
| ifcblender | Historic Blender IFC import add-on | LGPL-3.0-or-later\* |
|
||||
| ifccityjson | Convert CityJSON to IFC | LGPL-3.0-or-later | [](https://pypi.org/project/ifccityjson/) |
|
||||
| ifcclash | Clash detection library and CLI app | LGPL-3.0-or-later | [](https://pypi.org/project/ifcclash/) |
|
||||
| ifcconvert | CLI app to convert IFC to many other formats | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcconvert/installation.html)
|
||||
| ifcconvert | CLI app to convert IFC to many other formats | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcconvert/installation.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcconvert&expanded=true)
|
||||
| ifccsv | Library and CLI app to export and import schedules from IFC | LGPL-3.0-or-later | [](https://pypi.org/project/ifccsv/) |
|
||||
| ifcdiff | Compare changes between IFC models | LGPL-3.0-or-later | [](https://pypi.org/project/ifcdiff/) |
|
||||
| ifcfm | Extract IFC data for FM handover requirements | LGPL-3.0-or-later | [](https://pypi.org/project/ifcfm/) |
|
||||
| ifcmax | Historic extension for IFC support in 3DS Max | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcmax.html)
|
||||
| ifcopenshell-python | Python library for IFC manipulation | LGPL-3.0-or-later\* | [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcopenshell-python&expanded=true) [](https://pypi.org/project/ifcopenshell/) [](https://anaconda.org/conda-forge/ifcopenshell) [](https://anaconda.org/ifcopenshell/ifcopenshell) [](https://hub.docker.com/r/aecgeeks/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell-git) |
|
||||
| ifcopenshell-python | Python library for IFC manipulation | LGPL-3.0-or-later\* | [](https://docs.ifcopenshell.org/ifcopenshell-python/installation.html) [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcopenshell-python&expanded=true) [](https://pypi.org/project/ifcopenshell/) [](https://anaconda.org/conda-forge/ifcopenshell) [](https://anaconda.org/ifcopenshell/ifcopenshell) [](https://hub.docker.com/r/aecgeeks/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell) [](https://aur.archlinux.org/packages/ifcopenshell-git) |
|
||||
| ifcpatch | Utility to run pre-packaged scripts to manipulate IFCs | LGPL-3.0-or-later | [](https://pypi.org/project/ifcpatch/) |
|
||||
| ifcsverchok | Blender Add-on for visual node programming with IFC | GPL-3.0-or-later | [](https://github.com/IfcOpenShell/IfcOpenShell/releases?q=ifcsverchok&expanded=true)
|
||||
| ifctester | Library, CLI and webapp for IDS model auditing | LGPL-3.0-or-later | [](https://pypi.org/project/ifctester/) |
|
||||
|
||||
@@ -221,10 +221,11 @@ endif()
|
||||
|
||||
if(GLTF_SUPPORT OR CITYJSON_SUPPORT)
|
||||
UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR)
|
||||
find_file(json_hpp "json.hpp" ${JSON_INCLUDE_DIR}/nlohmann)
|
||||
find_path(json_header_path "json.hpp" ${JSON_INCLUDE_DIR} PATH_SUFFIXES "nlohmann")
|
||||
set(JSON_INCLUDE_DIR ${json_header_path})
|
||||
|
||||
if(json_hpp)
|
||||
message(STATUS "JSON for Modern C++ header file found")
|
||||
if(json_header_path)
|
||||
message(STATUS "JSON for Modern C++ header file found in ${JSON_INCLUDE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR "Unable to find JSON for Modern C++ header file, aborting")
|
||||
endif()
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
include = '''
|
||||
src/(
|
||||
bcf
|
||||
|bcfserver
|
||||
|blenderbim
|
||||
|bsdd
|
||||
|foundationserver
|
||||
|ifc2ca
|
||||
|ifc4d
|
||||
|ifc5d
|
||||
|ifcbimtester
|
||||
|ifcblender
|
||||
|ifccityjson
|
||||
|ifcclash
|
||||
|ifccsv
|
||||
|ifcdiff
|
||||
|ifcfm
|
||||
|ifcpatch
|
||||
|ifctester
|
||||
|ifcopenshell-python
|
||||
|ifcsverchok
|
||||
|opencdeserver
|
||||
)/.*.py$
|
||||
'''
|
||||
extend-exclude = '''
|
||||
src/ifcopenshell-python/ifcopenshell/express/*
|
||||
|src/ifcopenshell-python/ifcopenshell/mvd/*
|
||||
|src/ifc2ca/templates/*
|
||||
'''
|
||||
|
||||
[tool.pyright]
|
||||
reportInvalidTypeForm = false
|
||||
|
||||
@@ -33,6 +33,10 @@ models:
|
||||
cd src && xsdata generate -p bcf.v2.model --unnest-classes --kw-only --slots -ds Google bcf/v2/xsd
|
||||
cd src && xsdata generate -p bcf.v3.model --unnest-classes --kw-only --slots -ds Google bcf/v3/xsd
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
pytest -p no:pytest-blender tests
|
||||
|
||||
# .PHONY
|
||||
# api:
|
||||
# openapi-python-client generate --url https://api.swaggerhub.com/apis/buildingSMART/BCF/3.0
|
||||
|
||||
@@ -18,6 +18,7 @@ GNU Lesser General Public License for more details.
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with BCF. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
|
||||
@@ -5,8 +5,8 @@ from numpy.typing import NDArray
|
||||
|
||||
|
||||
def camera_vectors_from_element_placement(
|
||||
elem_placement: NDArray[np.float_],
|
||||
) -> tuple[NDArray[np.float_], NDArray[np.float_], NDArray[np.float_]]:
|
||||
elem_placement: NDArray[np.float64],
|
||||
) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]:
|
||||
"""
|
||||
Calculate the vectors of a camera pointing to an element.
|
||||
|
||||
@@ -21,8 +21,8 @@ def camera_vectors_from_element_placement(
|
||||
|
||||
|
||||
def camera_vectors_from_target_position(
|
||||
target_position: NDArray[np.float_], offset: Optional[NDArray[np.float_]] = None
|
||||
) -> tuple[NDArray[np.float_], NDArray[np.float_], NDArray[np.float_]]:
|
||||
target_position: NDArray[np.float64], offset: Optional[NDArray[np.float64]] = None
|
||||
) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]:
|
||||
"""
|
||||
Calculate the vectors of a camera pointing to a target point.
|
||||
|
||||
@@ -50,7 +50,7 @@ def camera_vectors_from_target_position(
|
||||
# return camera_position, -mat[:3, 2], mat[:3, 1]
|
||||
|
||||
|
||||
def unit_vector(v: NDArray[np.float_]) -> NDArray[np.float_]:
|
||||
def unit_vector(v: NDArray[np.float64]) -> NDArray[np.float64]:
|
||||
"""
|
||||
Return the unit vector of a vector.
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ Copyright (c) 2017-2020 Anthon van der Neut, Ruamel bvba
|
||||
|
||||
original idea from https://stackoverflow.com/a/19722365/1307905
|
||||
"""
|
||||
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
from os import PathLike
|
||||
@@ -13,8 +14,7 @@ from typing import Any, Optional, Protocol
|
||||
|
||||
|
||||
class ZipFileInterface(Protocol):
|
||||
def writestr(self, filename_in_zip: str | zipfile.ZipInfo, file_contents: bytes | str) -> None:
|
||||
...
|
||||
def writestr(self, filename_in_zip: str | zipfile.ZipInfo, file_contents: bytes | str) -> None: ...
|
||||
|
||||
|
||||
class InMemoryZipFile:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""BCF XML V2 handler."""
|
||||
|
||||
import uuid
|
||||
import warnings
|
||||
import zipfile
|
||||
|
||||
@@ -34,7 +34,7 @@ class BimSnippet:
|
||||
metadata={
|
||||
"name": "isExternal",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class HeaderFile:
|
||||
"name": "Filename",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
date: Optional[XmlDateTime] = field(
|
||||
default=None,
|
||||
@@ -72,7 +72,7 @@ class HeaderFile:
|
||||
"name": "Date",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
reference: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -80,7 +80,7 @@ class HeaderFile:
|
||||
"name": "Reference",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
ifc_project: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -89,7 +89,7 @@ class HeaderFile:
|
||||
"type": "Attribute",
|
||||
"length": 22,
|
||||
"pattern": r"[0-9,A-Z,a-z,_$]*",
|
||||
}
|
||||
},
|
||||
)
|
||||
ifc_spatial_structure_element: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -98,14 +98,14 @@ class HeaderFile:
|
||||
"type": "Attribute",
|
||||
"length": 22,
|
||||
"pattern": r"[0-9,A-Z,a-z,_$]*",
|
||||
}
|
||||
},
|
||||
)
|
||||
is_external: bool = field(
|
||||
default=True,
|
||||
metadata={
|
||||
"name": "isExternal",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ class TopicDocumentReference:
|
||||
"name": "ReferencedDocument",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
description: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -128,7 +128,7 @@ class TopicDocumentReference:
|
||||
"name": "Description",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -136,14 +136,14 @@ class TopicDocumentReference:
|
||||
"name": "Guid",
|
||||
"type": "Attribute",
|
||||
"pattern": r"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}",
|
||||
}
|
||||
},
|
||||
)
|
||||
is_external: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"name": "isExternal",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ class ViewPoint:
|
||||
"name": "Viewpoint",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
snapshot: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -178,7 +178,7 @@ class ViewPoint:
|
||||
"name": "Snapshot",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
index: Optional[int] = field(
|
||||
default=None,
|
||||
@@ -186,7 +186,7 @@ class ViewPoint:
|
||||
"name": "Index",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
@@ -230,7 +230,7 @@ class Comment:
|
||||
"name": "Viewpoint",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
modified_date: Optional[XmlDateTime] = field(
|
||||
default=None,
|
||||
@@ -238,7 +238,7 @@ class Comment:
|
||||
"name": "ModifiedDate",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
modified_author: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -246,7 +246,7 @@ class Comment:
|
||||
"name": "ModifiedAuthor",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
@@ -267,7 +267,7 @@ class Header:
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"min_occurs": 1,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ class Topic:
|
||||
"name": "ReferenceLink",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
title: str = field(
|
||||
metadata={
|
||||
@@ -295,7 +295,7 @@ class Topic:
|
||||
"name": "Priority",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
index: Optional[int] = field(
|
||||
default=None,
|
||||
@@ -303,7 +303,7 @@ class Topic:
|
||||
"name": "Index",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
labels: List[str] = field(
|
||||
default_factory=list,
|
||||
@@ -311,7 +311,7 @@ class Topic:
|
||||
"name": "Labels",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
creation_date: XmlDateTime = field(
|
||||
metadata={
|
||||
@@ -335,7 +335,7 @@ class Topic:
|
||||
"name": "ModifiedDate",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
modified_author: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -343,7 +343,7 @@ class Topic:
|
||||
"name": "ModifiedAuthor",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
due_date: Optional[XmlDateTime] = field(
|
||||
default=None,
|
||||
@@ -351,7 +351,7 @@ class Topic:
|
||||
"name": "DueDate",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
assigned_to: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -359,7 +359,7 @@ class Topic:
|
||||
"name": "AssignedTo",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
stage: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -367,7 +367,7 @@ class Topic:
|
||||
"name": "Stage",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
description: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -375,7 +375,7 @@ class Topic:
|
||||
"name": "Description",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
bim_snippet: Optional[BimSnippet] = field(
|
||||
default=None,
|
||||
@@ -383,7 +383,7 @@ class Topic:
|
||||
"name": "BimSnippet",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
document_reference: List[TopicDocumentReference] = field(
|
||||
default_factory=list,
|
||||
@@ -391,7 +391,7 @@ class Topic:
|
||||
"name": "DocumentReference",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
related_topic: List[TopicRelatedTopic] = field(
|
||||
default_factory=list,
|
||||
@@ -399,7 +399,7 @@ class Topic:
|
||||
"name": "RelatedTopic",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
@@ -414,14 +414,14 @@ class Topic:
|
||||
metadata={
|
||||
"name": "TopicType",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
topic_status: Optional[str] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "TopicStatus",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -433,7 +433,7 @@ class Markup:
|
||||
"name": "Header",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
topic: Topic = field(
|
||||
metadata={
|
||||
@@ -449,7 +449,7 @@ class Markup:
|
||||
"name": "Comment",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
viewpoints: List[ViewPoint] = field(
|
||||
default_factory=list,
|
||||
@@ -457,5 +457,5 @@ class Markup:
|
||||
"name": "Viewpoints",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ class Project:
|
||||
"name": "Name",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
project_id: str = field(
|
||||
metadata={
|
||||
@@ -29,7 +29,7 @@ class ProjectExtension:
|
||||
"name": "Project",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
extension_schema: str = field(
|
||||
metadata={
|
||||
|
||||
@@ -10,12 +10,12 @@ class Version:
|
||||
"name": "DetailedVersion",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
version_id: Optional[str] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "VersionId",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -15,14 +15,14 @@ class Component:
|
||||
metadata={
|
||||
"name": "OriginatingSystem",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
authoring_tool_id: Optional[str] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "AuthoringToolId",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
ifc_guid: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -31,7 +31,7 @@ class Component:
|
||||
"type": "Attribute",
|
||||
"length": 22,
|
||||
"pattern": r"[0-9,A-Z,a-z,_$]*",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -92,21 +92,21 @@ class ViewSetupHints:
|
||||
metadata={
|
||||
"name": "SpacesVisible",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
space_boundaries_visible: Optional[bool] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "SpaceBoundariesVisible",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
openings_visible: Optional[bool] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "OpeningsVisible",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ class ComponentColoringColor:
|
||||
"name": "Component",
|
||||
"type": "Element",
|
||||
"min_occurs": 1,
|
||||
}
|
||||
},
|
||||
)
|
||||
color: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -147,7 +147,7 @@ class ComponentColoringColor:
|
||||
"name": "Color",
|
||||
"type": "Attribute",
|
||||
"pattern": r"[0-9,a-f,A-F]{6}([0-9,a-f,A-F]{2})?",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ class ComponentSelection:
|
||||
"name": "Component",
|
||||
"type": "Element",
|
||||
"min_occurs": 1,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ class ComponentVisibilityExceptions:
|
||||
"name": "Component",
|
||||
"type": "Element",
|
||||
"min_occurs": 1,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ class OrthogonalCamera:
|
||||
camera_up_vector:
|
||||
view_to_world_scale: view's visible size in meters
|
||||
"""
|
||||
|
||||
camera_view_point: Point = field(
|
||||
metadata={
|
||||
"name": "CameraViewPoint",
|
||||
@@ -247,6 +248,7 @@ class PerspectiveCamera:
|
||||
release and viewers should be expect values outside this
|
||||
range in current implementations.
|
||||
"""
|
||||
|
||||
camera_view_point: Point = field(
|
||||
metadata={
|
||||
"name": "CameraViewPoint",
|
||||
@@ -336,7 +338,7 @@ class ComponentColoring:
|
||||
"name": "Color",
|
||||
"type": "Element",
|
||||
"min_occurs": 1,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -347,14 +349,14 @@ class ComponentVisibility:
|
||||
metadata={
|
||||
"name": "Exceptions",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
default_visibility: Optional[bool] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "DefaultVisibility",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -368,7 +370,7 @@ class VisualizationInfoClippingPlanes:
|
||||
metadata={
|
||||
"name": "ClippingPlane",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -383,7 +385,7 @@ class VisualizationInfoLines:
|
||||
"name": "Line",
|
||||
"type": "Element",
|
||||
"min_occurs": 1,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -394,14 +396,14 @@ class Components:
|
||||
metadata={
|
||||
"name": "ViewSetupHints",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
selection: Optional[ComponentSelection] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Selection",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
visibility: ComponentVisibility = field(
|
||||
metadata={
|
||||
@@ -415,7 +417,7 @@ class Components:
|
||||
metadata={
|
||||
"name": "Coloring",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -424,47 +426,48 @@ class VisualizationInfo:
|
||||
"""
|
||||
VisualizationInfo documentation.
|
||||
"""
|
||||
|
||||
components: Optional[Components] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Components",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
orthogonal_camera: Optional[OrthogonalCamera] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "OrthogonalCamera",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
perspective_camera: Optional[PerspectiveCamera] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "PerspectiveCamera",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
lines: Optional[VisualizationInfoLines] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Lines",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
clipping_planes: Optional[VisualizationInfoClippingPlanes] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "ClippingPlanes",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
bitmap: List[VisualizationInfoBitmap] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "Bitmap",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""BCF XML V2 Topic handler."""
|
||||
|
||||
import datetime
|
||||
import tempfile
|
||||
import uuid
|
||||
@@ -277,7 +278,7 @@ class TopicHandler:
|
||||
self.add_visinfo_handler(new_viewpoint)
|
||||
return new_viewpoint
|
||||
|
||||
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float_], *guids: str) -> None:
|
||||
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float64], *guids: str) -> None:
|
||||
"""Add a viewpoint pointing at an XYZ point in space
|
||||
|
||||
Args:
|
||||
|
||||
@@ -176,7 +176,7 @@ class VisualizationInfoHandler:
|
||||
@classmethod
|
||||
def create_from_point_and_guids(
|
||||
cls,
|
||||
position: NDArray[np.float_],
|
||||
position: NDArray[np.float64],
|
||||
*guids: str,
|
||||
xml_handler: Optional[AbstractXmlParserSerializer] = None,
|
||||
) -> "VisualizationInfoHandler":
|
||||
@@ -224,7 +224,7 @@ def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
|
||||
)
|
||||
|
||||
|
||||
def build_viewpoint_from_position_and_guids(position: NDArray[np.float_], *guids: str) -> mdl.VisualizationInfo:
|
||||
def build_viewpoint_from_position_and_guids(position: NDArray[np.float64], *guids: str) -> mdl.VisualizationInfo:
|
||||
"""
|
||||
Return a BCF viewpoint of an IFC element.
|
||||
|
||||
@@ -261,7 +261,7 @@ def build_components(*guids: str) -> mdl.Components:
|
||||
)
|
||||
|
||||
|
||||
def build_camera(elem_placement: NDArray[np.float_]) -> mdl.PerspectiveCamera:
|
||||
def build_camera(elem_placement: NDArray[np.float64]) -> mdl.PerspectiveCamera:
|
||||
"""
|
||||
Return a BCF camera for an IFC element placement matrix.
|
||||
|
||||
@@ -275,7 +275,7 @@ def build_camera(elem_placement: NDArray[np.float_]) -> mdl.PerspectiveCamera:
|
||||
|
||||
|
||||
def build_camera_from_vectors(
|
||||
camera_position: NDArray[np.float_], camera_dir: NDArray[np.float_], camera_up: NDArray[np.float_]
|
||||
camera_position: NDArray[np.float64], camera_dir: NDArray[np.float64], camera_up: NDArray[np.float64]
|
||||
) -> mdl.PerspectiveCamera:
|
||||
"""
|
||||
Return a BCF camera for an IFC element placement matrix.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""BCF XML V3 handlers."""
|
||||
|
||||
import uuid
|
||||
import warnings
|
||||
import zipfile
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""BCF XML V3 Documents handler."""
|
||||
|
||||
import zipfile
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class Document:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
@@ -45,7 +45,7 @@ class DocumentInfoDocuments:
|
||||
"name": "Document",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -57,5 +57,5 @@ class DocumentInfo:
|
||||
"name": "Documents",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ class ExtensionsPriorities:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class ExtensionsSnippetTypes:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class ExtensionsStages:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class ExtensionsTopicLabels:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class ExtensionsTopicStatuses:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ class ExtensionsTopicTypes:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class ExtensionsUsers:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ class Extensions:
|
||||
"name": "TopicTypes",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
topic_statuses: Optional[ExtensionsTopicStatuses] = field(
|
||||
default=None,
|
||||
@@ -137,7 +137,7 @@ class Extensions:
|
||||
"name": "TopicStatuses",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
priorities: Optional[ExtensionsPriorities] = field(
|
||||
default=None,
|
||||
@@ -145,7 +145,7 @@ class Extensions:
|
||||
"name": "Priorities",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
topic_labels: Optional[ExtensionsTopicLabels] = field(
|
||||
default=None,
|
||||
@@ -153,7 +153,7 @@ class Extensions:
|
||||
"name": "TopicLabels",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
users: Optional[ExtensionsUsers] = field(
|
||||
default=None,
|
||||
@@ -161,7 +161,7 @@ class Extensions:
|
||||
"name": "Users",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
snippet_types: Optional[ExtensionsSnippetTypes] = field(
|
||||
default=None,
|
||||
@@ -169,7 +169,7 @@ class Extensions:
|
||||
"name": "SnippetTypes",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
stages: Optional[ExtensionsStages] = field(
|
||||
default=None,
|
||||
@@ -177,5 +177,5 @@ class Extensions:
|
||||
"name": "Stages",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ class BimSnippet:
|
||||
metadata={
|
||||
"name": "IsExternal",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class DocumentReference:
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
"pattern": r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}",
|
||||
}
|
||||
},
|
||||
)
|
||||
url: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -78,7 +78,7 @@ class DocumentReference:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
description: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -88,7 +88,7 @@ class DocumentReference:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
@@ -110,7 +110,7 @@ class File:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
date: Optional[XmlDateTime] = field(
|
||||
default=None,
|
||||
@@ -118,7 +118,7 @@ class File:
|
||||
"name": "Date",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
reference: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -128,7 +128,7 @@ class File:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
ifc_project: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -137,7 +137,7 @@ class File:
|
||||
"type": "Attribute",
|
||||
"length": 22,
|
||||
"pattern": r"[0-9A-Za-z_$]*",
|
||||
}
|
||||
},
|
||||
)
|
||||
ifc_spatial_structure_element: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -146,14 +146,14 @@ class File:
|
||||
"type": "Attribute",
|
||||
"length": 22,
|
||||
"pattern": r"[0-9A-Za-z_$]*",
|
||||
}
|
||||
},
|
||||
)
|
||||
is_external: bool = field(
|
||||
default=True,
|
||||
metadata={
|
||||
"name": "IsExternal",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ class TopicLabels:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ class TopicReferenceLinks:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ class ViewPoint:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
snapshot: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -226,7 +226,7 @@ class ViewPoint:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
index: Optional[int] = field(
|
||||
default=None,
|
||||
@@ -234,7 +234,7 @@ class ViewPoint:
|
||||
"name": "Index",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
@@ -274,7 +274,7 @@ class Comment:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
viewpoint: Optional[CommentViewpoint] = field(
|
||||
default=None,
|
||||
@@ -282,7 +282,7 @@ class Comment:
|
||||
"name": "Viewpoint",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
modified_date: Optional[XmlDateTime] = field(
|
||||
default=None,
|
||||
@@ -290,7 +290,7 @@ class Comment:
|
||||
"name": "ModifiedDate",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
modified_author: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -300,7 +300,7 @@ class Comment:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
@@ -323,7 +323,7 @@ class HeaderFiles:
|
||||
"name": "File",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ class TopicDocumentReferences:
|
||||
"name": "DocumentReference",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -353,7 +353,7 @@ class TopicRelatedTopics:
|
||||
"name": "RelatedTopic",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ class TopicViewpoints:
|
||||
"name": "ViewPoint",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -380,7 +380,7 @@ class Header:
|
||||
"name": "Files",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -395,7 +395,7 @@ class TopicComments:
|
||||
"name": "Comment",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@ class Topic:
|
||||
"name": "ReferenceLinks",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
title: str = field(
|
||||
metadata={
|
||||
@@ -427,7 +427,7 @@ class Topic:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
index: Optional[int] = field(
|
||||
default=None,
|
||||
@@ -435,7 +435,7 @@ class Topic:
|
||||
"name": "Index",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
labels: Optional[TopicLabels] = field(
|
||||
default=None,
|
||||
@@ -443,7 +443,7 @@ class Topic:
|
||||
"name": "Labels",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
creation_date: XmlDateTime = field(
|
||||
metadata={
|
||||
@@ -469,7 +469,7 @@ class Topic:
|
||||
"name": "ModifiedDate",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
modified_author: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -479,7 +479,7 @@ class Topic:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
due_date: Optional[XmlDateTime] = field(
|
||||
default=None,
|
||||
@@ -487,7 +487,7 @@ class Topic:
|
||||
"name": "DueDate",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
assigned_to: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -497,7 +497,7 @@ class Topic:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
stage: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -507,7 +507,7 @@ class Topic:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
description: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -517,7 +517,7 @@ class Topic:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
bim_snippet: Optional[BimSnippet] = field(
|
||||
default=None,
|
||||
@@ -525,7 +525,7 @@ class Topic:
|
||||
"name": "BimSnippet",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
document_references: Optional[TopicDocumentReferences] = field(
|
||||
default=None,
|
||||
@@ -533,7 +533,7 @@ class Topic:
|
||||
"name": "DocumentReferences",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
related_topics: Optional[TopicRelatedTopics] = field(
|
||||
default=None,
|
||||
@@ -541,7 +541,7 @@ class Topic:
|
||||
"name": "RelatedTopics",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
comments: Optional[TopicComments] = field(
|
||||
default=None,
|
||||
@@ -549,7 +549,7 @@ class Topic:
|
||||
"name": "Comments",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
viewpoints: Optional[TopicViewpoints] = field(
|
||||
default=None,
|
||||
@@ -557,7 +557,7 @@ class Topic:
|
||||
"name": "Viewpoints",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
@@ -574,7 +574,7 @@ class Topic:
|
||||
"type": "Attribute",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
topic_type: str = field(
|
||||
metadata={
|
||||
@@ -604,7 +604,7 @@ class Markup:
|
||||
"name": "Header",
|
||||
"type": "Element",
|
||||
"namespace": "",
|
||||
}
|
||||
},
|
||||
)
|
||||
topic: Topic = field(
|
||||
metadata={
|
||||
|
||||
@@ -12,7 +12,7 @@ class Project:
|
||||
"namespace": "",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
project_id: str = field(
|
||||
metadata={
|
||||
|
||||
@@ -17,7 +17,7 @@ class Component:
|
||||
"type": "Element",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
authoring_tool_id: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -26,7 +26,7 @@ class Component:
|
||||
"type": "Element",
|
||||
"min_length": 1,
|
||||
"white_space": "collapse",
|
||||
}
|
||||
},
|
||||
)
|
||||
ifc_guid: Optional[str] = field(
|
||||
default=None,
|
||||
@@ -35,7 +35,7 @@ class Component:
|
||||
"type": "Attribute",
|
||||
"length": 22,
|
||||
"pattern": r"[0-9A-Za-z_$]*",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -96,21 +96,21 @@ class ViewSetupHints:
|
||||
metadata={
|
||||
"name": "SpacesVisible",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
space_boundaries_visible: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"name": "SpaceBoundariesVisible",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
openings_visible: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"name": "OpeningsVisible",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ class ComponentColoringColorComponents:
|
||||
"name": "Component",
|
||||
"type": "Element",
|
||||
"min_occurs": 1,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ class ComponentSelection:
|
||||
metadata={
|
||||
"name": "Component",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ class ComponentVisibilityExceptions:
|
||||
metadata={
|
||||
"name": "Component",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -249,6 +249,7 @@ class OrthogonalCamera:
|
||||
aspect_ratio: Proportional relationship between the width and
|
||||
the height of the view (w/h).
|
||||
"""
|
||||
|
||||
camera_view_point: Point = field(
|
||||
metadata={
|
||||
"name": "CameraViewPoint",
|
||||
@@ -302,6 +303,7 @@ class PerspectiveCamera:
|
||||
aspect_ratio: Proportional relationship between the width and
|
||||
the height of the view (w/h).
|
||||
"""
|
||||
|
||||
camera_view_point: Point = field(
|
||||
metadata={
|
||||
"name": "CameraViewPoint",
|
||||
@@ -371,21 +373,21 @@ class ComponentVisibility:
|
||||
metadata={
|
||||
"name": "ViewSetupHints",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
exceptions: Optional[ComponentVisibilityExceptions] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Exceptions",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
default_visibility: bool = field(
|
||||
default=False,
|
||||
metadata={
|
||||
"name": "DefaultVisibility",
|
||||
"type": "Attribute",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -399,7 +401,7 @@ class VisualizationInfoBitmaps:
|
||||
metadata={
|
||||
"name": "Bitmap",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -413,7 +415,7 @@ class VisualizationInfoClippingPlanes:
|
||||
metadata={
|
||||
"name": "ClippingPlane",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -427,7 +429,7 @@ class VisualizationInfoLines:
|
||||
metadata={
|
||||
"name": "Line",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -438,7 +440,7 @@ class ComponentColoring:
|
||||
metadata={
|
||||
"name": "Color",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -449,21 +451,21 @@ class Components:
|
||||
metadata={
|
||||
"name": "Selection",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
visibility: Optional[ComponentVisibility] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Visibility",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
coloring: Optional[ComponentColoring] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Coloring",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -472,47 +474,48 @@ class VisualizationInfo:
|
||||
"""
|
||||
VisualizationInfo documentation.
|
||||
"""
|
||||
|
||||
components: Optional[Components] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Components",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
orthogonal_camera: Optional[OrthogonalCamera] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "OrthogonalCamera",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
perspective_camera: Optional[PerspectiveCamera] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "PerspectiveCamera",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
lines: Optional[VisualizationInfoLines] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Lines",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
clipping_planes: Optional[VisualizationInfoClippingPlanes] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "ClippingPlanes",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
bitmaps: Optional[VisualizationInfoBitmaps] = field(
|
||||
default=None,
|
||||
metadata={
|
||||
"name": "Bitmaps",
|
||||
"type": "Element",
|
||||
}
|
||||
},
|
||||
)
|
||||
guid: str = field(
|
||||
metadata={
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""BCF XML V3 Topic handler."""
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
import zipfile
|
||||
@@ -174,7 +175,7 @@ class TopicHandler:
|
||||
new_viewpoint = VisualizationInfoHandler.create_new(element, self._xml_handler)
|
||||
self.add_visinfo_handler(new_viewpoint)
|
||||
|
||||
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float_], *guids: str) -> None:
|
||||
def add_viewpoint_from_point_and_guids(self, position: NDArray[np.float64], *guids: str) -> None:
|
||||
"""
|
||||
Add a viewpoint tergeting an IFC element to the topic.
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ class VisualizationInfoHandler:
|
||||
@classmethod
|
||||
def create_from_point_and_guids(
|
||||
cls,
|
||||
position: NDArray[np.float_],
|
||||
position: NDArray[np.float64],
|
||||
*guids: str,
|
||||
xml_handler: Optional[AbstractXmlParserSerializer] = None,
|
||||
) -> "VisualizationInfoHandler":
|
||||
@@ -218,7 +218,7 @@ def build_viewpoint(element: entity_instance) -> mdl.VisualizationInfo:
|
||||
)
|
||||
|
||||
|
||||
def build_viewpoint_from_position_and_guids(position: NDArray[np.float_], *guids: str) -> mdl.VisualizationInfo:
|
||||
def build_viewpoint_from_position_and_guids(position: NDArray[np.float64], *guids: str) -> mdl.VisualizationInfo:
|
||||
"""
|
||||
Return a BCF viewpoint of an IFC element.
|
||||
|
||||
@@ -255,7 +255,7 @@ def build_components(*guids: str) -> mdl.Components:
|
||||
)
|
||||
|
||||
|
||||
def build_camera(elem_placement: NDArray[np.float_]) -> mdl.PerspectiveCamera:
|
||||
def build_camera(elem_placement: NDArray[np.float64]) -> mdl.PerspectiveCamera:
|
||||
"""
|
||||
Return a BCF camera for an IFC element placement matrix.
|
||||
|
||||
@@ -269,7 +269,7 @@ def build_camera(elem_placement: NDArray[np.float_]) -> mdl.PerspectiveCamera:
|
||||
|
||||
|
||||
def build_camera_from_vectors(
|
||||
camera_position: NDArray[np.float_], camera_dir: NDArray[np.float_], camera_up: NDArray[np.float_]
|
||||
camera_position: NDArray[np.float64], camera_dir: NDArray[np.float64], camera_up: NDArray[np.float64]
|
||||
) -> mdl.PerspectiveCamera:
|
||||
"""
|
||||
Return a BCF camera for an IFC element placement matrix.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""XML Parser and Serializer factories."""
|
||||
|
||||
from typing import Optional, Protocol, Type, TypeVar
|
||||
|
||||
from xsdata.formats.dataclass.context import XmlContext
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""BCF XML tests."""
|
||||
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""BCF XML tests."""
|
||||
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
@@ -91,12 +91,11 @@ BLENDER_PLATFORM:=windows-x64
|
||||
endif
|
||||
|
||||
# Current build commit hash.
|
||||
OLD:=c18e4ea
|
||||
OLD:=d51fa2c
|
||||
.PHONY: bump
|
||||
bump:
|
||||
cd . && $(SED) -b "s/$(OLD)/$(NEW)/" Makefile
|
||||
cd ../ifcopenshell-python/ && $(SED) -b "s/$(OLD)/$(NEW)/" Makefile
|
||||
cd ../ifcopenshell-python/docs/ifcconvert/ && $(SED) -b "s/$(OLD)/$(NEW)/" installation.rst
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
@@ -196,19 +195,20 @@ endif
|
||||
cd build/blenderbim/bim/data/gantt/ && wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
||||
|
||||
# Provides IFCJSON functionality
|
||||
cd build && wget https://github.com/IFCJSON-Team/IFC2JSON_python/archive/master.zip
|
||||
cd build && unzip master.zip && rm master.zip
|
||||
# TODO: replace with main repo if https://github.com/IFCJSON-Team/IFC2JSON_python/pull/3 is merged.
|
||||
cd build && wget -O ifc2json.zip https://github.com/Moult/IFC2JSON_python/archive/refs/heads/feature-ios-v0.8.0.zip
|
||||
cd build && unzip ifc2json.zip && rm ifc2json.zip
|
||||
# IFCJSON doesn't have pyproject.toml, so we use python command.
|
||||
cd build && . env/$(VENV_ACTIVATE) && cd IFC2JSON_python-master/file_converters && \
|
||||
cd build && . env/$(VENV_ACTIVATE) && cd IFC2JSON_python-*/file_converters && \
|
||||
$(PYTHON) -c "from setuptools import setup; \
|
||||
setup( \
|
||||
name='ifcjson', \
|
||||
version='0.0.0', \
|
||||
version='0.0.1', \
|
||||
author='Jan Brouwer', \
|
||||
author_email='jan@brewsky.nl', \
|
||||
packages=['ifcjson'], \
|
||||
)" bdist_wheel
|
||||
cp -r build/IFC2JSON_python-master/file_converters/dist/*.whl build/wheels/
|
||||
cp -r build/IFC2JSON_python-*/file_converters/dist/*.whl build/wheels/
|
||||
|
||||
# Brickschema requires pkg_resources which is provided by Blender.
|
||||
# Provides Brickschema functionality
|
||||
|
||||
@@ -98,10 +98,10 @@ classes = [
|
||||
operator.AddIfcFile,
|
||||
operator.BIM_OT_add_section_plane,
|
||||
operator.BIM_OT_delete_object,
|
||||
operator.BIM_OT_open_webbrowser,
|
||||
operator.BIM_OT_remove_section_plane,
|
||||
operator.BIM_OT_select_object,
|
||||
operator.BIM_OT_show_description,
|
||||
operator.BIM_OT_multiple_file_selector,
|
||||
operator.ClippingPlaneCutWithCappings,
|
||||
operator.CloseError,
|
||||
operator.EditBlenderCollection,
|
||||
@@ -121,6 +121,7 @@ classes = [
|
||||
prop.StrProperty,
|
||||
operator.BIM_OT_enum_property_search, # /!\ Register AFTER prop.StrProperty
|
||||
prop.ObjProperty,
|
||||
prop.MultipleFileSelect,
|
||||
prop.Attribute,
|
||||
prop.BIMAreaProperties,
|
||||
prop.BIMTabProperties,
|
||||
|
||||
@@ -2,7 +2,9 @@ import sys
|
||||
import os
|
||||
import webbrowser
|
||||
|
||||
blenderbim_lib_path = os.environ.get("blenderbim_lib_path")
|
||||
blenderbim_lib_path = os.environ.get("BLENDERBIM_LIB_PATH")
|
||||
blenderbim_version = os.environ.get("BLENDERBIM_VERSION")
|
||||
|
||||
if blenderbim_lib_path:
|
||||
sys.path.insert(0, blenderbim_lib_path)
|
||||
|
||||
@@ -52,9 +54,16 @@ class WebNamespace(socketio.AsyncNamespace):
|
||||
"web_operator",
|
||||
data,
|
||||
namespace="/blender",
|
||||
room=data["blenderId"],
|
||||
room=data.get("blenderId", None),
|
||||
)
|
||||
|
||||
async def on_get_svg(self, sid, data):
|
||||
print("hello world!")
|
||||
file_path = data["path"]
|
||||
with open(file_path, "r") as file:
|
||||
svg_data = file.read()
|
||||
await sio.emit("svg_data", svg_data, room=sid, namespace="/web")
|
||||
|
||||
async def send_cached_messages(self, sid):
|
||||
# Send cached messages to the connected web client
|
||||
for blenderId, messages in blender_messages.items():
|
||||
@@ -98,6 +107,12 @@ class BlenderNamespace(socketio.AsyncNamespace):
|
||||
blender_messages[sid]["gantt_data"] = data
|
||||
await sio.emit("gantt_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||
|
||||
async def on_drawings_data(self, sid, data):
|
||||
print(f"Drawings directory from Blender client {sid}")
|
||||
print(data)
|
||||
blender_messages[sid]["drwings_data"] = data
|
||||
await sio.emit("drawings_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||
|
||||
|
||||
# Attach namespaces
|
||||
sio.register_namespace(WebNamespace("/web"))
|
||||
@@ -108,26 +123,45 @@ sio.register_namespace(BlenderNamespace("/blender"))
|
||||
async def index(request):
|
||||
with open("templates/index.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port})
|
||||
html_content = pystache.render(template, {"port": sio_port, "version": blenderbim_version})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
async def gantt(request):
|
||||
with open("templates/gantt.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port, "version": blenderbim_version})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
async def drawings(request):
|
||||
with open("templates/drawings.html", "r") as f:
|
||||
template = f.read()
|
||||
html_content = pystache.render(template, {"port": sio_port})
|
||||
return web.Response(text=html_content, content_type="text/html")
|
||||
|
||||
|
||||
async def open_web_browser(app):
|
||||
webbrowser.open(f"http://127.0.0.1:{sio_port}/")
|
||||
async def on_startup(app):
|
||||
pid_file = "running_pid.json"
|
||||
|
||||
if os.path.exists(pid_file):
|
||||
with open(pid_file, "r") as f:
|
||||
pids = json.load(f)
|
||||
else:
|
||||
pids = {}
|
||||
|
||||
pids[str(os.getpid())] = sio_port
|
||||
|
||||
with open(pid_file, "w") as f:
|
||||
json.dump(pids, f, indent=4)
|
||||
|
||||
|
||||
app.router.add_get("/", index)
|
||||
app.router.add_get("/drawings", drawings)
|
||||
app.router.add_get("/gantt", gantt)
|
||||
app.router.add_static("/jsgantt/", path="../gantt", name="jsgantt")
|
||||
app.router.add_static("/static/", path="./static", name="static")
|
||||
app.on_startup.append(open_web_browser)
|
||||
app.on_startup.append(on_startup)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
:root {
|
||||
--font-family: Arial, sans-serif;
|
||||
--base-font-size: 16px;
|
||||
--margin-tiny: 0.125rem;
|
||||
--margin-small: 0.625rem;
|
||||
--margin-medium: 1.25rem;
|
||||
--margin-large: 2.5rem;
|
||||
--padding-tiny: 0.125rem;
|
||||
--padding-small: 0.625rem;
|
||||
--padding-medium: 1rem;
|
||||
--font-size-large: 1.2rem;
|
||||
--logo-height: 2.5rem;
|
||||
--nav-height: 2.5rem;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
color-scheme: dark;
|
||||
--bg-color: #252525;
|
||||
--text-color: #e0e0e0;
|
||||
--nav-bg-color: #121212;
|
||||
--nav-border-color: #25682a;
|
||||
--nav-link-color: #fff;
|
||||
--nav-link-hover-color: #3fb449;
|
||||
--warning-color: #FFDB8F;
|
||||
--border-color: #464444;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
color-scheme: light;
|
||||
--bg-color: #ffffff;
|
||||
--text-color: #000000;
|
||||
--nav-bg-color: #f8f8f8;
|
||||
--nav-border-color: #cccccc;
|
||||
--nav-link-color: #000000;
|
||||
--nav-link-hover-color: #38a63d;
|
||||
--warning-color: #FF4500;
|
||||
--border-color: #222;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: var(--base-font-size);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-top: var(--margin-medium);
|
||||
margin-left: var(--margin-small);
|
||||
margin-right: var(--margin-small);
|
||||
margin-bottom: var(--margin-medium);
|
||||
height: calc(100vh - var(--nav-height));
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: var(--nav-bg-color);
|
||||
height: var(--nav-height);
|
||||
padding: var(--padding-medium) 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
border-bottom: 2px solid var(--nav-border-color);
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
margin-left: var(--margin-large);
|
||||
height: var(--logo-height);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
margin-left: 0.0625rem;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
margin-right: 3.125rem;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: var(--nav-link-color);
|
||||
font-size: var(--font-size-large);
|
||||
}
|
||||
|
||||
nav ul li a:hover,
|
||||
nav ul li a.active {
|
||||
color: var(--nav-link-hover-color);
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: var(--warning-color);
|
||||
margin-bottom: var(--margin-small);
|
||||
padding: var(--padding-tiny);
|
||||
display: none;
|
||||
}
|
||||
|
||||
#toggle-theme {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: var(--font-size-large);
|
||||
margin-right: var(--margin-medium);
|
||||
}
|
||||
|
||||
#toggle-theme:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#client-list {
|
||||
position: absolute;
|
||||
top: calc(0 + var(--nav-height));
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||
border: 1px solid var(--table-border-color);
|
||||
background-color: var(--nav-bg-color);
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#client-list.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#connected-list-div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.client {
|
||||
padding: var(--margin-small);
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.client-details {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
padding-left: var(--padding-small);
|
||||
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||
background-color: var(--nav-bg-color);
|
||||
margin-top: var(--margin-tiny);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.client-details.show {
|
||||
max-height: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.client-detail {
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
#show-connected-button {
|
||||
width: auto;
|
||||
background-color: var(--bg-color);
|
||||
border: none;
|
||||
font-size: var(--base-font-size);
|
||||
}
|
||||
|
||||
#show-connected-button:hover,
|
||||
.scroll-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.scroll-button {
|
||||
font-size: var(--base-font-size);
|
||||
margin-left: var(--margin-tiny);
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
background-color: var(--bg-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 4;
|
||||
background-color: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
#svg-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background-color: var(--nav-bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 10px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-header,
|
||||
.card-body {
|
||||
background-color: var(--bg-color);
|
||||
border: 1px solid var(--border-color);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--nav-bg-color);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
li.svg-name {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#dropdown-menu:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* remove bootstrap css confilcts */
|
||||
*,
|
||||
::after,
|
||||
::before {
|
||||
box-sizing: unset;
|
||||
}
|
||||
|
||||
.btn,
|
||||
.btn:hover {
|
||||
color: var(--nav-link-hover-color);
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
:root {
|
||||
--font-family: Arial, sans-serif;
|
||||
--base-font-size: 16px;
|
||||
--margin-tiny: 0.125rem;
|
||||
--margin-small: 0.625rem;
|
||||
--margin-medium: 1.25rem;
|
||||
--margin-large: 2.5rem;
|
||||
--padding-small: 0.125rem;
|
||||
--padding-tiny: 0.125rem;
|
||||
--padding-small: 0.625rem;
|
||||
--padding-medium: 1rem;
|
||||
--font-size-small: 1rem;
|
||||
--font-size-large: 1.2rem;
|
||||
--logo-height: 2.5rem;
|
||||
--nav-height: 2.5rem;
|
||||
--folder-collapse-font-size: 0.75rem;
|
||||
--folder-collapse-font-family: Courier, "Courier New", monospace;
|
||||
--box-shadow: 0 0 0.7rem #5f5f5f66;
|
||||
@@ -31,6 +35,7 @@
|
||||
--group-item-bg-color: #4b4b4b;
|
||||
--input-bg-color: #3b3b3b;
|
||||
--input-border-color: #000;
|
||||
--details-border-color: #464444;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
@@ -42,6 +47,7 @@
|
||||
--nav-link-color: #000000;
|
||||
--nav-link-hover-color: #38a63d;
|
||||
--warning-color: #FF4500;
|
||||
--details-border-color: #222;
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -53,9 +59,13 @@ body {
|
||||
color: var(--primary-text-color);
|
||||
margin: 0;
|
||||
font-family: var(--font-family);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#container {
|
||||
flex: 1;
|
||||
margin-top: var(--margin-medium);
|
||||
margin-left: var(--margin-small);
|
||||
margin-right: var(--margin-small);
|
||||
@@ -107,7 +117,7 @@ nav ul li a.active {
|
||||
.warning {
|
||||
color: var(--warning-color);
|
||||
margin-bottom: var(--margin-small);
|
||||
padding: var(--padding-small);
|
||||
padding: var(--padding-tiny);
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -141,6 +151,87 @@ nav ul li a.active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#client-list {
|
||||
position: absolute;
|
||||
top: calc(0 + var(--nav-height));
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||
border: 1px solid var(--table-border-color);
|
||||
background-color: var(--nav-bg-color);
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#client-list.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#connected-list-div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.client {
|
||||
padding: var(--margin-small);
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.client-details {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
padding-left: var(--padding-small);
|
||||
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||
background-color: var(--nav-bg-color);
|
||||
margin-top: var(--margin-tiny);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.client-details.show {
|
||||
max-height: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.client-detail {
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
#show-connected-button {
|
||||
width: auto;
|
||||
background-color: var(--bg-color);
|
||||
border: none;
|
||||
font-size: var(--base-font-size);
|
||||
}
|
||||
|
||||
#show-connected-button:hover,
|
||||
.scroll-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.scroll-button {
|
||||
font-size: var(--base-font-size);
|
||||
margin-left: var(--margin-tiny);
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--nav-bg-color);
|
||||
text-align: right;
|
||||
padding: var(--padding-tiny);
|
||||
border-top: 1px solid var(--nav-border-color);
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
color: var(--text-color);
|
||||
margin-right: var(--margin-small);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.gantt-info {
|
||||
margin: 0.5rem;
|
||||
font-size: var(--font-size-small);
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
:root {
|
||||
--font-family: Arial, sans-serif;
|
||||
--base-font-size: 16px;
|
||||
--margin-tiny: 0.125rem;
|
||||
--margin-small: 0.625rem;
|
||||
--margin-medium: 1.25rem;
|
||||
--margin-large: 2.5rem;
|
||||
--padding-small: 0.125rem;
|
||||
--padding-tiny: 0.125rem;
|
||||
--padding-small: 0.625rem;
|
||||
--padding-medium: 1rem;
|
||||
--font-size-large: 1.2rem;
|
||||
--logo-height: 2.5rem;
|
||||
--nav-height: 2.5rem;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
@@ -18,6 +22,7 @@
|
||||
--nav-link-color: #fff;
|
||||
--nav-link-hover-color: #3fb449;
|
||||
--warning-color: #FFDB8F;
|
||||
--table-border-color: #464444;
|
||||
}
|
||||
|
||||
:root.light {
|
||||
@@ -29,6 +34,7 @@
|
||||
--nav-link-color: #000000;
|
||||
--nav-link-hover-color: #38a63d;
|
||||
--warning-color: #FF4500;
|
||||
--table-border-color: #222;
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -40,29 +46,26 @@ body {
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
font-family: var(--font-family);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#container {
|
||||
flex: 1;
|
||||
margin-top: var(--margin-medium);
|
||||
margin-left: var(--margin-small);
|
||||
margin-right: var(--margin-small);
|
||||
margin-bottom: var(--margin-medium);
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-bottom: var(--margin-large);
|
||||
}
|
||||
|
||||
.csv-table {
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: var(--nav-bg-color);
|
||||
height: var(--nav-height);
|
||||
padding: var(--padding-medium) 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -102,7 +105,7 @@ nav ul li a.active {
|
||||
.warning {
|
||||
color: var(--warning-color);
|
||||
margin-bottom: var(--margin-small);
|
||||
padding: var(--padding-small);
|
||||
padding: var(--padding-tiny);
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -118,6 +121,95 @@ nav ul li a.active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#client-list {
|
||||
position: absolute;
|
||||
top: calc(0 + var(--nav-height));
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
|
||||
border: 1px solid var(--table-border-color);
|
||||
background-color: var(--nav-bg-color);
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#client-list.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#connected-list-div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.client {
|
||||
padding: var(--margin-small);
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.client-details {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
padding-left: var(--padding-small);
|
||||
transition: max-height 0.2s ease-out, padding 0.2s ease-out, opacity 0.1s ease-out;
|
||||
background-color: var(--nav-bg-color);
|
||||
margin-top: var(--margin-tiny);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.client-details.show {
|
||||
max-height: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.client-detail {
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
padding: var(--padding-small);
|
||||
}
|
||||
|
||||
#show-connected-button {
|
||||
width: auto;
|
||||
background-color: var(--bg-color);
|
||||
border: none;
|
||||
font-size: var(--base-font-size);
|
||||
}
|
||||
|
||||
#show-connected-button:hover,
|
||||
.scroll-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.scroll-button {
|
||||
font-size: var(--base-font-size);
|
||||
margin-left: var(--margin-tiny);
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--nav-bg-color);
|
||||
text-align: right;
|
||||
padding: var(--padding-tiny);
|
||||
border-top: 1px solid var(--nav-border-color);
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
color: var(--text-color);
|
||||
margin-right: var(--margin-small);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-bottom: var(--margin-large);
|
||||
}
|
||||
|
||||
.csv-table {
|
||||
margin-top: var(--margin-small);
|
||||
}
|
||||
|
||||
/* ------------ Overwriting Tabulator CSS rules ------------ */
|
||||
|
||||
:root.light .tabulator-header,
|
||||
@@ -127,6 +219,6 @@ nav ul li a.active {
|
||||
}
|
||||
|
||||
:root.light .tabulator {
|
||||
border-top: 1px solid #222;
|
||||
border-bottom: 2px solid #222;
|
||||
border-top: 1px solid var(--table-border-color);
|
||||
border-bottom: 2px solid var(--table-border-color);
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
// keeps track of blenders connected
|
||||
const connectedClients = {};
|
||||
const allDrawings = {};
|
||||
let socket;
|
||||
|
||||
// Document ready function
|
||||
$(document).ready(function () {
|
||||
var systemTheme = window.matchMedia("(prefers-color-scheme: light)").matches
|
||||
? "light"
|
||||
: "dark";
|
||||
var theme = localStorage.getItem("theme") || systemTheme;
|
||||
setTheme(theme);
|
||||
|
||||
$("#dropdown-menu").change(function () {
|
||||
const blenderId = $(this).attr("id");
|
||||
const ifcFile = $(this).val();
|
||||
displayDrawingsNames(blenderId, ifcFile);
|
||||
});
|
||||
|
||||
connectSocket();
|
||||
});
|
||||
|
||||
// Function to connect to Socket.IO server
|
||||
function connectSocket() {
|
||||
const url = "ws://localhost:" + SOCKET_PORT + "/web";
|
||||
socket = io(url);
|
||||
console.log("socket: ", socket);
|
||||
|
||||
// Register socket event handlers
|
||||
socket.on("blender_connect", handleBlenderConnect);
|
||||
socket.on("blender_disconnect", handleBlenderDisconnect);
|
||||
socket.on("connect", handleWebConnect);
|
||||
socket.on("drawings_data", handleDrawingsData);
|
||||
socket.on("svg_data", handleSvgData);
|
||||
// socket.on("default_data", handleDefaultData);
|
||||
}
|
||||
|
||||
// function used to get drawings data from blenderbim
|
||||
function handleWebConnect() {
|
||||
const msg = {
|
||||
sourcePage: "drawings",
|
||||
operator: {
|
||||
type: "getDrawings",
|
||||
},
|
||||
};
|
||||
socket.emit("web_operator", msg);
|
||||
}
|
||||
|
||||
// Function to handle 'blender_connect' event
|
||||
function handleBlenderConnect(blenderId) {
|
||||
console.log("blender_connect: ", blenderId);
|
||||
if (!connectedClients.hasOwnProperty(blenderId)) {
|
||||
connectedClients[blenderId] = {
|
||||
shown: false,
|
||||
ifc_file: "",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle 'blender_disconnect' event
|
||||
function handleBlenderDisconnect(blenderId) {
|
||||
console.log("blender_disconnect: ", blenderId);
|
||||
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||
delete connectedClients[blenderId];
|
||||
// remove(blenderId);
|
||||
}
|
||||
|
||||
$("#blender-count").text(function (i, text) {
|
||||
return parseInt(text, 10) - 1;
|
||||
});
|
||||
}
|
||||
|
||||
function handleDrawingsData(data) {
|
||||
const blenderId = data["blenderId"];
|
||||
|
||||
console.log(data);
|
||||
|
||||
const filename = data["data"]["ifc_file"];
|
||||
const drawings = data["data"]["drawings_data"]["drawings"];
|
||||
const sheets = data["data"]["drawings_data"]["sheets"];
|
||||
|
||||
allDrawings[filename] = { drawings: drawings, sheets: sheets };
|
||||
|
||||
console.log(connectedClients);
|
||||
|
||||
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||
if (!connectedClients[blenderId].shown) {
|
||||
connectedClients[blenderId] = {
|
||||
shown: true,
|
||||
ifc_file: filename,
|
||||
};
|
||||
addSelectOption(blenderId, filename);
|
||||
} else {
|
||||
// update(blenderId, data, filename);
|
||||
}
|
||||
} else {
|
||||
connectedClients[blenderId] = {
|
||||
shown: true,
|
||||
ifc_file: filename,
|
||||
};
|
||||
addSelectOption(blenderId, filename);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSvgData(data) {
|
||||
const svgContainer = document.getElementById("svg-container");
|
||||
svgContainer.innerHTML = "";
|
||||
svgContainer.style.backgroundColor = "white";
|
||||
|
||||
const dom = SVG(svgContainer).svg(data);
|
||||
const svgElement = dom.node.children[0];
|
||||
|
||||
// to make the svg not grow outside the svg container
|
||||
svgElement.setAttribute("width", "100%");
|
||||
svgElement.setAttribute("height", "100%");
|
||||
svgElement.style.maxWidth = "100%";
|
||||
svgElement.style.maxHeight = "100%";
|
||||
svgElement.style.width = "100%";
|
||||
svgElement.style.height = "100%";
|
||||
svgElement.style.boxSizing = "border-box"; // Ensure padding and borders are included in the element's total width and height
|
||||
|
||||
const panZoomControls = svgPanZoom(svgElement, {
|
||||
zoomEnabled: true,
|
||||
controlIconsEnabled: true,
|
||||
fit: true,
|
||||
center: true,
|
||||
});
|
||||
}
|
||||
|
||||
function addSelectOption(blenderId, filename) {
|
||||
const filenameOption = $("<option></option>")
|
||||
.attr("id", "blender-" + blenderId)
|
||||
.val(filename)
|
||||
.text(filename);
|
||||
$("#dropdown-menu").append(filenameOption);
|
||||
}
|
||||
|
||||
function displayDrawingsNames(blenderId, ifcFile) {
|
||||
drawings = allDrawings[ifcFile].drawings;
|
||||
sheets = allDrawings[ifcFile].sheets;
|
||||
|
||||
function createSvgNames(text, index, type) {
|
||||
var label = $("<li></lio>")
|
||||
.text(text)
|
||||
.attr("id", type + "-" + index)
|
||||
.addClass("svg-name")
|
||||
.click(function () {
|
||||
const id = $(this).attr("id").split("-");
|
||||
const index = parseInt(id[1]);
|
||||
const type = id[0];
|
||||
const ifcFile = $("#dropdown-menu").val();
|
||||
|
||||
var path = "";
|
||||
|
||||
if (type === "drawing") {
|
||||
path = allDrawings[ifcFile].drawings[index].path;
|
||||
} else if (type === "sheet") {
|
||||
path = allDrawings[ifcFile].sheets[index].path;
|
||||
}
|
||||
|
||||
const msg = {
|
||||
path: path,
|
||||
};
|
||||
console.log(msg);
|
||||
socket.emit("get_svg", msg);
|
||||
});
|
||||
|
||||
if (type === "drawing") $("#drawings-sub-panel").append(label);
|
||||
else if (type === "sheet") $("#sheets-sub-panel").append(label);
|
||||
}
|
||||
|
||||
$("#drawings-sub-panel").empty();
|
||||
drawings.forEach(function (drawing, index) {
|
||||
createSvgNames(drawing.name, index, "drawing");
|
||||
});
|
||||
|
||||
$("#sheets-sub-panel").empty();
|
||||
sheets.forEach(function (sheet, index) {
|
||||
createSvgNames(sheet.name, index, "sheet");
|
||||
});
|
||||
}
|
||||
|
||||
function setTheme(theme) {
|
||||
if (theme === "light") {
|
||||
$("html").removeClass("dark").addClass("light");
|
||||
$("#toggle-theme").html('<i class="fas fa-sun"></i>');
|
||||
} else {
|
||||
$("html").removeClass("light").addClass("dark");
|
||||
$("#toggle-theme").html('<i class="fas fa-moon"></i>');
|
||||
}
|
||||
localStorage.setItem("theme", theme);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
if ($("html").hasClass("dark")) {
|
||||
setTheme("light");
|
||||
} else {
|
||||
setTheme("dark");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleClientList() {
|
||||
var clientList = $("#client-list");
|
||||
|
||||
if (clientList.hasClass("show")) {
|
||||
clientList.removeClass("show");
|
||||
return;
|
||||
}
|
||||
|
||||
clientList.empty();
|
||||
|
||||
$.each(connectedClients, function (id, client) {
|
||||
if (!client.shown) return;
|
||||
|
||||
const dropdownIcon = $("<i>")
|
||||
.addClass("fas fa-chevron-down")
|
||||
.css("margin-left", "0.5rem");
|
||||
|
||||
const clientDiv = $("<div>").addClass("client").text(client.ifc_file);
|
||||
|
||||
clientDiv.append(dropdownIcon);
|
||||
|
||||
const clientDetailsDiv = $("<div>").addClass("client-details");
|
||||
|
||||
if (id) {
|
||||
const clientId = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(`Blender ID: ${id}`);
|
||||
clientDetailsDiv.append(clientId);
|
||||
}
|
||||
|
||||
if (client.headers && client.headers.length) {
|
||||
const clientHeaders = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(`Table Headers: ${client.headers.join(", ")}`);
|
||||
|
||||
const scrollButton = $("<button>")
|
||||
.addClass("scroll-button")
|
||||
.text("Scroll to Table")
|
||||
.on("click", function () {
|
||||
$("html, body").animate(
|
||||
{
|
||||
scrollTop: $("#table-" + id).offset().top,
|
||||
},
|
||||
600
|
||||
);
|
||||
clientList.removeClass("show");
|
||||
});
|
||||
|
||||
clientDetailsDiv.append(clientHeaders);
|
||||
clientDetailsDiv.append(scrollButton);
|
||||
}
|
||||
|
||||
clientDiv.append(clientDetailsDiv);
|
||||
|
||||
clientDiv.on("click", function () {
|
||||
clientDetailsDiv.toggleClass("show");
|
||||
});
|
||||
|
||||
clientList.append(clientDiv);
|
||||
});
|
||||
clientList.addClass("show");
|
||||
}
|
||||
@@ -60,6 +60,10 @@ function handleBlenderDisconnect(blenderId) {
|
||||
delete connectedClients[blenderId];
|
||||
removeGanttElement(blenderId);
|
||||
}
|
||||
|
||||
$("#blender-count").text(function (i, text) {
|
||||
return parseInt(text, 10) - 1;
|
||||
});
|
||||
}
|
||||
|
||||
// Function to handle 'gantt_data' event
|
||||
@@ -77,6 +81,7 @@ function handleGanttData(data) {
|
||||
if (!connectedClients[blenderId].shown) {
|
||||
connectedClients[blenderId] = {
|
||||
shown: true,
|
||||
ifc_file: filename,
|
||||
ganttTasks: ganttTasks,
|
||||
workSchedule: ganttWorkSched,
|
||||
};
|
||||
@@ -89,6 +94,7 @@ function handleGanttData(data) {
|
||||
} else {
|
||||
connectedClients[blenderId] = {
|
||||
shown: true,
|
||||
ifc_file: filename,
|
||||
ganttTasks: ganttTasks,
|
||||
workSchedule: ganttWorkSched,
|
||||
};
|
||||
@@ -105,6 +111,10 @@ function handleDefaultData(data) {
|
||||
|
||||
// Function to add a new gantt with data and filename
|
||||
function addGanttElement(blenderId, tasks, workSched, filename) {
|
||||
$("#blender-count").text(function (i, text) {
|
||||
return parseInt(text, 10) + 1;
|
||||
});
|
||||
|
||||
const ganttContainer = $("<div></div>")
|
||||
.addClass("gantt-container")
|
||||
.attr("id", "container-" + blenderId);
|
||||
@@ -382,3 +392,75 @@ function toggleTheme() {
|
||||
setTheme("dark");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleClientList() {
|
||||
var clientList = $("#client-list");
|
||||
|
||||
if (clientList.hasClass("show")) {
|
||||
clientList.removeClass("show");
|
||||
return;
|
||||
}
|
||||
|
||||
clientList.empty();
|
||||
|
||||
$.each(connectedClients, function (id, client) {
|
||||
if (!client.shown) return;
|
||||
|
||||
const dropdownIcon = $("<i>")
|
||||
.addClass("fas fa-chevron-down")
|
||||
.css("margin-left", "0.5rem");
|
||||
|
||||
const clientDiv = $("<div>").addClass("client").text(client.ifc_file);
|
||||
|
||||
clientDiv.append(dropdownIcon);
|
||||
|
||||
const clientDetailsDiv = $("<div>").addClass("client-details");
|
||||
|
||||
if (id) {
|
||||
const clientId = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(`Blender ID: ${id}`);
|
||||
clientDetailsDiv.append(clientId);
|
||||
}
|
||||
|
||||
if (client.workSchedule && client.gantt) {
|
||||
const clientScheduleName = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(`Schedule Name: ${client.workSchedule.Name}`);
|
||||
|
||||
const clientScheduleDate = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(
|
||||
`Schedule Date: ${new Date(
|
||||
client.workSchedule.CreationDate
|
||||
).toLocaleDateString()}`
|
||||
);
|
||||
|
||||
const scrollButton = $("<button>")
|
||||
.addClass("scroll-button")
|
||||
.text("Scroll to Gantt Chart")
|
||||
.on("click", function () {
|
||||
$("html, body").animate(
|
||||
{
|
||||
scrollTop: $("#gantt-" + id).offset().top,
|
||||
},
|
||||
600
|
||||
);
|
||||
clientList.removeClass("show");
|
||||
});
|
||||
|
||||
clientDetailsDiv.append(clientScheduleName);
|
||||
clientDetailsDiv.append(clientScheduleDate);
|
||||
clientDetailsDiv.append(scrollButton);
|
||||
}
|
||||
|
||||
clientDiv.append(clientDetailsDiv);
|
||||
|
||||
clientDiv.on("click", function () {
|
||||
clientDetailsDiv.toggleClass("show");
|
||||
});
|
||||
|
||||
clientList.append(clientDiv);
|
||||
});
|
||||
clientList.addClass("show");
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ function handleBlenderDisconnect(blenderId) {
|
||||
delete connectedClients[blenderId];
|
||||
removeTableElement(blenderId);
|
||||
}
|
||||
|
||||
$("#blender-count").text(function (i, text) {
|
||||
return parseInt(text, 10) - 1;
|
||||
});
|
||||
}
|
||||
|
||||
// Function to handle 'csv_data' event
|
||||
@@ -81,6 +85,10 @@ function handleDefaultData(data) {
|
||||
|
||||
// Function to add a new table with data and filename
|
||||
function addTableElement(blenderId, csvData, filename) {
|
||||
$("#blender-count").text(function (i, text) {
|
||||
return parseInt(text, 10) + 1;
|
||||
});
|
||||
|
||||
// store headers of the csv data
|
||||
const firstLine = csvData.indexOf("\n");
|
||||
const csvHeaders = csvData.substring(0, firstLine).split(",");
|
||||
@@ -274,3 +282,66 @@ function toggleTheme() {
|
||||
setTheme("dark");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleClientList() {
|
||||
var clientList = $("#client-list");
|
||||
|
||||
if (clientList.hasClass("show")) {
|
||||
clientList.removeClass("show");
|
||||
return;
|
||||
}
|
||||
|
||||
clientList.empty();
|
||||
|
||||
$.each(connectedClients, function (id, client) {
|
||||
if (!client.shown) return;
|
||||
|
||||
const dropdownIcon = $("<i>")
|
||||
.addClass("fas fa-chevron-down")
|
||||
.css("margin-left", "0.5rem");
|
||||
|
||||
const clientDiv = $("<div>").addClass("client").text(client.ifc_file);
|
||||
|
||||
clientDiv.append(dropdownIcon);
|
||||
|
||||
const clientDetailsDiv = $("<div>").addClass("client-details");
|
||||
|
||||
if (id) {
|
||||
const clientId = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(`Blender ID: ${id}`);
|
||||
clientDetailsDiv.append(clientId);
|
||||
}
|
||||
|
||||
if (client.headers && client.headers.length) {
|
||||
const clientHeaders = $("<div>")
|
||||
.addClass("client-detail")
|
||||
.text(`Table Headers: ${client.headers.join(", ")}`);
|
||||
|
||||
const scrollButton = $("<button>")
|
||||
.addClass("scroll-button")
|
||||
.text("Scroll to Table")
|
||||
.on("click", function () {
|
||||
$("html, body").animate(
|
||||
{
|
||||
scrollTop: $("#table-" + id).offset().top,
|
||||
},
|
||||
600
|
||||
);
|
||||
clientList.removeClass("show");
|
||||
});
|
||||
|
||||
clientDetailsDiv.append(clientHeaders);
|
||||
clientDetailsDiv.append(scrollButton);
|
||||
}
|
||||
|
||||
clientDiv.append(clientDetailsDiv);
|
||||
|
||||
clientDiv.on("click", function () {
|
||||
clientDetailsDiv.toggleClass("show");
|
||||
});
|
||||
|
||||
clientList.append(clientDiv);
|
||||
});
|
||||
clientList.addClass("show");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Web Client</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="/static/css/drawings.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"
|
||||
/>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/3.1.1/svg.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.6.1/dist/svg-pan-zoom.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://cdn.socket.io/4.0.0/socket.io.min.js"
|
||||
></script>
|
||||
<script>
|
||||
var SOCKET_PORT = {{port}};
|
||||
</script>
|
||||
<script defer src="./static/js/drawings.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<img
|
||||
src="https://blenderbim.org/assets/images/blender/blender-logo.png"
|
||||
alt="Logo"
|
||||
class="logo"
|
||||
/>
|
||||
<ul>
|
||||
<li><a href="/">IFC Data</a></li>
|
||||
<li><a href="/gantt">Gantt Chart</a></li>
|
||||
<li><a href="/drawings" class="active">Drawings</a></li>
|
||||
</ul>
|
||||
<button id="toggle-theme" onclick="toggleTheme()">
|
||||
<i class="fas fa-moon"></i>
|
||||
</button>
|
||||
</nav>
|
||||
<div id="connected-list-div">
|
||||
<button id="show-connected-button" onclick="toggleClientList()">
|
||||
Connected Blenders:
|
||||
<span id="blender-count">0</span>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
<div id="client-list"></div>
|
||||
</div>
|
||||
<div class="container-fluid d-flex" id="container">
|
||||
<div class="col-2 left">
|
||||
<!-- Dropdown menu -->
|
||||
<div class="dropdown mb-3">
|
||||
<label for="dropdown-menu">IFC File:</label>
|
||||
<select class="p-0 pl-2" id="dropdown-menu">
|
||||
<!-- IFC files will be dynamically populated here -->
|
||||
<option value="" selected disabled hidden>IFC File</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Panel with sub-panels -->
|
||||
<div class="panel">
|
||||
<div id="accordion">
|
||||
<div class="card pb-2">
|
||||
<div class="card-header overflow-hidden" id="headingOne">
|
||||
<button
|
||||
class="btn p-0 w-100 text-left"
|
||||
data-toggle="collapse"
|
||||
data-target="#collapseOne"
|
||||
>
|
||||
Drawings
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="collapseOne" class="collapse show">
|
||||
<div class="card-body">
|
||||
<ul class="sub-panel pl-2" id="drawings-sub-panel">
|
||||
<!-- Drawings will be dynamically populated here -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card pb-2">
|
||||
<div class="card-header overflow-hidden" id="headingTwo">
|
||||
<button
|
||||
class="btn p-0 w-100 text-left"
|
||||
data-toggle="collapse"
|
||||
data-target="#collapseTwo"
|
||||
>
|
||||
Sheets
|
||||
</button>
|
||||
</div>
|
||||
<div id="collapseTwo" class="collapse show">
|
||||
<div class="card-body">
|
||||
<ul class="sub-panel pl-2" id="sheets-sub-panel">
|
||||
<!-- Sheets will be dynamically populated here -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-10 right pl-3">
|
||||
<div id="svg-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -34,11 +34,23 @@
|
||||
<ul>
|
||||
<li><a href="/">IFC Data</a></li>
|
||||
<li><a href="/gantt" class="active">Gantt Chart</a></li>
|
||||
<li><a href="/drawings">Drawings</a></li>
|
||||
</ul>
|
||||
<button id="toggle-theme" onclick="toggleTheme()">
|
||||
<i class="fas fa-moon"></i>
|
||||
</button>
|
||||
</nav>
|
||||
<div id="connected-list-div">
|
||||
<button id="show-connected-button" onclick="toggleClientList()">
|
||||
Connected Blenders:
|
||||
<span id="blender-count">0</span>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
<div id="client-list"></div>
|
||||
</div>
|
||||
<div id="container"></div>
|
||||
<footer>
|
||||
<p>BlenderBIM Version: {{version}}</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -41,11 +41,23 @@
|
||||
<ul>
|
||||
<li><a href="/" class="active">IFC Data</a></li>
|
||||
<li><a href="/gantt">Gantt Chart</a></li>
|
||||
<li><a href="/drawings">Drawings</a></li>
|
||||
</ul>
|
||||
<button id="toggle-theme" onclick="toggleTheme()">
|
||||
<i class="fas fa-moon"></i>
|
||||
</button>
|
||||
</nav>
|
||||
<div id="connected-list-div">
|
||||
<button id="show-connected-button" onclick="toggleClientList()">
|
||||
Connected Blenders:
|
||||
<span id="blender-count">0</span>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
<div id="client-list"></div>
|
||||
</div>
|
||||
<div id="container"></div>
|
||||
<footer>
|
||||
<p>BlenderBIM Version: {{version}}</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -170,6 +170,11 @@ def subscribe_to(obj, data_path, callback):
|
||||
|
||||
|
||||
def refresh_ui_data():
|
||||
"""Refresh cached UI data.
|
||||
|
||||
Note that calling non-ifc-operators by itself doesn't refresh the UI data
|
||||
and it need to be refreshed manually if needed.
|
||||
"""
|
||||
from blenderbim.bim import modules
|
||||
|
||||
for name, value in modules.items():
|
||||
|
||||
@@ -50,6 +50,8 @@ def draw_attribute(attribute, layout, copy_operator=None):
|
||||
return
|
||||
if value_name == "enum_value":
|
||||
prop_with_search(layout, attribute, "enum_value", text=attribute.name)
|
||||
elif value_name == "filepath_value":
|
||||
attribute.filepath_value.layout_file_select(layout, filter_glob=attribute.filter_glob, text=attribute.name)
|
||||
elif attribute.name in ["ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"]:
|
||||
propis = bpy.context.scene.BIMWorkScheduleProperties
|
||||
for item in propis.durations_attributes:
|
||||
|
||||
@@ -551,7 +551,7 @@ class IfcImporter:
|
||||
for axis in axes:
|
||||
shape = tool.Loader.create_generic_shape(axis.AxisCurve)
|
||||
mesh = self.create_mesh(axis, shape)
|
||||
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
||||
obj = bpy.data.objects.new(tool.Loader.get_name(axis), mesh)
|
||||
if tool.Blender.get_addon_preferences().lock_grids_on_import:
|
||||
obj.lock_location = (True, True, True)
|
||||
obj.lock_rotation = (True, True, True)
|
||||
@@ -781,7 +781,7 @@ class IfcImporter:
|
||||
mesh = bpy.data.meshes.new(mesh_name)
|
||||
mesh.from_pydata([mathutils.Vector(vertex) * self.unit_scale], [], [])
|
||||
|
||||
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
|
||||
obj = bpy.data.objects.new(tool.Loader.get_name(product), mesh)
|
||||
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, placement_matrix))
|
||||
self.link_element(product, obj)
|
||||
|
||||
@@ -847,7 +847,7 @@ class IfcImporter:
|
||||
mesh.from_pydata(vertex_list, [], [])
|
||||
tool.Ifc.link(representation, mesh)
|
||||
|
||||
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
|
||||
obj = bpy.data.objects.new(tool.Loader.get_name(product), mesh)
|
||||
self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, placement_matrix))
|
||||
self.link_element(product, obj)
|
||||
return product
|
||||
|
||||
@@ -75,7 +75,7 @@ class AggregateData:
|
||||
return []
|
||||
if not element:
|
||||
return []
|
||||
|
||||
|
||||
product_linked_agg_group = [
|
||||
r
|
||||
for r in getattr(aggregate, "HasAssignments", []) or []
|
||||
@@ -85,7 +85,6 @@ class AggregateData:
|
||||
|
||||
if not product_linked_agg_group:
|
||||
return []
|
||||
|
||||
|
||||
total = len(product_linked_agg_group[0].RelatedObjects)
|
||||
return total
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ class BIM_OT_add_aggregate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Spatial,
|
||||
structure_obj=tool.Ifc.get_object(current_container),
|
||||
container=current_container,
|
||||
element_obj=aggregate,
|
||||
)
|
||||
core.assign_object(tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=aggregate, related_obj=obj)
|
||||
|
||||
@@ -99,7 +99,7 @@ class BIM_PT_aggregate(Panel):
|
||||
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.obj = context.active_object.name
|
||||
|
||||
|
||||
|
||||
class BIM_PT_linked_aggregate(Panel):
|
||||
bl_label = "Linked Aggregates"
|
||||
bl_idname = "BIM_PT_linked_aggregate"
|
||||
@@ -133,9 +133,8 @@ class BIM_PT_linked_aggregate(Panel):
|
||||
props = obj.BIMObjectAggregateProperties
|
||||
row = layout.row(align=True)
|
||||
|
||||
|
||||
if element.Decomposes:
|
||||
Number_Linked_Aggregates = AggregateData.data['total_linked_aggregate']
|
||||
Number_Linked_Aggregates = AggregateData.data["total_linked_aggregate"]
|
||||
if not Number_Linked_Aggregates:
|
||||
row.label(text="Not a Linked Aggregate")
|
||||
else:
|
||||
@@ -151,6 +150,3 @@ class BIM_PT_linked_aggregate(Panel):
|
||||
op = row.operator("bim.break_link_to_other_aggregates", text="", icon="X")
|
||||
else:
|
||||
row.label(text="Not an Aggregate")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
@@ -36,7 +37,7 @@ class EnableEditingAttributes(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
obj = bpy.data.objects.get(self.obj)
|
||||
obj = bpy.data.objects[self.obj]
|
||||
props = obj.BIMAttributeProperties
|
||||
props.attributes.clear()
|
||||
|
||||
|
||||
@@ -45,8 +45,7 @@ def draw_ui(context, layout, attributes):
|
||||
row.label(text=attribute["name"])
|
||||
# row.label(text=attribute["value"])
|
||||
op = row.operator("bim.select_similar", text=attribute["value"], icon="NONE", emboss=False)
|
||||
op.key = attribute['name']
|
||||
|
||||
op.key = attribute["name"]
|
||||
|
||||
# TODO: reimplement, see #1222
|
||||
# if "IfcSite/" in context.active_object.name or "IfcBuilding/" in context.active_object.name:
|
||||
|
||||
@@ -66,10 +66,7 @@ class LoadBcfProject(bpy.types.Operator):
|
||||
if bcfxml.version.version_id.startswith("2"):
|
||||
print("No project, we will create one for BBIM.")
|
||||
bcfxml.project_info = bcf.v2.model.ProjectExtension(
|
||||
project=bcf.v2.model.Project(
|
||||
name=nameless,
|
||||
project_id=str(uuid.uuid4())
|
||||
), extension_schema=""
|
||||
project=bcf.v2.model.Project(name=nameless, project_id=str(uuid.uuid4())), extension_schema=""
|
||||
)
|
||||
if bcfxml.project.name is None:
|
||||
bcfxml.project.name = nameless
|
||||
@@ -971,13 +968,14 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
self.set_colours(viewpoint)
|
||||
|
||||
def set_exceptions(self, viewpoint, context):
|
||||
if (
|
||||
not hasattr(viewpoint.visualization_info.components, "visibility")
|
||||
or not hasattr(viewpoint.visualization_info.components.visibility.exceptions, "component")
|
||||
if not hasattr(viewpoint.visualization_info.components, "visibility") or not hasattr(
|
||||
viewpoint.visualization_info.components.visibility.exceptions, "component"
|
||||
):
|
||||
return
|
||||
|
||||
exception_global_ids = {v.ifc_guid for v in viewpoint.visualization_info.components.visibility.exceptions.component or []}
|
||||
exception_global_ids = {
|
||||
v.ifc_guid for v in viewpoint.visualization_info.components.visibility.exceptions.component or []
|
||||
}
|
||||
|
||||
# print("default_visibility: {}".format(viewpoint.visualization_info.components.visibility.default_visibility))
|
||||
if viewpoint.visualization_info.components.visibility.default_visibility:
|
||||
@@ -1034,7 +1032,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
context.area.type = old
|
||||
|
||||
def set_openings_visibility(self, is_visible, context):
|
||||
pass # We no longer have an openings collection
|
||||
pass # We no longer have an openings collection
|
||||
|
||||
def set_selection(self, viewpoint):
|
||||
if not viewpoint.visualization_info.components or not viewpoint.visualization_info.components.selection:
|
||||
@@ -1147,7 +1145,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
# https://github.com/buildingSMART/BCF-XML/tree/release_3_0/Documentation#coloring
|
||||
if lv == 8:
|
||||
t = tuple(int(value[i : i + lv // 4], 16) for i in range(0, lv, lv // 4))
|
||||
col = [t[1] / 255.0, t[2] / 255.0, t[3] / 255.0, t[0] / 255.0]
|
||||
col = [t[1] / 255.0, t[2] / 255.0, t[3] / 255.0, t[0] / 255.0]
|
||||
else:
|
||||
t = tuple(int(value[i : i + lv // 3], 16) for i in range(0, lv, lv // 3))
|
||||
col = [t[0] / 255.0, t[1] / 255.0, t[2] / 255.0, 1]
|
||||
|
||||
@@ -603,7 +603,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
objs = context.selected_objects
|
||||
|
||||
if not any((element:=tool.Ifc.get_entity(obj)) and element.is_a("IfcSpace") for obj in objs):
|
||||
if not any((element := tool.Ifc.get_entity(obj)) and element.is_a("IfcSpace") for obj in objs):
|
||||
self.report({"INFO"}, "No IfcSpace elements selected.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -166,9 +166,9 @@ class BrickschemaReferencesData:
|
||||
results.append(
|
||||
{
|
||||
"id": reference.id(),
|
||||
"identification": reference.ItemReference
|
||||
if tool.Ifc.get_schema() == "IFC2X3"
|
||||
else reference.Identification,
|
||||
"identification": (
|
||||
reference.ItemReference if tool.Ifc.get_schema() == "IFC2X3" else reference.Identification
|
||||
),
|
||||
"name": reference.Name or "Unnamed",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -34,6 +34,7 @@ import blenderbim.core.brick as core
|
||||
import blenderbim.tool.brick as tool
|
||||
from blenderbim.tool.brick import BrickStore
|
||||
|
||||
|
||||
def update_active_brick_index(self, context):
|
||||
BrickschemaData.is_loaded = False
|
||||
|
||||
@@ -53,7 +54,7 @@ def get_brick_entity_classes(self, context):
|
||||
return [(uri, uri.split("#")[-1], "") for uri in BrickStore.entity_classes[entity]]
|
||||
|
||||
|
||||
def get_brick_roots(self, context):
|
||||
def get_brick_roots(self, context):
|
||||
return [(root, root, "") for root in BrickStore.root_classes]
|
||||
|
||||
|
||||
@@ -64,13 +65,13 @@ def get_brick_relations(self, context):
|
||||
return relations
|
||||
relations.append(("http://www.w3.org/2000/01/rdf-schema#label", "label", ""))
|
||||
return relations
|
||||
|
||||
|
||||
|
||||
def update_view(self, context):
|
||||
root = context.scene.BIMBrickProperties.brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=False)
|
||||
|
||||
|
||||
def split_screen_update_view(self, context):
|
||||
root = context.scene.BIMBrickProperties.split_screen_brick_list_root
|
||||
core.set_brick_list_root(tool.Brick, brick_root=root, split_screen=True)
|
||||
@@ -108,7 +109,11 @@ class BIMBrickProperties(PropertyGroup):
|
||||
# create relations split screen
|
||||
split_screen_toggled: BoolProperty(name="Split Screen Toggled", default=False)
|
||||
split_screen_bricks: CollectionProperty(name="Split Screen Bricks", type=Brick)
|
||||
split_screen_active_brick_index: IntProperty(name="Split Screen Active Brick Index", update=update_active_brick_index)
|
||||
split_screen_active_brick_index: IntProperty(
|
||||
name="Split Screen Active Brick Index", update=update_active_brick_index
|
||||
)
|
||||
split_screen_active_brick_class: StringProperty(name="Split Screen Active Brick Class")
|
||||
split_screen_brick_breadcrumbs: CollectionProperty(name="Split Screen Brick Breadcrumbs", type=StrProperty)
|
||||
split_screen_brick_list_root: EnumProperty(name="Split Screen Brick List Root", items=get_brick_roots, update=split_screen_update_view)
|
||||
split_screen_brick_list_root: EnumProperty(
|
||||
name="Split Screen Brick List Root", items=get_brick_roots, update=split_screen_update_view
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ class BIM_PT_brickschema_project_info(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMBrickProperties
|
||||
|
||||
|
||||
if not BrickschemaData.data["is_loaded"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.new_brick_file", text="Create Project")
|
||||
@@ -58,7 +58,7 @@ class BIM_PT_brickschema_project_info(Panel):
|
||||
row.label(text=BrickStore.path, icon="FILEBROWSER")
|
||||
else:
|
||||
row.label(text="No file", icon="FILEBROWSER")
|
||||
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
if BrickStore.last_saved:
|
||||
row.label(text=BrickStore.last_saved, icon="TIME")
|
||||
@@ -130,7 +130,7 @@ class BIM_PT_brickschema_create_entity(Panel):
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
prop_with_search(row, self.props, "brick_entity_class", text="Type")
|
||||
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
active = tool.Ifc.get_entity(context.active_object)
|
||||
if active and context.selected_objects:
|
||||
@@ -141,7 +141,7 @@ class BIM_PT_brickschema_create_entity(Panel):
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.add_brick", text="Create Entity")
|
||||
|
||||
|
||||
|
||||
class BIM_PT_brickschema_viewport(Panel):
|
||||
bl_label = "Viewport"
|
||||
@@ -194,7 +194,9 @@ class BIM_PT_brickschema_viewport(Panel):
|
||||
|
||||
row = grid_right.row()
|
||||
BIM_UL_bricks.split_screen = True
|
||||
row.template_list("BIM_UL_bricks", "", self.props, "split_screen_bricks", self.props, "split_screen_active_brick_index")
|
||||
row.template_list(
|
||||
"BIM_UL_bricks", "", self.props, "split_screen_bricks", self.props, "split_screen_active_brick_index"
|
||||
)
|
||||
|
||||
if BrickschemaData.data["active_relations"]:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -213,7 +215,13 @@ class BIM_PT_brickschema_viewport(Panel):
|
||||
row.label(text="No selection", icon="INFO")
|
||||
else:
|
||||
prop_with_search(row, self.props, "new_brick_relation_type", text="")
|
||||
row.label(text=split_screen_selection.label if split_screen_selection.label else split_screen_selection.name)
|
||||
row.label(
|
||||
text=(
|
||||
split_screen_selection.label
|
||||
if split_screen_selection.label
|
||||
else split_screen_selection.name
|
||||
)
|
||||
)
|
||||
row.operator("bim.add_brick_relation", text="", icon="ADD")
|
||||
|
||||
elif self.props.brick_create_relations_toggled:
|
||||
@@ -234,7 +242,11 @@ class BIM_PT_brickschema_viewport(Panel):
|
||||
row.label(text=relation["object_name"])
|
||||
row = split.row(align=True)
|
||||
row.column().alignment = "RIGHT"
|
||||
if self.props.brick_edit_relations_toggled and relation["predicate_uri"] and relation["predicate_name"] != "type":
|
||||
if (
|
||||
self.props.brick_edit_relations_toggled
|
||||
and relation["predicate_uri"]
|
||||
and relation["predicate_name"] != "type"
|
||||
):
|
||||
op = row.operator("bim.remove_brick_relation", text="", icon="UNLINKED")
|
||||
op.predicate = relation["predicate_uri"]
|
||||
op.object = relation["object_uri"]
|
||||
@@ -263,6 +275,7 @@ class BIM_UL_bricks(UIList):
|
||||
row = split.row()
|
||||
row.label(text=str(item.total_items))
|
||||
|
||||
|
||||
class BIM_PT_ifc_brickschema_references(Panel):
|
||||
bl_label = "Brickschema References"
|
||||
bl_idname = "BIM_PT_ifc_brickschema_references"
|
||||
@@ -291,13 +304,13 @@ class BIM_PT_ifc_brickschema_references(Panel):
|
||||
row.label(text="No IFC Libraries")
|
||||
row.operator("bim.convert_brick_project", text="", icon="ADD")
|
||||
return
|
||||
|
||||
|
||||
elif not BrickschemaReferencesData.data["libraries"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No IFC Libraries: save the Brick project to create a new library", icon="ERROR")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Libraries must have location pointing to a \".ttl\" file", icon="INFO")
|
||||
row.label(text='Libraries must have location pointing to a ".ttl" file', icon="INFO")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
@@ -318,4 +331,4 @@ class BIM_PT_ifc_brickschema_references(Panel):
|
||||
row.label(text=reference["identification"], icon="ASSET_MANAGER")
|
||||
row.label(text=reference["name"])
|
||||
row.operator("bim.unassign_library_reference", text="", icon="X").reference = reference["id"]
|
||||
row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT").item = reference["identification"]
|
||||
row.operator("bim.view_brick_item", text="", icon="DISCLOSURE_TRI_RIGHT").item = reference["identification"]
|
||||
|
||||
@@ -47,19 +47,27 @@ class SetActiveBSDDDictionary(bpy.types.Operator):
|
||||
class SearchBSDDClass(bpy.types.Operator):
|
||||
bl_idname = "bim.search_bsdd_classifications"
|
||||
bl_label = "Search bSDD Class"
|
||||
bl_description = "Search for bSDD classes by the provided keyword"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
keyword = context.scene.BIMBSDDProperties.keyword
|
||||
core.search_class(keyword, bsdd.Client(), tool.Bsdd)
|
||||
classes_found = core.search_class(keyword, bsdd.Client(), tool.Bsdd)
|
||||
self.report({"INFO"}, f"{classes_found} bSDD classes found for '{keyword}'.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GetBSDDClassificationProperties(bpy.types.Operator):
|
||||
bl_idname = "bim.get_bsdd_classification_properties"
|
||||
bl_label = "Search bSDD Classifications"
|
||||
bl_label = "Search bSDD Class Properties"
|
||||
bl_description = (
|
||||
"Search for bSDD class properties for the currently selected class. All non-IFC properties are skipped"
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.get_class_properties(bsdd.Client(), tool.Bsdd)
|
||||
pset_data = core.get_class_properties(bsdd.Client(), tool.Bsdd)
|
||||
psets_found = len(pset_data)
|
||||
props_found = sum(len(pset) for pset in pset_data.values())
|
||||
self.report({"INFO"}, f"{psets_found} psets found ({props_found} props) for the active classification.")
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -43,7 +43,6 @@ class BSDDDomain(PropertyGroup):
|
||||
class BSDDClassification(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
reference_code: StringProperty(name="Reference Code")
|
||||
description: StringProperty(name="Description")
|
||||
uri: StringProperty(name="Namespace URI")
|
||||
domain_name: StringProperty(name="Domain Name")
|
||||
domain_namespace_uri: StringProperty(name="Domain Namespace URI")
|
||||
@@ -62,6 +61,12 @@ class BIMBSDDProperties(PropertyGroup):
|
||||
classifications: CollectionProperty(name="Classifications", type=BSDDClassification)
|
||||
active_classification_index: IntProperty(name="Active Classification Index")
|
||||
keyword: StringProperty(name="Keyword")
|
||||
should_filter_ifc_class: BoolProperty(name="Filter Active IFC Class", default=True)
|
||||
load_preview_domains: BoolProperty(name="Load Preview Domains", default=False)
|
||||
should_filter_ifc_class: BoolProperty(
|
||||
name="Filter Active IFC Class",
|
||||
description="Whether to search only for bSDD classes that match active object's IFC class",
|
||||
default=True,
|
||||
)
|
||||
load_preview_domains: BoolProperty(
|
||||
name="Load Preview Domains", description="Whether it should load preview and inactive domains", default=False
|
||||
)
|
||||
classification_psets: CollectionProperty(name="Classification Psets", type=BSDDPset)
|
||||
|
||||
@@ -32,6 +32,7 @@ class BIM_PT_bsdd(Panel):
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMBSDDProperties
|
||||
layout = self.layout
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "load_preview_domains")
|
||||
if len(props.domains):
|
||||
@@ -53,6 +54,22 @@ class BIM_PT_bsdd(Panel):
|
||||
props,
|
||||
"active_domain_index",
|
||||
)
|
||||
if 0 <= props.active_domain_index < len(props.domains):
|
||||
selected_domain = props.domains[props.active_domain_index]
|
||||
else:
|
||||
selected_domain = None
|
||||
|
||||
if selected_domain:
|
||||
layout.label(text="Selected domain:")
|
||||
box = layout.box()
|
||||
row = box.row(align=True)
|
||||
row.label(text="Language")
|
||||
row.label(text=selected_domain.default_language_code)
|
||||
row = box.row(align=True)
|
||||
row.label(text="Version")
|
||||
row.label(text=selected_domain.version)
|
||||
box.operator("bim.open_uri", text="Open bSDD In Browser", icon="URL").uri = selected_domain.uri
|
||||
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.operator("bim.load_bsdd_domains")
|
||||
@@ -61,14 +78,18 @@ class BIM_PT_bsdd(Panel):
|
||||
class BIM_UL_bsdd_domains(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
props = context.scene.BIMBSDDProperties
|
||||
row = layout.row(align=True)
|
||||
if item.status != "Active":
|
||||
row.label(text=f"{item.name} ({item.organization_name_owner}) - {item.status}", icon="ERROR")
|
||||
else:
|
||||
row.label(text=f"{item.name} ({item.organization_name_owner})")
|
||||
op = row.operator("bim.set_active_bsdd_domain", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.name = item.name
|
||||
op.uri = item.uri
|
||||
if item.uri == props.active_uri:
|
||||
row.label(text="", icon="URL")
|
||||
else:
|
||||
op = row.operator("bim.set_active_bsdd_domain", text="", icon="RESTRICT_SELECT_OFF")
|
||||
op.name = item.name
|
||||
op.uri = item.uri
|
||||
|
||||
|
||||
class BIM_UL_bsdd_classifications(UIList):
|
||||
@@ -77,3 +98,4 @@ class BIM_UL_bsdd_classifications(UIList):
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.reference_code)
|
||||
row.label(text=item.name)
|
||||
row.operator("bim.open_uri", text="", icon="URL").uri = item.uri
|
||||
|
||||
@@ -144,7 +144,7 @@ class CadFillet(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.radius = self.radius * si_conversion
|
||||
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
@@ -369,7 +369,7 @@ class CadOffset(bpy.types.Operator):
|
||||
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.distance = self.distance * si_conversion
|
||||
|
||||
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
@@ -507,8 +507,6 @@ class CadOffset(bpy.types.Operator):
|
||||
local_direction = (rotation_i @ direction).normalized()
|
||||
normals.append(local_direction)
|
||||
|
||||
|
||||
|
||||
if len(normals) == 2:
|
||||
# https://stackoverflow.com/a/54042831/9627415
|
||||
new_normal = (normals[0].lerp(normals[1], 0.5)).normalized()
|
||||
@@ -638,7 +636,7 @@ class AddIfcArcIndexFillet(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.radius = self.radius * si_conversion
|
||||
|
||||
|
||||
if self.has_selected_existing_arc(context):
|
||||
self.change_radius(context)
|
||||
else:
|
||||
@@ -814,8 +812,8 @@ class AddRectangle(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
self.x = self.x * si_conversion
|
||||
self.y = self.y * si_conversion
|
||||
|
||||
self.y = self.y * si_conversion
|
||||
|
||||
obj = context.active_object
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
cursor = obj.matrix_world.inverted() @ context.scene.cursor.location
|
||||
|
||||
@@ -70,7 +70,7 @@ class ClashDecorator:
|
||||
blf.color(font_id, *color)
|
||||
if coords_2d:
|
||||
w, h = blf.dimensions(font_id, text)
|
||||
coords_2d -= Vector((w * .5, 0))
|
||||
coords_2d -= Vector((w * 0.5, 0))
|
||||
blf.position(font_id, coords_2d[0], coords_2d[1], 0)
|
||||
blf.draw(font_id, text) # Set your text here
|
||||
|
||||
|
||||
@@ -496,4 +496,3 @@ class SelectSmartGroup(bpy.types.Operator):
|
||||
with bpy.context.temp_override(**context_override):
|
||||
bpy.ops.view3d.view_selected()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -117,20 +117,29 @@ class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
is_ifc2x3 = tool.Ifc.get_schema() == "IFC2X3"
|
||||
|
||||
props = context.scene.BIMBSDDProperties
|
||||
domain = [d for d in props.domains if d.name == props.active_domain][0]
|
||||
domain = next((d for d in props.domains if d.name == props.active_domain and d.uri == props.active_uri), None)
|
||||
|
||||
# Maybe user loaded preview domains, set it as active
|
||||
# and then reloaded them without preview domains.
|
||||
if not domain:
|
||||
self.report(
|
||||
{"INFO"},
|
||||
f"Couldn't find domain '{props.active_domain}' ({props.active_uri}). Try to reload bSDD dictionaries.",
|
||||
)
|
||||
return
|
||||
|
||||
for element in tool.Ifc.get().by_type("IfcClassification"):
|
||||
if element.Name == props.active_domain or (not is_ifc2x3 and element.Location == domain.uri):
|
||||
if element.Name == props.active_domain or (tool.Classification.get_location(element) == domain.uri):
|
||||
self.report({"INFO"}, f"Classification '{props.active_domain}' is already added to the project.")
|
||||
return
|
||||
|
||||
classification = ifcopenshell.api.run(
|
||||
"classification.add_classification", tool.Ifc.get(), classification=props.active_domain
|
||||
)
|
||||
classification.Source = domain.organization_name_owner
|
||||
classification.Edition = domain.version
|
||||
if not is_ifc2x3:
|
||||
classification.Location = domain.uri
|
||||
tool.Classification.set_location(classification, domain.uri)
|
||||
|
||||
|
||||
class EnableAddingManualClassification(bpy.types.Operator):
|
||||
@@ -400,8 +409,6 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
is_ifc2x3 = tool.Ifc.get_schema() == "IFC2X3"
|
||||
|
||||
if self.obj_type == "Object":
|
||||
if context.selected_objects:
|
||||
objects = [o.name for o in context.selected_objects]
|
||||
@@ -416,9 +423,8 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
classification = None
|
||||
for element in tool.Ifc.get().by_type("IfcClassification"):
|
||||
if (
|
||||
element.Name == bsdd_classification.domain_name
|
||||
or (not is_ifc2x3 and element.Location == bsdd_classification.domain_namespace_uri)
|
||||
if element.Name == bsdd_classification.domain_name or (
|
||||
tool.Classification.get_location(element) == bsdd_classification.domain_namespace_uri
|
||||
):
|
||||
classification = element
|
||||
break
|
||||
@@ -427,8 +433,7 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
||||
classification = ifcopenshell.api.run(
|
||||
"classification.add_classification", tool.Ifc.get(), classification=bsdd_classification.domain_name
|
||||
)
|
||||
if not is_ifc2x3:
|
||||
classification.Location = bsdd_classification.domain_namespace_uri
|
||||
tool.Classification.set_location(classification, bsdd_classification.domain_namespace_uri)
|
||||
|
||||
for obj in objects:
|
||||
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
||||
|
||||
@@ -105,7 +105,7 @@ class BIM_PT_object_constraints(Panel):
|
||||
if constraint["type"] == "IfcObjective":
|
||||
icon = "LIGHT"
|
||||
else:
|
||||
continue # Metric not implemented
|
||||
continue # Metric not implemented
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=constraint["name"])
|
||||
row.operator("bim.unassign_constraint", text="", icon="X").constraint = constraint["id"]
|
||||
|
||||
@@ -164,7 +164,14 @@ class CostSchedulesData:
|
||||
if unit:
|
||||
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
|
||||
if quantity.is_a("IfcPhysicalSimpleQuantity"):
|
||||
measure_class = quantity.wrapped_data.declaration().as_entity().attribute_by_index(3).type_of_attribute().declared_type().name()
|
||||
measure_class = (
|
||||
quantity.wrapped_data.declaration()
|
||||
.as_entity()
|
||||
.attribute_by_index(3)
|
||||
.type_of_attribute()
|
||||
.declared_type()
|
||||
.name()
|
||||
)
|
||||
if "Count" in measure_class:
|
||||
data["UnitSymbol"] = "U"
|
||||
|
||||
|
||||
@@ -47,14 +47,14 @@ class BIM_PT_cost_schedules(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
if CostSchedulesData.data["total_cost_schedules"]:
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.export_cost_schedules",icon="EXPORT",text="Export All Schedules")
|
||||
row.operator("bim.export_cost_schedules", icon="EXPORT", text="Export All Schedules")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
|
||||
else:
|
||||
row.label(text="No Cost Schedules found.", icon="TEXT")
|
||||
|
||||
row.operator("bim.add_cost_schedule", icon="ADD", text="")
|
||||
row.operator("bim.import_cost_schedule_csv",icon="IMPORT",text="")
|
||||
row.operator("bim.import_cost_schedule_csv", icon="IMPORT", text="")
|
||||
|
||||
for schedule in CostSchedulesData.data["schedules"]:
|
||||
self.draw_cost_schedule_ui(schedule)
|
||||
@@ -96,9 +96,9 @@ class BIM_PT_cost_schedules(Panel):
|
||||
text="{}[{}]".format(cost_schedule["name"], cost_schedule["predefined_type"]), icon="LINENUMBERS_ON"
|
||||
)
|
||||
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule["id"]
|
||||
row.operator(
|
||||
"bim.enable_editing_cost_schedule_attributes", text="", icon="GREASEPENCIL"
|
||||
).cost_schedule = cost_schedule["id"]
|
||||
row.operator("bim.enable_editing_cost_schedule_attributes", text="", icon="GREASEPENCIL").cost_schedule = (
|
||||
cost_schedule["id"]
|
||||
)
|
||||
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule["id"]
|
||||
if self.props.active_cost_schedule_id == cost_schedule["id"]:
|
||||
if self.props.is_editing == "COST_SCHEDULE_ATTRIBUTES":
|
||||
@@ -616,9 +616,9 @@ class BIM_UL_cost_items_trait:
|
||||
def draw_parent_operator(self, row, cost_item_id):
|
||||
if self.props.active_cost_item_id:
|
||||
if self.props.active_cost_item_id != cost_item_id:
|
||||
op = row.operator(
|
||||
"bim.change_parent_cost_item", text="", icon="LINKED", emboss=False
|
||||
).new_parent = cost_item_id
|
||||
op = row.operator("bim.change_parent_cost_item", text="", icon="LINKED", emboss=False).new_parent = (
|
||||
cost_item_id
|
||||
)
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
#from blenderbim.bim.module.spatial.data import SpatialData
|
||||
|
||||
# from blenderbim.bim.module.spatial.data import SpatialData
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -30,10 +31,13 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
#import blenderbim.tool as tool
|
||||
#import blenderbim.core.geometry
|
||||
#import ifcopenshell
|
||||
|
||||
# import blenderbim.tool as tool
|
||||
# import blenderbim.core.geometry
|
||||
# import ifcopenshell
|
||||
|
||||
|
||||
class BIMCoveringProperties(PropertyGroup):
|
||||
ceiling_height: bpy.props.FloatProperty(name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height")
|
||||
ceiling_height: bpy.props.FloatProperty(
|
||||
name="ceiling_height", default=2.7, subtype="DISTANCE", description="Ceiling height"
|
||||
)
|
||||
|
||||
@@ -89,7 +89,7 @@ class BIM_PT_ifccsv(Panel):
|
||||
|
||||
row = layout.row(align=True)
|
||||
op = row.operator("bim.search", text="Select", icon="VIEWZOOM")
|
||||
op.property_group = "CsvProperties"
|
||||
op.property_group = "CsvProperties"
|
||||
layout.separator()
|
||||
|
||||
row = layout.row(align=True)
|
||||
@@ -128,6 +128,3 @@ class BIM_PT_ifccsv(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.export_ifccsv", icon="EXPORT", text="Export IFC to " + props.format.upper())
|
||||
row.operator("bim.import_ifccsv", icon="IMPORT")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -90,9 +90,9 @@ class BIM_PT_debug(Panel):
|
||||
row.operator("bim.select_high_polygon_meshes").threshold = context.scene.BIMDebugProperties.number_of_polygons
|
||||
row.prop(props, "number_of_polygons", text="")
|
||||
row = layout.split(factor=0.7, align=True)
|
||||
row.operator(
|
||||
"bim.select_highest_polygon_meshes"
|
||||
).percentile = context.scene.BIMDebugProperties.percentile_of_polygons
|
||||
row.operator("bim.select_highest_polygon_meshes").percentile = (
|
||||
context.scene.BIMDebugProperties.percentile_of_polygons
|
||||
)
|
||||
row.prop(props, "percentile_of_polygons", text="")
|
||||
|
||||
row = layout.split(factor=0.5, align=True)
|
||||
@@ -140,9 +140,9 @@ class BIM_PT_debug(Panel):
|
||||
op = row.operator("bim.print_object_placement", icon="OBJECT_ORIGIN", text="")
|
||||
op.step_id = attribute.int_value
|
||||
if attribute.int_value:
|
||||
row.operator(
|
||||
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""
|
||||
).step_id = attribute.int_value
|
||||
row.operator("bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text="").step_id = (
|
||||
attribute.int_value
|
||||
)
|
||||
|
||||
if props.inverse_attributes:
|
||||
layout.label(text="Inverse attributes:")
|
||||
@@ -152,9 +152,9 @@ class BIM_PT_debug(Panel):
|
||||
row.prop(attribute, "name", text="")
|
||||
row.prop(attribute, "string_value", text="")
|
||||
if attribute.int_value:
|
||||
row.operator(
|
||||
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""
|
||||
).step_id = attribute.int_value
|
||||
row.operator("bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text="").step_id = (
|
||||
attribute.int_value
|
||||
)
|
||||
|
||||
if props.inverse_references:
|
||||
layout.label(text="Inverse references:")
|
||||
@@ -163,6 +163,6 @@ class BIM_PT_debug(Panel):
|
||||
row = layout.row(align=True)
|
||||
row.prop(attribute, "string_value", text="")
|
||||
if attribute.int_value:
|
||||
row.operator(
|
||||
"bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text=""
|
||||
).step_id = attribute.int_value
|
||||
row.operator("bim.inspect_from_step_id", icon="DISCLOSURE_TRI_RIGHT", text="").step_id = (
|
||||
attribute.int_value
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
import bpy
|
||||
from blenderbim.bim.module.demo.data import DemoData
|
||||
|
||||
|
||||
# Every panel in the interface correlates to one of these classes. If you enable
|
||||
# the "Developer Extras" option in Blender, then you can actually right click on
|
||||
# any panel in Blender, and click "Edit Source". This will bring you to a ui.py
|
||||
|
||||
@@ -159,7 +159,7 @@ class BaseDecorator:
|
||||
objecttype = "NOTDEFINED"
|
||||
|
||||
def __init__(self):
|
||||
self.font_id = 0 # 0 is the default font
|
||||
self.font_id = 0 # 0 is the default font
|
||||
|
||||
# POLYLINE_UNIFORM_COLOR is good for smoothed lines since `bgl.enable(GL_LINE_SMOOTH)` is deprecated
|
||||
self.line_shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
|
||||
@@ -2020,7 +2020,6 @@ class DecorationsHandler:
|
||||
for decorator in self.decorators.values():
|
||||
decorator.font_id = font_id
|
||||
|
||||
|
||||
def get_objects_and_decorators(self, collection):
|
||||
# TODO: do it in data instead of the handler for performance?
|
||||
results = []
|
||||
|
||||
@@ -41,9 +41,8 @@ def set_active_camera_resolution(scene):
|
||||
props = scene.camera.data.BIMCameraProperties
|
||||
ortho_scale = max((props.width, props.height))
|
||||
aspect_ratio = props.width / props.height
|
||||
if (
|
||||
(scene.camera.data.ortho_scale != ortho_scale)
|
||||
or (scene.render.resolution_x / scene.render.resolution_y != aspect_ratio)
|
||||
if (scene.camera.data.ortho_scale != ortho_scale) or (
|
||||
scene.render.resolution_x / scene.render.resolution_y != aspect_ratio
|
||||
):
|
||||
scene.camera.data.ortho_scale = ortho_scale
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ def format_distance(
|
||||
precision=None,
|
||||
decimal_places=None,
|
||||
suppress_zero_inches=False,
|
||||
in_unit_length = False
|
||||
in_unit_length=False,
|
||||
):
|
||||
s_code = "\u00b2" # Superscript two THIS IS LEGACY (but being kept for when Area Measurements are re-implimented)
|
||||
|
||||
@@ -141,9 +141,9 @@ def format_distance(
|
||||
# Imperial Formatting
|
||||
if unit_system == "IMPERIAL":
|
||||
if in_unit_length:
|
||||
if unit_length == 'INCHES':
|
||||
if unit_length == "INCHES":
|
||||
toInches = 1
|
||||
if unit_length == 'FEET':
|
||||
if unit_length == "FEET":
|
||||
toInches = 11.9999
|
||||
else:
|
||||
toInches = 39.3700787401574887
|
||||
@@ -222,11 +222,11 @@ def format_distance(
|
||||
# METRIC FORMATTING
|
||||
elif unit_system == "METRIC":
|
||||
if in_unit_length:
|
||||
if unit_length == 'CENTIMETERS':
|
||||
value = value/100
|
||||
if unit_length == 'MILLIMETERS':
|
||||
value = value/1000
|
||||
|
||||
if unit_length == "CENTIMETERS":
|
||||
value = value / 100
|
||||
if unit_length == "MILLIMETERS":
|
||||
value = value / 1000
|
||||
|
||||
if precision:
|
||||
value = precision * round(float(value) / precision)
|
||||
|
||||
|
||||
@@ -1472,12 +1472,9 @@ class ActivateModel(bpy.types.Operator):
|
||||
|
||||
CutDecorator.uninstall()
|
||||
|
||||
# save current visibility statuses for Views and Types collections
|
||||
# save current visibility statuses
|
||||
visibility_status: dict[bpy.types.Object, bool] = {}
|
||||
for col in bpy.data.collections["Views"].children:
|
||||
for obj in col.objects:
|
||||
visibility_status[obj] = obj.hide_get()
|
||||
for obj in bpy.data.collections["Types"].objects:
|
||||
for obj in bpy.data.objects:
|
||||
visibility_status[obj] = obj.hide_get()
|
||||
|
||||
if not bpy.app.background:
|
||||
@@ -1508,6 +1505,7 @@ class ActivateModel(bpy.types.Operator):
|
||||
obj.hide_set(hide_status)
|
||||
|
||||
tool.Blender.update_viewport()
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -560,7 +560,7 @@ class Scheduler:
|
||||
|
||||
text_tag = self.svg.text("", **(text_params | {"font-size": "0"} | box_alignment_params))
|
||||
|
||||
# TODO: Should be done without using magic number for self.font_width
|
||||
# TODO: Should be done without using magic number for self.font_width
|
||||
if wrap_text:
|
||||
wrapped_lines = []
|
||||
for line in text_lines:
|
||||
|
||||
@@ -123,18 +123,18 @@ class BIM_PT_element_filters(Panel):
|
||||
self.layout, props.include_filter_groups, ElementFiltersData, "drawing_include"
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator(
|
||||
"bim.edit_element_filter", icon="CHECKMARK", text="Save Include Filter"
|
||||
).filter_mode = "INCLUDE"
|
||||
row.operator("bim.edit_element_filter", icon="CHECKMARK", text="Save Include Filter").filter_mode = (
|
||||
"INCLUDE"
|
||||
)
|
||||
row.operator("bim.enable_editing_element_filter", icon="CANCEL", text="").filter_mode = "NONE"
|
||||
elif props.filter_mode == "EXCLUDE":
|
||||
blenderbim.bim.helper.draw_filter(
|
||||
self.layout, props.exclude_filter_groups, ElementFiltersData, "drawing_exclude"
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator(
|
||||
"bim.edit_element_filter", icon="CHECKMARK", text="Save Exclude Filter"
|
||||
).filter_mode = "EXCLUDE"
|
||||
row.operator("bim.edit_element_filter", icon="CHECKMARK", text="Save Exclude Filter").filter_mode = (
|
||||
"EXCLUDE"
|
||||
)
|
||||
row.operator("bim.enable_editing_element_filter", icon="CANCEL", text="").filter_mode = "NONE"
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -260,9 +260,9 @@ class BIM_PT_drawings(Panel):
|
||||
row2 = col.row(align=True)
|
||||
row2.operator("bim.remove_drawing", icon="X", text="").drawing = active_drawing.ifc_definition_id
|
||||
|
||||
row2.operator(
|
||||
"bim.duplicate_drawing", icon="COPYDOWN", text=""
|
||||
).drawing = active_drawing.ifc_definition_id
|
||||
row2.operator("bim.duplicate_drawing", icon="COPYDOWN", text="").drawing = (
|
||||
active_drawing.ifc_definition_id
|
||||
)
|
||||
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
@@ -339,9 +339,9 @@ class BIM_PT_schedules(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.open_schedule", icon="URL", text="").schedule = active_schedule.ifc_definition_id
|
||||
row.operator(
|
||||
"bim.build_schedule", icon="LINENUMBERS_ON", text=""
|
||||
).schedule = active_schedule.ifc_definition_id
|
||||
row.operator("bim.build_schedule", icon="LINENUMBERS_ON", text="").schedule = (
|
||||
active_schedule.ifc_definition_id
|
||||
)
|
||||
row.operator("bim.remove_schedule", icon="X", text="").schedule = active_schedule.ifc_definition_id
|
||||
|
||||
self.layout.template_list(
|
||||
@@ -612,13 +612,13 @@ class BIM_UL_sheets(bpy.types.UIList):
|
||||
row = layout.row(align=True)
|
||||
if item.is_sheet:
|
||||
if item.is_expanded:
|
||||
row.operator(
|
||||
"bim.contract_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
|
||||
).sheet = item.ifc_definition_id
|
||||
row.operator("bim.contract_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN").sheet = (
|
||||
item.ifc_definition_id
|
||||
)
|
||||
else:
|
||||
row.operator(
|
||||
"bim.expand_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
|
||||
).sheet = item.ifc_definition_id
|
||||
row.operator("bim.expand_sheet", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT").sheet = (
|
||||
item.ifc_definition_id
|
||||
)
|
||||
|
||||
row.label(text=f"{item.identification} - {item.name}")
|
||||
else:
|
||||
|
||||
@@ -22,7 +22,6 @@ from . import ui, prop, operator
|
||||
classes = (
|
||||
operator.ExecuteIfcFM,
|
||||
operator.ExecuteIfcFMFederate,
|
||||
operator.SelectFMIfcFile,
|
||||
operator.SelectFMSpreadsheetFiles,
|
||||
prop.BIMFMProperties,
|
||||
ui.BIM_PT_fm,
|
||||
|
||||
@@ -24,21 +24,8 @@ import logging
|
||||
import tempfile
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.operator import MultipleFileSelector
|
||||
|
||||
|
||||
class SelectFMIfcFile(MultipleFileSelector):
|
||||
bl_idname = "bim.select_fm_ifc_file"
|
||||
bl_label = "Select FM IFC File"
|
||||
filename_ext = ".ifc"
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMFMProperties
|
||||
|
||||
self.update_props(props, "ifc_file", props.ifc_files)
|
||||
return {"FINISHED"}
|
||||
|
||||
class ExecuteIfcFM(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_ifcfm"
|
||||
bl_label = "Execute IfcFM"
|
||||
@@ -49,7 +36,7 @@ class ExecuteIfcFM(bpy.types.Operator):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMFMProperties
|
||||
return props.should_load_from_memory or props.ifc_file
|
||||
return props.should_load_from_memory or props.ifc_files.single_file
|
||||
|
||||
def invoke(self, context, event):
|
||||
props = context.scene.BIMFMProperties
|
||||
@@ -62,14 +49,13 @@ class ExecuteIfcFM(bpy.types.Operator):
|
||||
props = context.scene.BIMFMProperties
|
||||
ifc_file = tool.Ifc.get()
|
||||
filepaths = []
|
||||
if not (ifc_file and props.should_load_from_memory):
|
||||
if len(props.ifc_files):
|
||||
ifc_files = [ifcopenshell.open(f.name) for f in props.ifc_files]
|
||||
filepaths = [f.name for f in props.ifc_files]
|
||||
else:
|
||||
ifc_files = [ifcopenshell.open(props.ifc_file)]
|
||||
else:
|
||||
if ifc_file and props.should_load_from_memory:
|
||||
ifc_files = [ifc_file]
|
||||
else:
|
||||
ifc_files = [ifcopenshell.open(f.name) for f in props.ifc_files.file_list]
|
||||
|
||||
if len(ifc_files) > 1:
|
||||
filepaths = [f.name for f in props.ifc_files.file_list]
|
||||
|
||||
for i, ifc_file in enumerate(ifc_files):
|
||||
if filepaths:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.module.fm.data import FMData
|
||||
from blenderbim.bim.prop import StrProperty
|
||||
from blenderbim.bim.prop import MultipleFileSelect, StrProperty
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -39,11 +39,10 @@ def get_engine(self, context):
|
||||
|
||||
|
||||
class BIMFMProperties(PropertyGroup):
|
||||
ifc_file: StringProperty(default="", name="IFC File")
|
||||
ifc_files: CollectionProperty(name="IFC Files", type=StrProperty)
|
||||
ifc_files: PointerProperty(type=MultipleFileSelect)
|
||||
spreadsheet_files: CollectionProperty(name="Spreadsheets", type=StrProperty)
|
||||
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
|
||||
engine: EnumProperty(items=get_engine, name="Engine")
|
||||
engine: EnumProperty(items=get_engine, name="Engine", options=set())
|
||||
format: EnumProperty(
|
||||
items=[
|
||||
("csv", "csv", ""),
|
||||
@@ -52,4 +51,5 @@ class BIMFMProperties(PropertyGroup):
|
||||
],
|
||||
name="Format",
|
||||
default="ods",
|
||||
options=set(),
|
||||
)
|
||||
|
||||
@@ -45,12 +45,8 @@ class BIM_PT_fm(Panel):
|
||||
row.prop(props, "should_load_from_memory")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
row = layout.row(align=True)
|
||||
if len(props.ifc_files) > 1:
|
||||
row.label(text=f"{len(props.ifc_files)} Files Selected")
|
||||
else:
|
||||
row.prop(props, "ifc_file")
|
||||
row.operator("bim.select_fm_ifc_file", icon="FILE_FOLDER", text="")
|
||||
row = layout.row()
|
||||
props.ifc_files.layout_file_select(row, "*.ifc;*.ifczip;*.ifcxml", "IFC File(s)")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "engine")
|
||||
|
||||
@@ -85,15 +85,15 @@ class BIM_MT_object_set_origin(Menu):
|
||||
bl_label = "IFC Set Origin"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Geometry to Origin"
|
||||
).origin_type = "GEOMETRY_ORIGIN"
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Origin to Geometry"
|
||||
).origin_type = "ORIGIN_GEOMETRY"
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Origin to 3D Cursor"
|
||||
).origin_type = "ORIGIN_CURSOR"
|
||||
self.layout.operator("bim.override_origin_set", icon="PLUGIN", text="IFC Geometry to Origin").origin_type = (
|
||||
"GEOMETRY_ORIGIN"
|
||||
)
|
||||
self.layout.operator("bim.override_origin_set", icon="PLUGIN", text="IFC Origin to Geometry").origin_type = (
|
||||
"ORIGIN_GEOMETRY"
|
||||
)
|
||||
self.layout.operator("bim.override_origin_set", icon="PLUGIN", text="IFC Origin to 3D Cursor").origin_type = (
|
||||
"ORIGIN_CURSOR"
|
||||
)
|
||||
self.layout.operator(
|
||||
"bim.override_origin_set", icon="PLUGIN", text="IFC Origin to Center of Mass (Surface)"
|
||||
).origin_type = "ORIGIN_CENTER_OF_MASS"
|
||||
|
||||
@@ -22,8 +22,6 @@ from . import ui, prop, operator
|
||||
classes = (
|
||||
operator.AddGeoreferencing,
|
||||
operator.ConvertAngleToCoordinates,
|
||||
operator.ConvertGlobalToLocal,
|
||||
operator.ConvertLocalToGlobal,
|
||||
operator.DisableEditingGeoreferencing,
|
||||
operator.DisableEditingTrueNorth,
|
||||
operator.DisableEditingWCS,
|
||||
|
||||
@@ -21,7 +21,7 @@ import bpy
|
||||
import numpy as np
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.geolocation
|
||||
from mathutils import Matrix
|
||||
from mathutils import Matrix, Vector
|
||||
from ifcopenshell.util.doc import get_entity_doc
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ class GeoreferenceData:
|
||||
cls.data["local_unit_symbol"] = cls.local_unit_symbol()
|
||||
cls.data["map_unit_symbol"] = cls.map_unit_symbol()
|
||||
cls.data["world_coordinate_system"] = cls.world_coordinate_system()
|
||||
cls.data["local_origin"] = cls.local_origin()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
@@ -162,8 +163,40 @@ class GeoreferenceData:
|
||||
result["has_transformation"] = True
|
||||
result["rotation"] = str(round(ifcopenshell.util.geolocation.yaxis2angle(*wcs[:, 1][:2]), 3))
|
||||
result["x"], result["y"], result["z"] = wcs[:, 3][:3]
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
blender_xyz = ifcopenshell.util.geolocation.enh2xyz(
|
||||
result["x"],
|
||||
result["y"],
|
||||
result["z"],
|
||||
float(props.blender_offset_x),
|
||||
float(props.blender_offset_y),
|
||||
float(props.blender_offset_z),
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
else:
|
||||
blender_xyz = wcs[:, 3][:3]
|
||||
|
||||
result["blender_x"], result["blender_y"], result["blender_z"] = blender_xyz
|
||||
result["blender_location"] = Vector([co * unit_scale for co in blender_xyz])
|
||||
|
||||
wcs[0][3] *= unit_scale
|
||||
wcs[1][3] *= unit_scale
|
||||
wcs[2][3] *= unit_scale
|
||||
result["matrix"] = Matrix(wcs)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def local_origin(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if not props.has_blender_offset:
|
||||
return
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
x = float(props.blender_offset_x) * -1
|
||||
y = float(props.blender_offset_y) * -1
|
||||
z = float(props.blender_offset_z) * -1
|
||||
enh = ifcopenshell.util.geolocation.auto_xyz2enh(tool.Ifc.get(), 0, 0, 0)
|
||||
xyz_si = Vector([x * unit_scale, y * unit_scale, z * unit_scale])
|
||||
return {"x": x, "y": y, "z": z, "enh": enh, "location": xyz_si}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import blf
|
||||
import gpu
|
||||
import bmesh
|
||||
@@ -61,10 +62,10 @@ class GeoreferenceDecorator:
|
||||
self.calculate_angles(context)
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
e, n, h = [round(float(o), 7) for o in props.model_origin.split(",")]
|
||||
|
||||
wcs_matrix = None
|
||||
if GeoreferenceData.data["world_coordinate_system"]["has_transformation"]:
|
||||
wcs_matrix = GeoreferenceData.data["world_coordinate_system"]["matrix"]
|
||||
if (operation := GeoreferenceData.data["coordinate_operation"]) and (scale := operation.get("Scale", None)):
|
||||
e *= scale
|
||||
n *= scale
|
||||
h *= scale
|
||||
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
|
||||
@@ -75,10 +76,14 @@ class GeoreferenceDecorator:
|
||||
blf.enable(self.font_id, blf.SHADOW)
|
||||
blf.shadow(self.font_id, 6, 0, 0, 0, 1)
|
||||
|
||||
text = "Blender Coordinates X: 0, Y: 0, Z: 0"
|
||||
if props.has_blender_offset:
|
||||
text = "Blender Origin"
|
||||
text += f"\nBlender Coordinates ({GeoreferenceData.data['local_unit_symbol']}) X: 0, Y: 0, Z: 0"
|
||||
else:
|
||||
text = "IFC Local Origin"
|
||||
text += f"\nLocal Coordinates ({GeoreferenceData.data['local_unit_symbol']}) X: {props.blender_offset_x}, Y: {props.blender_offset_y}, Z: {props.blender_offset_z}"
|
||||
if GeoreferenceData.data["projected_crs"]:
|
||||
text += f"\nMap Coordinates ({GeoreferenceData.data['local_unit_symbol']}) E: {e}, N: {n}, H: {h}"
|
||||
text += f"\nMap Coordinates ({GeoreferenceData.data['map_unit_symbol']}) E: {e}, N: {n}, H: {h}"
|
||||
self.draw_text_at_position(context, text, Vector((0, -0.1, 0)))
|
||||
|
||||
angle = Matrix.Rotation(radians(self.pn_angle), 4, "Z")
|
||||
@@ -111,25 +116,42 @@ class GeoreferenceDecorator:
|
||||
arc_mid = angle_half @ arc_start
|
||||
self.draw_text_at_position(context, f"{self.tn_angle}deg", arc_mid)
|
||||
|
||||
if wcs_matrix:
|
||||
x, y, z = wcs_matrix.translation
|
||||
if (wcs := GeoreferenceData.data["world_coordinate_system"]) and wcs["has_transformation"]:
|
||||
text = "WCS"
|
||||
if props.has_blender_offset:
|
||||
text += f"\nBlender Coordinates ({GeoreferenceData.data['local_unit_symbol']}) X: {wcs['blender_x']}, Y: {wcs['blender_y']}, Z: {wcs['blender_z']}"
|
||||
text += f"\nLocal Coordinates ({GeoreferenceData.data['local_unit_symbol']}) X: {wcs['x']}, Y: {wcs['y']}, Z: {wcs['z']}"
|
||||
if operation := GeoreferenceData.data["coordinate_operation"]:
|
||||
e = operation.get("Eastings")
|
||||
n = operation.get("Northings")
|
||||
h = operation.get("OrthogonalHeight")
|
||||
text = f"WCS\n X: {x}, Y: {y}, Z: {z}\nE: {e}, N: {n}, H: {h}"
|
||||
else:
|
||||
text = f"WCS\n X: {x}, Y: {y}, Z: {z}"
|
||||
text += f"\nMap Coordinates ({GeoreferenceData.data['map_unit_symbol']}) E: {e}, N: {n}, H: {h}"
|
||||
|
||||
if wcs_matrix.translation.length < 1000:
|
||||
position = wcs_matrix.translation.copy()
|
||||
if wcs["blender_location"].length < 1000:
|
||||
position = wcs["blender_location"].copy()
|
||||
else:
|
||||
position = wcs_matrix.translation.normalized() * 3
|
||||
position = wcs["blender_location"].normalized() * 3
|
||||
text += "\n(Warning: Actual XYZ Not Shown)"
|
||||
position -= Vector((0, 0.1, 0))
|
||||
|
||||
self.draw_text_at_position(context, text, position)
|
||||
|
||||
if props.has_blender_offset:
|
||||
text = "IFC Local Origin"
|
||||
x = GeoreferenceData.data["local_origin"]["x"]
|
||||
y = GeoreferenceData.data["local_origin"]["y"]
|
||||
z = GeoreferenceData.data["local_origin"]["z"]
|
||||
location = GeoreferenceData.data["local_origin"]["location"]
|
||||
text += f"\nBlender Coordinates ({GeoreferenceData.data['local_unit_symbol']}) X: {x}, Y: {y}, Z: {z}"
|
||||
text += f"\nLocal Coordinates ({GeoreferenceData.data['local_unit_symbol']}) X: 0, Y: 0, Z: 0"
|
||||
if GeoreferenceData.data["projected_crs"]:
|
||||
e, n, h = GeoreferenceData.data["local_origin"]["enh"]
|
||||
text += f"\nMap Coordinates ({GeoreferenceData.data['map_unit_symbol']}) E: {e}, N: {n}, H: {h}"
|
||||
if location.length > 1000:
|
||||
location = location.normalized() * 3
|
||||
text += "\n(Warning: Actual XYZ Not Shown)"
|
||||
self.draw_text_at_position(context, text, location)
|
||||
|
||||
def draw_text_at_position(self, context, text, position):
|
||||
coords_2d = location_3d_to_region_2d(context.region, context.region_data, position)
|
||||
if not coords_2d:
|
||||
@@ -144,10 +166,6 @@ class GeoreferenceDecorator:
|
||||
def draw_geometry(self, context):
|
||||
self.calculate_angles(context)
|
||||
|
||||
wcs_matrix = None
|
||||
if GeoreferenceData.data["world_coordinate_system"]["has_transformation"]:
|
||||
wcs_matrix = GeoreferenceData.data["world_coordinate_system"]["matrix"]
|
||||
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
decorator_color = self.addon_prefs.decorations_colour
|
||||
decorator_color_special = self.addon_prefs.decorator_color_special
|
||||
@@ -263,24 +281,50 @@ class GeoreferenceDecorator:
|
||||
verts, edges = arc_segments
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
|
||||
if wcs_matrix:
|
||||
if wcs_matrix.translation.length < 1000:
|
||||
verts = [Vector((0, 0, 0)), wcs_matrix.translation]
|
||||
if (wcs := GeoreferenceData.data["world_coordinate_system"]) and wcs["has_transformation"]:
|
||||
if wcs["blender_location"].length < 1000:
|
||||
verts = [Vector((0, 0, 0)), wcs["blender_location"]]
|
||||
edges = [[0, 1]]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
self.draw_batch("POINTS", verts[1:], decorator_color_special)
|
||||
else:
|
||||
location = wcs["blender_location"].normalized()
|
||||
edges = [[0, 1]]
|
||||
verts = [Vector((0, 0, 0)), wcs_matrix.translation.normalized() * 3]
|
||||
verts = [Vector((0, 0, 0)), location * 3]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
verts = [wcs_matrix.translation.normalized() * 3, wcs_matrix.translation.normalized() * 3.1]
|
||||
self.draw_batch("LINES", verts, decorator_color_error, edges)
|
||||
verts = [wcs_matrix.translation.normalized() * 3.12, wcs_matrix.translation.normalized() * 3.2]
|
||||
self.draw_batch("LINES", verts, decorator_color_error, edges)
|
||||
verts = [wcs_matrix.translation.normalized() * 3.22, wcs_matrix.translation.normalized() * 3.3]
|
||||
self.draw_batch("LINES", verts, decorator_color_error, edges)
|
||||
verts = [wcs_matrix.translation.normalized() * 3.32, wcs_matrix.translation.normalized() * 3.4]
|
||||
self.draw_batch("LINES", verts, decorator_color_error, edges)
|
||||
self.draw_dashed_line(location * 3, location * 6, decorator_color_error)
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
if props.has_blender_offset:
|
||||
location = GeoreferenceData.data["local_origin"]["location"]
|
||||
if location.length < 1000:
|
||||
verts = [Vector((0, 0, 0)), location]
|
||||
edges = [[0, 1]]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
self.draw_batch("POINTS", verts[1:], decorator_color_special)
|
||||
else:
|
||||
location = location.normalized()
|
||||
edges = [[0, 1]]
|
||||
verts = [Vector((0, 0, 0)), location * 3]
|
||||
self.draw_batch("LINES", verts, decorator_color_special, edges)
|
||||
self.draw_dashed_line(location * 3, location * 6, decorator_color_error)
|
||||
|
||||
def draw_dashed_line(self, start, end, colour):
|
||||
direction = (end - start).normalized()
|
||||
distance = (end - start).length
|
||||
current_distance = Vector((0, 0, 0))
|
||||
gap = 0.05 * direction
|
||||
dash = 0.1 * direction
|
||||
points = [start]
|
||||
while current_distance.length < distance:
|
||||
points.append(start + current_distance + dash)
|
||||
points.append(start + current_distance + dash + gap)
|
||||
current_distance += gap + dash
|
||||
points.pop()
|
||||
|
||||
edges = [[i, i + 1] for i in range(0, len(points), 2)]
|
||||
verts = points
|
||||
self.draw_batch("LINES", verts, colour, edges)
|
||||
|
||||
def calculate_angles(self, context):
|
||||
self.pn_angle = 0.0
|
||||
|
||||
@@ -87,37 +87,6 @@ class GetCursorLocation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
core.get_cursor_location(tool.Georeference)
|
||||
|
||||
|
||||
class ConvertLocalToGlobal(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.convert_local_to_global"
|
||||
bl_label = "Convert Local To Global"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Convert local coordinate to global coordinate"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = tool.Ifc.get()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
return file and props.local_coordinates.count(",") == 2
|
||||
|
||||
def _execute(self, context):
|
||||
core.convert_local_to_global(tool.Georeference)
|
||||
|
||||
|
||||
class ConvertGlobalToLocal(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.convert_global_to_local"
|
||||
bl_label = "Convert Global To Local"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Convert global coordinate to local coordinate"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = tool.Ifc.get()
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
return file and file.by_type("IfcUnitAssignment") and props.local_coordinates.count(",") == 2
|
||||
|
||||
def _execute(self, context):
|
||||
core.convert_global_to_local(tool.Georeference)
|
||||
|
||||
class ConvertAngleToCoordinates(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.convert_angle_to_coord"
|
||||
bl_label = "Convert Angle To Y Axis"
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.util.geolocation
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -101,6 +102,74 @@ def update_should_visualise(self, context):
|
||||
GeoreferenceDecorator.uninstall()
|
||||
|
||||
|
||||
def update_blender_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
blender_coordinates = tool.Georeference.get_coordinates("blender")
|
||||
local_coordinates = ifcopenshell.util.geolocation.xyz2enh(
|
||||
*blender_coordinates,
|
||||
float(props.blender_offset_x),
|
||||
float(props.blender_offset_y),
|
||||
float(props.blender_offset_z),
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
)
|
||||
tool.Georeference.set_coordinates("local", local_coordinates)
|
||||
tool.Georeference.set_coordinates(
|
||||
"map", ifcopenshell.util.geolocation.auto_xyz2enh(tool.Ifc.get(), *local_coordinates)
|
||||
)
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
def update_local_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
local_coordinates = tool.Georeference.get_coordinates("local")
|
||||
tool.Georeference.set_coordinates(
|
||||
"map", ifcopenshell.util.geolocation.auto_xyz2enh(tool.Ifc.get(), *local_coordinates)
|
||||
)
|
||||
if props.has_blender_offset:
|
||||
tool.Georeference.set_coordinates(
|
||||
"blender",
|
||||
ifcopenshell.util.geolocation.enh2xyz(
|
||||
*local_coordinates,
|
||||
float(props.blender_offset_x),
|
||||
float(props.blender_offset_y),
|
||||
float(props.blender_offset_z),
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
),
|
||||
)
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
def update_map_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
map_coordinates = tool.Georeference.get_coordinates("map")
|
||||
local_coordinates = ifcopenshell.util.geolocation.auto_enh2xyz(tool.Ifc.get(), *map_coordinates)
|
||||
tool.Georeference.set_coordinates("local", local_coordinates)
|
||||
if props.has_blender_offset:
|
||||
tool.Georeference.set_coordinates(
|
||||
"blender",
|
||||
ifcopenshell.util.geolocation.enh2xyz(
|
||||
*local_coordinates,
|
||||
float(props.blender_offset_x),
|
||||
float(props.blender_offset_y),
|
||||
float(props.blender_offset_z),
|
||||
float(props.blender_x_axis_abscissa),
|
||||
float(props.blender_x_axis_ordinate),
|
||||
),
|
||||
)
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
class BIMGeoreferenceProperties(PropertyGroup):
|
||||
coordinate_operation_class: bpy.props.EnumProperty(
|
||||
items=get_coordinate_operation_class, name="Coordinate Operation Class"
|
||||
@@ -111,11 +180,24 @@ class BIMGeoreferenceProperties(PropertyGroup):
|
||||
is_editing_true_north: BoolProperty(name="Is Editing True North")
|
||||
coordinate_operation: CollectionProperty(name="Coordinate Operation", type=Attribute)
|
||||
projected_crs: CollectionProperty(name="Projected CRS", type=Attribute)
|
||||
is_updating_coordinates: BoolProperty(name="Is Updating Coordinates", default=False)
|
||||
blender_coordinates: StringProperty(
|
||||
name="Blender Coordinates",
|
||||
description='Formatted "x,y,z" (without quotes)',
|
||||
default="0,0,0",
|
||||
update=update_blender_coordinates,
|
||||
)
|
||||
local_coordinates: StringProperty(
|
||||
name="Local Coordinates", description='Formatted "x,y,z" (without quotes)', default="0,0,0"
|
||||
name="Local Coordinates",
|
||||
description='Formatted "x,y,z" (without quotes)',
|
||||
default="0,0,0",
|
||||
update=update_local_coordinates,
|
||||
)
|
||||
map_coordinates: StringProperty(
|
||||
name="Map Coordinates", description='Formatted "x,y,z" (without quotes)', default="0,0,0"
|
||||
name="Map Coordinates",
|
||||
description='Formatted "x,y,z" (without quotes)',
|
||||
default="0,0,0",
|
||||
update=update_map_coordinates,
|
||||
)
|
||||
should_visualise: BoolProperty(
|
||||
name="Should Visualise",
|
||||
|
||||
@@ -288,12 +288,15 @@ class BIM_PT_gis_calculator(Panel):
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "local_coordinates", text=f"Local ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.operator("bim.get_cursor_location", text="", icon="TRACKER")
|
||||
row = self.layout.row()
|
||||
row.prop(props, "map_coordinates", text=f"Map ({GeoreferenceData.data['map_unit_symbol']})")
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "blender_coordinates", text=f"Blender ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
row.operator("bim.get_cursor_location", text="", icon="TRACKER")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.convert_local_to_global", text="Local to Global")
|
||||
row.operator("bim.convert_global_to_local", text="Global to Local")
|
||||
row.prop(props, "local_coordinates", text=f"Local ({GeoreferenceData.data['local_unit_symbol']})")
|
||||
if not props.has_blender_offset:
|
||||
row.operator("bim.get_cursor_location", text="", icon="TRACKER")
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(props, "map_coordinates", text=f"Map ({GeoreferenceData.data['map_unit_symbol']})")
|
||||
|
||||
@@ -101,9 +101,9 @@ class LibraryReferencesData:
|
||||
results.append(
|
||||
{
|
||||
"id": library.id(),
|
||||
"identification": library.ItemReference
|
||||
if tool.Ifc.get_schema() == "IFC2X3"
|
||||
else library.Identification,
|
||||
"identification": (
|
||||
library.ItemReference if tool.Ifc.get_schema() == "IFC2X3" else library.Identification
|
||||
),
|
||||
"name": library.Name or "Unnamed",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -34,10 +34,12 @@ classes = (
|
||||
ui.BIM_PT_solar,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.radiance_exporter_properties = bpy.props.PointerProperty(type=prop.RadianceExporterProperties)
|
||||
bpy.types.Scene.BIMSolarProperties = bpy.props.PointerProperty(type=prop.BIMSolarProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.radiance_exporter_properties
|
||||
del bpy.types.Scene.BIMSolarProperties
|
||||
|
||||
@@ -87,7 +87,6 @@ class SolarDecorator:
|
||||
blf.position(self.font_id, co[0], co[1], 0)
|
||||
blf.draw(self.font_id, line)
|
||||
|
||||
|
||||
def draw_geometry(self, context):
|
||||
self.addon_prefs = tool.Blender.get_addon_preferences()
|
||||
decorator_color_special = self.addon_prefs.decorator_color_special
|
||||
@@ -153,8 +152,7 @@ class SolarDecorator:
|
||||
analemma_verts = sun_position.sun_calc.calc_analemma(context, h)
|
||||
coords.extend([origin @ v for v in analemma_verts])
|
||||
for i in range(len(analemma_verts) - 1):
|
||||
indices.append((coord_offset + i,
|
||||
coord_offset + i+1))
|
||||
indices.append((coord_offset + i, coord_offset + i + 1))
|
||||
coord_offset += len(analemma_verts)
|
||||
|
||||
self.draw_batch("LINES", coords, decorator_color_special, indices)
|
||||
|
||||
@@ -37,12 +37,13 @@ from blenderbim.bim.module.light.data import SolarData
|
||||
|
||||
ifc_materials = []
|
||||
|
||||
|
||||
class ExportOBJ(bpy.types.Operator):
|
||||
"""Exports the IFC File to OBJ"""
|
||||
|
||||
bl_idname = "export_scene.radiance"
|
||||
bl_label = "Export"
|
||||
bl_description = "Export the IFC to OBJ"
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
@@ -52,12 +53,11 @@ class ExportOBJ(bpy.types.Operator):
|
||||
|
||||
context.scene.radiance_exporter_properties.is_exporting = True
|
||||
|
||||
|
||||
# Conversion from IFC to OBJ
|
||||
# Settings for obj
|
||||
settings = ifcopenshell.geom.settings()
|
||||
serializer_settings = ifcopenshell.geom.serializer_settings()
|
||||
|
||||
|
||||
settings.set("dimensionality", ifcopenshell.ifcopenshell_wrapper.SURFACES_AND_SOLIDS)
|
||||
settings.set("apply-default-materials", True)
|
||||
serializer_settings.set("use-element-guids", True)
|
||||
@@ -65,17 +65,17 @@ class ExportOBJ(bpy.types.Operator):
|
||||
|
||||
if should_load_from_memory:
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
|
||||
else:
|
||||
ifc_file_path = context.scene.radiance_exporter_properties.ifc_file
|
||||
ifc_file = ifcopenshell.open(ifc_file_path)
|
||||
|
||||
|
||||
# for material in ifc_file.by_type("IfcMaterial"):
|
||||
# self.report({'INFO'}, f"Material: {material.Name}, ID: {material.id()}")
|
||||
|
||||
obj_file_path = os.path.join(output_dir, "model.obj")
|
||||
mtl_file_path = os.path.join(output_dir, "model.mtl")
|
||||
|
||||
|
||||
serialiser = ifcopenshell.geom.serializers.obj(obj_file_path, mtl_file_path, settings, serializer_settings)
|
||||
serialiser.setFile(ifc_file)
|
||||
serialiser.setUnitNameAndMagnitude("METER", 1.0)
|
||||
@@ -84,10 +84,10 @@ class ExportOBJ(bpy.types.Operator):
|
||||
if ifc_file.schema in ("IFC2X3", "IFC4"):
|
||||
elements = ifc_file.by_type("IfcElement") + ifc_file.by_type("IfcProxy")
|
||||
else:
|
||||
elements = ifc_file.by_type("IfcElement")
|
||||
elements = ifc_file.by_type("IfcElement")
|
||||
|
||||
elements = [e for e in elements if not e.is_a("IfcFeatureElement") or e.is_a("IfcSurfaceFeature")]
|
||||
|
||||
elements = [e for e in elements if not e.is_a("IfcFeatureElement") or e.is_a("IfcSurfaceFeature")]
|
||||
|
||||
iterator = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count(), include=elements)
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
@@ -102,30 +102,31 @@ class ExportOBJ(bpy.types.Operator):
|
||||
# print(material, dir(material))
|
||||
# print(material.name)
|
||||
ifc_materials.append(material.name)
|
||||
|
||||
|
||||
serialiser.write(shape)
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
|
||||
serialiser.finalize()
|
||||
context.scene.radiance_exporter_properties.is_exporting = False
|
||||
|
||||
self.report({'INFO'}, "Exported OBJ file to: {}".format(obj_file_path))
|
||||
self.report({"INFO"}, "Exported OBJ file to: {}".format(obj_file_path))
|
||||
|
||||
return {'FINISHED'}
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RadianceRender(bpy.types.Operator):
|
||||
"""Radiance Rendering"""
|
||||
|
||||
bl_idname = "render_scene.radiance"
|
||||
bl_label = "Render"
|
||||
bl_description = "Renders the scene using Radiance"
|
||||
|
||||
def execute(self, context):
|
||||
if pr is None:
|
||||
self.report({'ERROR'}, "PyRadiance is not available. Cannot perform rendering.")
|
||||
return {'CANCELLED'}
|
||||
|
||||
self.report({"ERROR"}, "PyRadiance is not available. Cannot perform rendering.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Get the resolution from the user input
|
||||
props = context.scene.radiance_exporter_properties
|
||||
resolution_x, resolution_y = props.radiance_resolution_x, props.radiance_resolution_y
|
||||
@@ -138,31 +139,29 @@ class RadianceRender(bpy.types.Operator):
|
||||
|
||||
obj_file_path = os.path.join(output_dir, "model.obj")
|
||||
|
||||
|
||||
camera = self.get_active_camera(context)
|
||||
if camera is None:
|
||||
self.report({'ERROR'}, "No active camera found in the scene. Please add a camera and set it as active.")
|
||||
return {'CANCELLED'}
|
||||
self.report({"ERROR"}, "No active camera found in the scene. Please add a camera and set it as active.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Get camera position and direction
|
||||
camera_position, camera_direction = self.get_camera_data(camera)
|
||||
|
||||
# Material processing
|
||||
# style = []
|
||||
|
||||
|
||||
# with open(obj_file_path, "r") as obj_file:
|
||||
# for line in obj_file:
|
||||
# if line.startswith("usemtl"):
|
||||
# l = line.strip().split(" ")
|
||||
# style.append(l[1])
|
||||
|
||||
|
||||
# Check if json file is empty or not
|
||||
props = context.scene.radiance_exporter_properties
|
||||
|
||||
if props.use_json_file:
|
||||
# Load data from JSON file
|
||||
with open(props.json_file, 'r') as file:
|
||||
with open(props.json_file, "r") as file:
|
||||
data = json.load(file)
|
||||
else:
|
||||
# Use in-UI material mappings
|
||||
@@ -170,7 +169,6 @@ class RadianceRender(bpy.types.Operator):
|
||||
|
||||
print(data)
|
||||
|
||||
|
||||
# Create materials.rad file
|
||||
materials_file = os.path.join(output_dir, "materials.rad")
|
||||
with open(materials_file, "w") as file:
|
||||
@@ -190,19 +188,19 @@ class RadianceRender(bpy.types.Operator):
|
||||
# print(i)
|
||||
# file.write("inherit alias " + i + " " + data.get(i, "white") + "\n")
|
||||
|
||||
self.report({'INFO'}, "Exported Materials Rad file to: {}".format(materials_file))
|
||||
self.report({"INFO"}, "Exported Materials Rad file to: {}".format(materials_file))
|
||||
|
||||
# Run obj2mesh
|
||||
rtm_file_path = os.path.join(output_dir, "model.rtm")
|
||||
mesh_file_path = save_obj2mesh_output(obj_file_path, rtm_file_path, matfiles=[materials_file])
|
||||
mesh_file_path = save_obj2mesh_output(obj_file_path, rtm_file_path, matfiles=[materials_file])
|
||||
# subprocess.run(["obj2mesh", "-a", materials_file, obj_file_path, rtm_file_path])
|
||||
self.report({'INFO'}, "obj2mesh output: {}".format(mesh_file_path))
|
||||
self.report({"INFO"}, "obj2mesh output: {}".format(mesh_file_path))
|
||||
scene_file = os.path.join(output_dir, "scene.rad")
|
||||
with open(scene_file, "w") as file:
|
||||
# file.write("void mesh model\n1 " + rtm_file_path + "\n0\n0\n")
|
||||
file.write('void mesh model\n1 "' + rtm_file_path + '"\n0\n0\n')
|
||||
|
||||
self.report({'INFO'}, "Exported Scene file to: {}".format(scene_file))
|
||||
self.report({"INFO"}, "Exported Scene file to: {}".format(scene_file))
|
||||
|
||||
# Py Radiance Rendering code
|
||||
scene = pr.Scene("ascene")
|
||||
@@ -216,26 +214,31 @@ class RadianceRender(bpy.types.Operator):
|
||||
aview = pr.View(position=camera_position, direction=camera_direction)
|
||||
scene.add_view(aview)
|
||||
|
||||
image = pr.render(scene, ambbounce=1, resolution=(resolution_x, resolution_y),
|
||||
quality=quality, detail=detail, variability=variability)
|
||||
|
||||
image = pr.render(
|
||||
scene,
|
||||
ambbounce=1,
|
||||
resolution=(resolution_x, resolution_y),
|
||||
quality=quality,
|
||||
detail=detail,
|
||||
variability=variability,
|
||||
)
|
||||
|
||||
output_path = os.path.join(output_dir, f"{output_file_name}.{output_file_format.lower()}")
|
||||
|
||||
if output_file_format == 'HDR':
|
||||
if output_file_format == "HDR":
|
||||
with open(output_path, "wb") as wtr:
|
||||
wtr.write(image)
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
self.report({'INFO'}, "Radiance rendering completed. Output: {}".format(output_path))
|
||||
return {'FINISHED'}
|
||||
|
||||
self.report({"INFO"}, "Radiance rendering completed. Output: {}".format(output_path))
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_active_camera(self, context):
|
||||
if context.scene.camera:
|
||||
return context.scene.camera
|
||||
for obj in context.scene.objects:
|
||||
if obj.type == 'CAMERA':
|
||||
if obj.type == "CAMERA":
|
||||
return obj
|
||||
return None
|
||||
|
||||
@@ -256,10 +259,11 @@ class RadianceRender(bpy.types.Operator):
|
||||
resolution_y = props.radiance_resolution_y
|
||||
return resolution_x, resolution_y
|
||||
|
||||
|
||||
def save_obj2mesh_output(inp: Union[bytes, str, Path], output_file: str, **kwargs):
|
||||
output_bytes = pr.obj2mesh(inp, **kwargs)
|
||||
|
||||
with open(output_file, 'wb') as f:
|
||||
with open(output_file, "wb") as f:
|
||||
f.write(output_bytes)
|
||||
return output_file
|
||||
|
||||
@@ -333,6 +337,7 @@ class ViewFromSun(bpy.types.Operator):
|
||||
props.hour = props.hour # Just to refresh camera position
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RefreshIFCMaterials(bpy.types.Operator):
|
||||
bl_idname = "bim.refresh_ifc_materials"
|
||||
bl_label = "Refresh IFC Materials"
|
||||
@@ -341,7 +346,7 @@ class RefreshIFCMaterials(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
props = context.scene.radiance_exporter_properties
|
||||
ifc_file = tool.Ifc.get() if props.should_load_from_memory else ifcopenshell.open(props.ifc_file)
|
||||
|
||||
|
||||
for style in ifc_file.by_type("IfcSurfaceStyle"):
|
||||
for render_item in style.Styles:
|
||||
if render_item.is_a("IfcSurfaceStyleRendering"):
|
||||
@@ -349,5 +354,5 @@ class RefreshIFCMaterials(bpy.types.Operator):
|
||||
style_name = style.Name or f"Unnamed Style {render_item.id()}"
|
||||
if not props.get_material_mapping(style_name):
|
||||
props.add_material_mapping(style_id, style_name)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -170,110 +170,88 @@ class RadianceExporterProperties(PropertyGroup):
|
||||
|
||||
def get_mappings_dict(self):
|
||||
return {item["style_id"]: item["radiance_material"] for item in self.material_mappings}
|
||||
|
||||
|
||||
use_json_file: BoolProperty(
|
||||
name="Upload JSON",
|
||||
description="Toggle between uploading a JSON file and using in-UI material mapping",
|
||||
default=False
|
||||
)
|
||||
|
||||
is_exporting: bpy.props.BoolProperty(
|
||||
name="Is Exporting",
|
||||
description="Whether the OBJ export is in progress",
|
||||
default=False
|
||||
default=False,
|
||||
)
|
||||
|
||||
material_mappings: bpy.props.CollectionProperty(
|
||||
type= bpy.types.PropertyGroup,
|
||||
name="Material Mappings"
|
||||
is_exporting: bpy.props.BoolProperty(
|
||||
name="Is Exporting", description="Whether the OBJ export is in progress", default=False
|
||||
)
|
||||
|
||||
material_mappings: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup, name="Material Mappings")
|
||||
|
||||
should_load_from_memory: BoolProperty(
|
||||
name="Load from Memory",
|
||||
default=False,
|
||||
default=False,
|
||||
)
|
||||
|
||||
radiance_resolution_x: IntProperty(
|
||||
name="X",
|
||||
description="Horizontal resolution of the output image",
|
||||
default=1920,
|
||||
min=1
|
||||
name="X", description="Horizontal resolution of the output image", default=1920, min=1
|
||||
)
|
||||
radiance_resolution_y: IntProperty(
|
||||
name="Y",
|
||||
description="Vertical resolution of the output image",
|
||||
default=1080,
|
||||
min=1
|
||||
name="Y", description="Vertical resolution of the output image", default=1080, min=1
|
||||
)
|
||||
output_dir: StringProperty(
|
||||
name="Output Directory",
|
||||
description="Directory to output Radiance files",
|
||||
default="",
|
||||
subtype="DIR_PATH",
|
||||
update=lambda self, context: self.update_output_dir(context)
|
||||
update=lambda self, context: self.update_output_dir(context),
|
||||
)
|
||||
ifc_file: StringProperty(
|
||||
name="IFC File",
|
||||
description="Path to the IFC file",
|
||||
default="",
|
||||
subtype="FILE_PATH",
|
||||
update=lambda self, context: self.update_ifc_file(context)
|
||||
update=lambda self, context: self.update_ifc_file(context),
|
||||
)
|
||||
json_file: StringProperty(
|
||||
name="JSON File",
|
||||
description="Path to the JSON file",
|
||||
default="",
|
||||
subtype="FILE_PATH",
|
||||
update=lambda self, context: self.update_json_file(context)
|
||||
update=lambda self, context: self.update_json_file(context),
|
||||
)
|
||||
|
||||
|
||||
radiance_quality: EnumProperty(
|
||||
name="Quality",
|
||||
description="Radiance rendering quality",
|
||||
items=[
|
||||
('LOW', "Low", "Low quality"),
|
||||
('MEDIUM', "Medium", "Medium quality"),
|
||||
('HIGH', "High", "High quality")
|
||||
],
|
||||
default='MEDIUM'
|
||||
items=[("LOW", "Low", "Low quality"), ("MEDIUM", "Medium", "Medium quality"), ("HIGH", "High", "High quality")],
|
||||
default="MEDIUM",
|
||||
)
|
||||
radiance_detail: EnumProperty(
|
||||
name="Detail",
|
||||
description="Radiance rendering detail",
|
||||
items=[
|
||||
('LOW', "Low", "Low detail"),
|
||||
('MEDIUM', "Medium", "Medium detail"),
|
||||
('HIGH', "High", "High detail")
|
||||
],
|
||||
default='MEDIUM'
|
||||
items=[("LOW", "Low", "Low detail"), ("MEDIUM", "Medium", "Medium detail"), ("HIGH", "High", "High detail")],
|
||||
default="MEDIUM",
|
||||
)
|
||||
radiance_variability: EnumProperty(
|
||||
name="Variability",
|
||||
description="Radiance rendering variability",
|
||||
items=[
|
||||
('LOW', "Low", "Low variability"),
|
||||
('MEDIUM', "Medium", "Medium variability"),
|
||||
('HIGH', "High", "High variability")
|
||||
("LOW", "Low", "Low variability"),
|
||||
("MEDIUM", "Medium", "Medium variability"),
|
||||
("HIGH", "High", "High variability"),
|
||||
],
|
||||
default="MEDIUM",
|
||||
)
|
||||
output_file_name: StringProperty(
|
||||
name="Output File Name",
|
||||
description="Name of the output image file (without extension)",
|
||||
default="render"
|
||||
name="Output File Name", description="Name of the output image file (without extension)", default="render"
|
||||
)
|
||||
|
||||
|
||||
output_file_format: EnumProperty(
|
||||
name="Output File Format",
|
||||
description="Format of the output image file",
|
||||
items=[
|
||||
('HDR', "HDR", "High Dynamic Range"),
|
||||
("HDR", "HDR", "High Dynamic Range"),
|
||||
],
|
||||
default='HDR'
|
||||
default="HDR",
|
||||
)
|
||||
|
||||
|
||||
|
||||
class BIMSolarProperties(PropertyGroup):
|
||||
sites: EnumProperty(items=get_sites, name="Sites")
|
||||
latitude: FloatProperty(name="Latitude", min=-90, max=90, update=update_latlong)
|
||||
|
||||
@@ -26,11 +26,12 @@ sun_position = tool.Blender.get_sun_position_addon()
|
||||
|
||||
class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
"""Creates a Panel in the render properties window"""
|
||||
|
||||
bl_label = "Radiance Exporter"
|
||||
bl_idname = "BIM_PT_radiance_exporter"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_lighting"
|
||||
|
||||
@@ -43,15 +44,13 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
if tool.Ifc.get():
|
||||
row = self.layout.row()
|
||||
row.prop(props, "should_load_from_memory")
|
||||
|
||||
|
||||
if not tool.Ifc.get() or not props.should_load_from_memory:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "ifc_file")
|
||||
# row.operator("bim.select_ifctester_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "output_dir")
|
||||
|
||||
|
||||
row = layout.row()
|
||||
layout.prop(props, "use_json_file")
|
||||
@@ -69,22 +68,21 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
|
||||
layout.operator("bim.refresh_ifc_materials")
|
||||
|
||||
|
||||
layout.separator()
|
||||
row = layout.row()
|
||||
row.label(text="Resolution")
|
||||
row.prop(props, "radiance_resolution_x", text="X")
|
||||
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="")
|
||||
row.prop(props, "radiance_resolution_y", text="Y")
|
||||
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "radiance_quality")
|
||||
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "radiance_detail")
|
||||
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "radiance_variability")
|
||||
|
||||
@@ -92,7 +90,7 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "output_file_name")
|
||||
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "output_file_format")
|
||||
layout.separator()
|
||||
@@ -104,12 +102,14 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
|
||||
row.operator("render_scene.radiance", text="Radiance Render")
|
||||
row.enabled = not props.is_exporting
|
||||
|
||||
|
||||
class BIM_PT_solar(bpy.types.Panel):
|
||||
"""Creates a Panel in the render properties window"""
|
||||
|
||||
bl_label = "Solar Access / Shadow"
|
||||
bl_idname = "BIM_PT_solar"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_solar_analysis"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
@@ -135,7 +135,7 @@ class BIM_PT_solar(bpy.types.Panel):
|
||||
sun_props = context.scene.sun_pos_properties
|
||||
|
||||
row = self.layout.row()
|
||||
row.prop(sun_props, "coordinates", icon='URL')
|
||||
row.prop(sun_props, "coordinates", icon="URL")
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "latitude")
|
||||
row.prop(props, "longitude")
|
||||
@@ -146,20 +146,24 @@ class BIM_PT_solar(bpy.types.Panel):
|
||||
row.operator("bim.import_true_north", icon="IMPORT", text="")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "month", text={
|
||||
1: "January",
|
||||
2: "February",
|
||||
3: "March",
|
||||
4: "April",
|
||||
5: "May",
|
||||
6: "June",
|
||||
7: "July",
|
||||
8: "August",
|
||||
9: "September",
|
||||
10: "October",
|
||||
11: "November",
|
||||
12: "December",
|
||||
}[props.month])
|
||||
row.prop(
|
||||
props,
|
||||
"month",
|
||||
text={
|
||||
1: "January",
|
||||
2: "February",
|
||||
3: "March",
|
||||
4: "April",
|
||||
5: "May",
|
||||
6: "June",
|
||||
7: "July",
|
||||
8: "August",
|
||||
9: "September",
|
||||
10: "October",
|
||||
11: "November",
|
||||
12: "December",
|
||||
}[props.month],
|
||||
)
|
||||
row.prop(props, "day")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -258,7 +258,9 @@ class ObjectMaterialData:
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
else:
|
||||
precision = None
|
||||
formatted_thickness = format_distance(total_thickness, precision=precision, suppress_zero_inches=True, in_unit_length = True)
|
||||
formatted_thickness = format_distance(
|
||||
total_thickness, precision=precision, suppress_zero_inches=True, in_unit_length=True
|
||||
)
|
||||
data["name"] = f"{formatted_thickness} - {data['name']}"
|
||||
if item.is_a("IfcMaterial"):
|
||||
data["material"] = item.Name or "Unnamed"
|
||||
|
||||
@@ -120,6 +120,11 @@ def get_contexts(self, context):
|
||||
|
||||
|
||||
def update_material_name(self: "Material", context: bpy.types.Context) -> None:
|
||||
# No need to update anything if it's either category
|
||||
# or ifc_definition_id is not yet set
|
||||
# (which occurs when Material still in the process of creation).
|
||||
if self.is_category or not self.ifc_definition_id:
|
||||
return
|
||||
ifc_file = tool.Ifc.get()
|
||||
name = self.name
|
||||
material = ifc_file.by_id(self.ifc_definition_id)
|
||||
|
||||
@@ -223,7 +223,7 @@ class BIM_PT_object_material(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Profiles Available")
|
||||
row.operator("bim.add_profile_def", icon="ADD", text="")
|
||||
else:
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
if ObjectMaterialData.data["set_item_name"] == "profile":
|
||||
prop_with_search(row, self.mprops, "profiles", icon="ITALIC", text="")
|
||||
@@ -235,7 +235,10 @@ class BIM_PT_object_material(Panel):
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="----- Exterior -----")
|
||||
for index, set_item in enumerate(ObjectMaterialData.data["set_items"]):
|
||||
if len(self.props.material_set_item_profile_attributes) and self.props.active_material_set_item_id == set_item["id"]:
|
||||
if (
|
||||
len(self.props.material_set_item_profile_attributes)
|
||||
and self.props.active_material_set_item_id == set_item["id"]
|
||||
):
|
||||
self.draw_editable_set_item_profile_ui(set_item)
|
||||
elif self.props.active_material_set_item_id == set_item["id"]:
|
||||
self.draw_editable_set_item_ui(set_item)
|
||||
@@ -341,7 +344,9 @@ class BIM_PT_object_material(Panel):
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
else:
|
||||
precision = None
|
||||
formatted_thickness = format_distance(total_thickness, precision=precision, suppress_zero_inches=True, in_unit_length = True)
|
||||
formatted_thickness = format_distance(
|
||||
total_thickness, precision=precision, suppress_zero_inches=True, in_unit_length=True
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"Total Thickness: {formatted_thickness}")
|
||||
|
||||
|
||||
@@ -143,7 +143,6 @@ classes = (
|
||||
stair.EnableEditingStair,
|
||||
stair.RemoveStair,
|
||||
pie.OpenPieClass,
|
||||
pie.PieUpdateContainer,
|
||||
pie.PieAddOpening,
|
||||
pie.VIEW3D_MT_PIE_bim,
|
||||
pie.VIEW3D_MT_PIE_bim_class,
|
||||
|
||||
@@ -84,9 +84,9 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMArrayProperties
|
||||
|
||||
|
||||
relating_obj = props.relating_array_object
|
||||
|
||||
|
||||
if relating_obj:
|
||||
element = tool.Ifc.get_entity(relating_obj)
|
||||
parent_globalid = ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Parent")
|
||||
@@ -102,9 +102,9 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props.use_local_space = data.get("use_local_space", False)
|
||||
props.sync_children = data.get("sync_children", False)
|
||||
props.method = data.get("method", "OFFSET")
|
||||
|
||||
|
||||
props.is_editing = self.item
|
||||
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -149,9 +149,9 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Blender.Modifier.Array.set_children_lock_state(element, self.item, True)
|
||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||
|
||||
#clears the relating_array_object so it doesn't show again next time
|
||||
# clears the relating_array_object so it doesn't show again next time
|
||||
props.relating_array_object = None
|
||||
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -285,8 +285,11 @@ class SelectAllArrayObjects(bpy.types.Operator):
|
||||
|
||||
array_objects = tool.Blender.Modifier.Array.get_all_objects(parent_element)
|
||||
tool.Blender.set_objects_selection(
|
||||
context, active_object=array_objects[0], selected_objects=array_objects, clear_previous_selection=False
|
||||
)
|
||||
context,
|
||||
active_object=array_objects[0],
|
||||
selected_objects=array_objects,
|
||||
clear_previous_selection=False,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.covering as core
|
||||
|
||||
@@ -31,9 +32,7 @@ class AddInstanceFlooringCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operato
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
return relating_type == "FLOORING"
|
||||
return tool.Covering.covering_poll_relating_type_check(cls, context, "FLOORING")
|
||||
|
||||
def _execute(self, context):
|
||||
try:
|
||||
@@ -50,9 +49,7 @@ class AddInstanceCeilingCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operator
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
return relating_type == "CEILING"
|
||||
return tool.Covering.covering_poll_relating_type_check(cls, context, "CEILING")
|
||||
|
||||
def _execute(self, context):
|
||||
try:
|
||||
@@ -69,8 +66,10 @@ class RegenSelectedCoveringObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
return element and element.is_a("IfcCovering")
|
||||
if (obj := context.active_object) and (element := tool.Ifc.get_entity(obj)) and element.is_a("IfcCovering"):
|
||||
return True
|
||||
cls.poll_message_set("IfcCovering must be selected.")
|
||||
return False
|
||||
|
||||
def _execute(self, context):
|
||||
try:
|
||||
@@ -89,11 +88,7 @@ class AddInstanceFlooringCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operato
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
if element and element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2":
|
||||
return context.selected_objects and relating_type == "FLOORING"
|
||||
return tool.Covering.covering_poll_wall_selected(cls, context, "FLOORING")
|
||||
|
||||
def _execute(self, context):
|
||||
# This only works based on a 2D plan only considering the standard
|
||||
@@ -117,11 +112,7 @@ class AddInstanceCeilingCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
if element and element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2":
|
||||
return context.selected_objects and relating_type == "CEILING"
|
||||
return tool.Covering.covering_poll_wall_selected(cls, context, "CEILING")
|
||||
|
||||
def _execute(self, context):
|
||||
# This only works based on a 2D plan only considering the standard
|
||||
|
||||
@@ -50,10 +50,11 @@ class AuthoringData:
|
||||
cls.props = bpy.context.scene.BIMModelProperties
|
||||
if ifc_element_type:
|
||||
cls.ifc_element_type = None if ifc_element_type == "all" else ifc_element_type
|
||||
cls.data["default_container"] = cls.default_container()
|
||||
cls.data["ifc_element_type"] = cls.ifc_element_type
|
||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||
cls.data["relating_type_id"] = cls.relating_type_id() # only after .ifc_classes()
|
||||
cls.data["predefined_type"] = cls.predefined_type() # only after .relating_type_id()
|
||||
cls.data["predefined_type"] = cls.predefined_type() # only after .relating_type_id()
|
||||
cls.data["type_class"] = cls.type_class()
|
||||
|
||||
# only after .type_class()
|
||||
@@ -74,6 +75,15 @@ class AuthoringData:
|
||||
cls.data["boundary_class"] = cls.boundary_class()
|
||||
cls.data["selected_material_usages"] = cls.selected_material_usages()
|
||||
|
||||
@classmethod
|
||||
def default_container(cls) -> str | None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
if props.default_container:
|
||||
try:
|
||||
return tool.Ifc.get().by_id(props.default_container).Name
|
||||
except:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def boundary_class(cls):
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcRelSpaceBoundary")
|
||||
|
||||
@@ -541,7 +541,9 @@ class MEPGenerator:
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
|
||||
rel = ifcopenshell.api.run("material.assign_material", ifc_file, products=[element], type="IfcMaterialProfileSet")
|
||||
rel = ifcopenshell.api.run(
|
||||
"material.assign_material", ifc_file, products=[element], type="IfcMaterialProfileSet"
|
||||
)
|
||||
profile_set = rel.RelatingMaterial
|
||||
material_profile = ifcopenshell.api.run(
|
||||
"material.add_profile", ifc_file, profile_set=profile_set, material=material
|
||||
|
||||
@@ -63,28 +63,6 @@ class PieAddOpening(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class PieUpdateContainer(bpy.types.Operator):
|
||||
bl_idname = "bim.pie_update_container"
|
||||
bl_label = "Update Spatial Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
for obj in context.selected_objects:
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
for collection in obj.users_collection:
|
||||
spatial_obj = collection.BIMCollectionProperties.obj
|
||||
if spatial_obj and spatial_obj.BIMObjectProperties.ifc_definition_id:
|
||||
blenderbim.core.spatial.assign_container(
|
||||
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=spatial_obj, element_obj=obj
|
||||
)
|
||||
break
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class VIEW3D_MT_PIE_bim(bpy.types.Menu):
|
||||
bl_label = "Geometry"
|
||||
|
||||
@@ -93,7 +71,6 @@ class VIEW3D_MT_PIE_bim(bpy.types.Menu):
|
||||
pie.operator("bim.edit_object_placement")
|
||||
pie.operator("bim.update_representation").ifc_representation_class = ""
|
||||
pie.operator("bim.pie_add_opening")
|
||||
pie.operator("bim.pie_update_container")
|
||||
pie.operator("bim.open_pie_class", text="Assign IFC Class")
|
||||
pie.operator("bim.aggregate_assign_object", text="Assign Aggregation")
|
||||
pie.operator("bim.aggregate_unassign_object", text="Unassign Aggregation")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user