mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-24 20:50:02 +00:00
Merge branch 'v0.8.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.8.0
This commit is contained in:
@@ -32,7 +32,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/bcf &&
|
cd src/bcf &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
- '.github/workflows/ci-blenderbim-daily.yml'
|
- '.github/workflows/ci-blenderbim-daily.yml'
|
||||||
- 'src/blenderbim/**'
|
- 'src/blenderbim/**'
|
||||||
- 'src/ifcopenshell-python/ifcopenshell/**'
|
- 'src/ifcopenshell-python/ifcopenshell/**'
|
||||||
- 'src/bcf/src/bcf/**'
|
- 'src/bcf/bcf/**'
|
||||||
- 'src/ifcclash/ifcclash/**'
|
- 'src/ifcclash/ifcclash/**'
|
||||||
- 'src/ifccobie/**'
|
- 'src/ifccobie/**'
|
||||||
- 'src/ifcdiff/**'
|
- 'src/ifcdiff/**'
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
name: ci-blenderbim
|
name: ci-blenderbim
|
||||||
|
|
||||||
|
# Differences from ci-blenderbim-daily.yml:
|
||||||
|
# - make has IS_STABLE=TRUE
|
||||||
|
# - action is never triggered and executed only manually
|
||||||
|
# - doesn't add a current date to the release and tag
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -48,15 +53,20 @@ jobs:
|
|||||||
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
run: echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
|
||||||
- name: Compile
|
- name: Compile
|
||||||
run: |
|
run: |
|
||||||
cp -r src/blenderbim src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
cd src/blenderbim &&
|
||||||
cd src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
|
||||||
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }} IS_STABLE=TRUE
|
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }} IS_STABLE=TRUE
|
||||||
|
- name: Find zip file name
|
||||||
|
id: find_zip
|
||||||
|
run: |
|
||||||
|
filepath=$(ls src/blenderbim/dist/blenderbim_*.zip)
|
||||||
|
echo "filepath=$filepath" >> $GITHUB_OUTPUT
|
||||||
|
echo "filename=$(basename $filepath)" >> $GITHUB_OUTPUT
|
||||||
- name: Upload zip file to release
|
- name: Upload zip file to release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
file: src/blenderbim_${{ matrix.config.short_name }}_${{ matrix.pyver }}/dist/blenderbim-${{steps.version.outputs.version}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
file: ${{ steps.find_zip.outputs.filepath }}
|
||||||
asset_name: blenderbim-${{steps.version.outputs.version}}-${{ matrix.pyver }}-${{ matrix.config.short_name }}.zip
|
asset_name: ${{ steps.find_zip.outputs.filename }}
|
||||||
release_name: "blenderbim-${{steps.version.outputs.version}}"
|
release_name: "blenderbim-${{steps.version.outputs.version}}"
|
||||||
tag: "blenderbim-${{steps.version.outputs.version}}"
|
tag: "blenderbim-${{steps.version.outputs.version}}"
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/bsdd &&
|
cd src/bsdd &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifc4d &&
|
cd src/ifc4d &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifc5d &&
|
cd src/ifc5d &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifccityjson &&
|
cd src/ifccityjson &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifcclash &&
|
cd src/ifcclash &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifccsv &&
|
cd src/ifccsv &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifcdiff &&
|
cd src/ifcdiff &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifcfm &&
|
cd src/ifcfm &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
pip install build
|
pip install build
|
||||||
cp -r src/ifcopenshell-python src/ifcopenshell_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
cp -r src/ifcopenshell-python src/ifcopenshell_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||||
cd src/ifcopenshell_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
cd src/ifcopenshell_${{ matrix.config.short_name }}_${{ matrix.pyver }} &&
|
||||||
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }}
|
make dist PLATFORM=${{ matrix.config.short_name }} PYVERSION=${{ matrix.pyver }} IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifcpatch &&
|
cd src/ifcpatch &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pip install build
|
pip install build
|
||||||
cd src/ifctester &&
|
cd src/ifctester &&
|
||||||
make dist
|
make dist IS_STABLE=TRUE
|
||||||
- name: Publish a Python distribution to PyPI
|
- name: Publish a Python distribution to PyPI
|
||||||
uses: ortega2247/pypi-upload-action@master
|
uses: ortega2247/pypi-upload-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
+46
-13
@@ -114,19 +114,18 @@ endif
|
|||||||
|
|
||||||
mkdir -p build/wheels
|
mkdir -p build/wheels
|
||||||
# Provides IfcOpenShell Python functionality
|
# Provides IfcOpenShell Python functionality
|
||||||
cd ../ifcopenshell-python && make dist PLATFORM=$(PLATFORM)64 PYVERSION=$(PYVERSION) && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifcopenshell-python && make dist PLATFORM=$(PLATFORM)64 PYVERSION=$(PYVERSION) && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../bcf && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../bcf && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifcclash && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifcclash && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifcbimtester && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifctester && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifctester && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifcfm && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifcfm && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../bsdd && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../bsdd && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifcdiff && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifcdiff && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifccsv && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifccsv && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifcpatch && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifcpatch && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifc4d && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifc4d && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifc5d && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifc5d && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
cd ../ifccityjson && make dist && mv dist/*.whl ../blenderbim/build/wheels/
|
||||||
cd ../ifccityjson && make dist && cp dist/*.whl ../blenderbim/build/wheels/
|
|
||||||
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download GitPython --dest=./wheels
|
cd build && . env/$(VENV_ACTIVATE) && $(PIP) download GitPython --dest=./wheels
|
||||||
# Provides audio playback for costing
|
# Provides audio playback for costing
|
||||||
# This is a REALLY IMPORTANT feature
|
# This is a REALLY IMPORTANT feature
|
||||||
@@ -252,9 +251,22 @@ endif
|
|||||||
$(SED) "s/os-arch/$(BLENDER_PLATFORM)/" build/blenderbim/blender_manifest.toml
|
$(SED) "s/os-arch/$(BLENDER_PLATFORM)/" build/blenderbim/blender_manifest.toml
|
||||||
|
|
||||||
# Provides BlenderBIM Add-on functionality
|
# Provides BlenderBIM Add-on functionality
|
||||||
|
ifeq ($(IS_STABLE), TRUE)
|
||||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
||||||
|
else
|
||||||
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)a$(VERSION_DATE)"/' build/pyproject.toml
|
||||||
|
endif
|
||||||
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
|
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
|
||||||
cp build/dist/*.whl build/wheels/
|
cp build/dist/*.whl build/wheels/
|
||||||
|
|
||||||
|
# Safeguard for non-wheel dependencies slipping in.
|
||||||
|
wheels=$$(find build/wheels/* ! -name "*.whl"); \
|
||||||
|
if [ -n "$$wheels" ]; then \
|
||||||
|
echo "Found non-wheel dependencies:"; \
|
||||||
|
echo "$$wheels"; \
|
||||||
|
echo "Error: non-wheel dependencies are not supported by Blender!"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
mv build/wheels/*.whl build/blenderbim/wheels/
|
mv build/wheels/*.whl build/blenderbim/wheels/
|
||||||
|
|
||||||
# Ugly patch to accomodate MacOS universal builds not being recognized by Blender.
|
# Ugly patch to accomodate MacOS universal builds not being recognized by Blender.
|
||||||
@@ -297,6 +309,27 @@ ifeq ($(PLATFORM), macos)
|
|||||||
mv "$$prev_whl_name" "$$whl_name";
|
mv "$$prev_whl_name" "$$whl_name";
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Safeguard for unhandled universal files.
|
||||||
|
wheels=$$(find build/blenderbim/wheels/*universal2*.whl); \
|
||||||
|
if [ -n "$$wheels" ]; then \
|
||||||
|
echo "Found universal2 wheel files:"; \
|
||||||
|
echo "$$wheels"; \
|
||||||
|
echo "Error: universal2 wheel files are not supported by Blender!"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
ifneq ($(PLATFORM), linux)
|
||||||
|
# Safeguard: in case one of `pip download` will break,
|
||||||
|
# it will produce a linux wheel for non-linux build (our github action machine is using linux).
|
||||||
|
wheels=$$(find build/blenderbim/wheels/*manylinux_*.whl); \
|
||||||
|
if [ -n "$$wheels" ]; then \
|
||||||
|
echo "Found linux wheel files in non-linux build:"; \
|
||||||
|
echo "$$wheels"; \
|
||||||
|
echo "Error: linux wheels are incompatible with build $(PLATFORM)!"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
endif
|
||||||
|
|
||||||
$(PYTHON) scripts/get_wheels.py build/blenderbim/wheels build/blenderbim/blender_manifest.toml
|
$(PYTHON) scripts/get_wheels.py build/blenderbim/wheels build/blenderbim/blender_manifest.toml
|
||||||
rm -rf build/blenderbim/bim/
|
rm -rf build/blenderbim/bim/
|
||||||
rm -rf build/blenderbim/core/
|
rm -rf build/blenderbim/core/
|
||||||
|
|||||||
@@ -27,15 +27,16 @@ import sys
|
|||||||
IN_BLENDER = sys.modules.get("bpy", None)
|
IN_BLENDER = sys.modules.get("bpy", None)
|
||||||
if IN_BLENDER:
|
if IN_BLENDER:
|
||||||
import bpy
|
import bpy
|
||||||
import addon_utils
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import platform
|
import platform
|
||||||
import traceback
|
import traceback
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
import uuid
|
||||||
|
import shutil
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Union, Any
|
from typing import Union, Any, Generator
|
||||||
|
|
||||||
|
|
||||||
last_commit_hash = "8888888"
|
last_commit_hash = "8888888"
|
||||||
@@ -52,19 +53,35 @@ def get_last_commit_hash() -> Union[str, None]:
|
|||||||
|
|
||||||
last_error = None
|
last_error = None
|
||||||
last_actions: deque = deque(maxlen=10)
|
last_actions: deque = deque(maxlen=10)
|
||||||
|
bbim_semver: dict[str, Any] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_bbim_semver():
|
||||||
|
"""Initialize `bbim_semver` dictionary.
|
||||||
|
|
||||||
|
Blender doesn't seem to store a full extension version (only major-minor-patch)
|
||||||
|
in `addon_utils.modules()->bl_info['version']`,
|
||||||
|
therefore we just parse it from .toml.
|
||||||
|
"""
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
toml_path = Path(__file__).parent / "blender_manifest.toml"
|
||||||
|
with open(toml_path, "rb") as f:
|
||||||
|
manifest = tomllib.load(f)
|
||||||
|
semver_pattern = r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
||||||
|
version_str = manifest["version"]
|
||||||
|
re_version = re.match(semver_pattern, version_str)
|
||||||
|
assert re_version
|
||||||
|
global bbim_semver
|
||||||
|
bbim_semver = re_version.groupdict()
|
||||||
|
bbim_semver["version"] = version_str
|
||||||
|
|
||||||
|
|
||||||
|
initialize_bbim_semver()
|
||||||
|
|
||||||
|
|
||||||
def get_debug_info():
|
def get_debug_info():
|
||||||
version = ".".join(
|
bbim_version = bbim_semver["version"]
|
||||||
[
|
|
||||||
str(x)
|
|
||||||
for x in [
|
|
||||||
addon.bl_info.get("version", (-1, -1, -1))
|
|
||||||
for addon in addon_utils.modules()
|
|
||||||
if addon.bl_info["name"] == "BlenderBIM"
|
|
||||||
][0]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"os": platform.system(),
|
"os": platform.system(),
|
||||||
@@ -74,7 +91,7 @@ def get_debug_info():
|
|||||||
"machine": platform.machine(),
|
"machine": platform.machine(),
|
||||||
"processor": platform.processor(),
|
"processor": platform.processor(),
|
||||||
"blender_version": bpy.app.version_string,
|
"blender_version": bpy.app.version_string,
|
||||||
"blenderbim_version": version,
|
"blenderbim_version": bbim_version,
|
||||||
"blenderbim_commit_hash": get_last_commit_hash(),
|
"blenderbim_commit_hash": get_last_commit_hash(),
|
||||||
"last_actions": last_actions,
|
"last_actions": last_actions,
|
||||||
"last_error": last_error,
|
"last_error": last_error,
|
||||||
@@ -92,6 +109,68 @@ def format_debug_info(info: dict):
|
|||||||
return text.strip()
|
return text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def get_binaries(path: Path) -> Generator[Path, None, None]:
|
||||||
|
yield from path.glob("**/*.pyd")
|
||||||
|
yield from path.glob("**/*.dll")
|
||||||
|
# pyradiance is using .so files on windows for some reason.
|
||||||
|
yield from path.glob("**/*.so")
|
||||||
|
|
||||||
|
|
||||||
|
def safe_link_dlls() -> None:
|
||||||
|
# Blender 4.2+ has a problem on Windows for disabling/enabling/reinstalling extensions
|
||||||
|
# with loaded binary dependencies (on Windows you can't remove a binary if it's loaded by some program).
|
||||||
|
# To avoid this issue we temporary hard link dlls to our temp directory on unregister()
|
||||||
|
# (unregister is executed before Blender will try to uninstall dependencies and the issue will arise).
|
||||||
|
# Then, Blender won't have a problem unlinking unloaded dlls as they are still linked somewhere.
|
||||||
|
# On register() we clean up our temp directory with binaries.
|
||||||
|
#
|
||||||
|
# TODO: If user uninstalls BlenderBIM to never use it again, temporary directory won't be cleared.
|
||||||
|
#
|
||||||
|
# See: https://projects.blender.org/blender/blender/issues/125049
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
ext_path = Path(bpy.utils.user_resource("EXTENSIONS"))
|
||||||
|
local_path = ext_path / ".local"
|
||||||
|
|
||||||
|
# We use random hash subfolder as user may try to enable/disable addon multiple times.
|
||||||
|
random_hash = uuid.uuid4().hex[:8]
|
||||||
|
temp_local = ext_path / ".local_temp" / random_hash
|
||||||
|
temp_local.mkdir(parents=True)
|
||||||
|
|
||||||
|
for filepath in get_binaries(local_path):
|
||||||
|
dest_path = temp_local / filepath.relative_to(local_path)
|
||||||
|
dest_path.parent.mkdir(exist_ok=True, parents=True)
|
||||||
|
os.link(filepath, dest_path)
|
||||||
|
|
||||||
|
|
||||||
|
def clean_up_dlls_safe_links() -> None:
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
ext_path = Path(bpy.utils.user_resource("EXTENSIONS"))
|
||||||
|
temp_path = ext_path / ".local_temp"
|
||||||
|
if not temp_path.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
for filepath in get_binaries(temp_path):
|
||||||
|
try:
|
||||||
|
os.unlink(filepath)
|
||||||
|
except PermissionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def is_empty_directory(directory: Path) -> bool:
|
||||||
|
return not any(directory.iterdir())
|
||||||
|
|
||||||
|
def remove_empty_folders(folder: Path) -> None:
|
||||||
|
for subfolder in folder.iterdir():
|
||||||
|
if subfolder.is_dir():
|
||||||
|
remove_empty_folders(subfolder)
|
||||||
|
|
||||||
|
if is_empty_directory(folder):
|
||||||
|
folder.rmdir()
|
||||||
|
|
||||||
|
remove_empty_folders(temp_path)
|
||||||
|
|
||||||
|
|
||||||
if IN_BLENDER:
|
if IN_BLENDER:
|
||||||
|
|
||||||
def get_binary_info() -> dict[str, Any]:
|
def get_binary_info() -> dict[str, Any]:
|
||||||
@@ -132,8 +211,7 @@ if IN_BLENDER:
|
|||||||
|
|
||||||
# We can't just use __file__ as blenderbim/__init__.py is typically not symlinked
|
# We can't just use __file__ as blenderbim/__init__.py is typically not symlinked
|
||||||
# as Blender have errors symlinking main addon package file.
|
# as Blender have errors symlinking main addon package file.
|
||||||
path = Path(__file__).parent / "bim" / "__init__.py"
|
path = Path(__file__).resolve().parent
|
||||||
path = path.resolve().parent
|
|
||||||
repo = git.Repo(str(path), search_parent_directories=True)
|
repo = git.Repo(str(path), search_parent_directories=True)
|
||||||
last_commit_hash = repo.head.object.hexsha
|
last_commit_hash = repo.head.object.hexsha
|
||||||
except:
|
except:
|
||||||
@@ -154,11 +232,17 @@ if IN_BLENDER:
|
|||||||
ifcopenshell.api.add_pre_listener("*", "action_logger", log_api)
|
ifcopenshell.api.add_pre_listener("*", "action_logger", log_api)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
clean_up_dlls_safe_links()
|
||||||
|
|
||||||
import blenderbim.bim
|
import blenderbim.bim
|
||||||
|
|
||||||
blenderbim.bim.register()
|
blenderbim.bim.register()
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
safe_link_dlls()
|
||||||
|
|
||||||
import blenderbim.bim
|
import blenderbim.bim
|
||||||
|
|
||||||
blenderbim.bim.unregister()
|
blenderbim.bim.unregister()
|
||||||
@@ -229,15 +313,15 @@ if IN_BLENDER:
|
|||||||
bbim_version = info["blenderbim_version"]
|
bbim_version = info["blenderbim_version"]
|
||||||
py_tag = py.replace(".", "")
|
py_tag = py.replace(".", "")
|
||||||
if "Linux" in info["os"]:
|
if "Linux" in info["os"]:
|
||||||
os = "linux"
|
os = "linux-x64"
|
||||||
elif "Darwin" in info["os"]:
|
elif "Darwin" in info["os"]:
|
||||||
if "arm64" in info["machine"]:
|
if "arm64" in info["machine"]:
|
||||||
os = "macosm1"
|
os = "macos-arm64"
|
||||||
else:
|
else:
|
||||||
os = "macos"
|
os = "macos-x64"
|
||||||
else:
|
else:
|
||||||
os = "win"
|
os = "windows-x64"
|
||||||
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{bbim_version}/blenderbim-{bbim_version}-py{py_tag}-{os}.zip"
|
op.uri = f"https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{bbim_version}/blenderbim_py{py_tag}-{bbim_version}-{os}.zip"
|
||||||
|
|
||||||
class OpenUri(bpy.types.Operator):
|
class OpenUri(bpy.types.Operator):
|
||||||
bl_idname = "bim.open_uri"
|
bl_idname = "bim.open_uri"
|
||||||
|
|||||||
@@ -196,7 +196,6 @@ for mod in modules.values():
|
|||||||
addon_keymaps = []
|
addon_keymaps = []
|
||||||
icons = None
|
icons = None
|
||||||
is_registering = False
|
is_registering = False
|
||||||
last_commit_hash = "8888888"
|
|
||||||
original_scene_panels_polls: dict[bpy.types.Panel, Union[Callable, None]] = dict()
|
original_scene_panels_polls: dict[bpy.types.Panel, Union[Callable, None]] = dict()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
blenderbim_lib_path = os.environ.get("blenderbim_lib_path")
|
||||||
|
if blenderbim_lib_path:
|
||||||
|
sys.path.insert(0, blenderbim_lib_path)
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import socketio
|
import socketio
|
||||||
@@ -21,6 +29,8 @@ sio = socketio.AsyncServer(
|
|||||||
app = web.Application()
|
app = web.Application()
|
||||||
sio.attach(app)
|
sio.attach(app)
|
||||||
|
|
||||||
|
|
||||||
|
# represents a caching for blender messages
|
||||||
blender_messages = {}
|
blender_messages = {}
|
||||||
|
|
||||||
|
|
||||||
@@ -31,6 +41,8 @@ class WebNamespace(socketio.AsyncNamespace):
|
|||||||
|
|
||||||
async def on_connect(self, sid, environ):
|
async def on_connect(self, sid, environ):
|
||||||
print(f"Web client connected: {sid}")
|
print(f"Web client connected: {sid}")
|
||||||
|
if blender_messages:
|
||||||
|
await self.send_cached_messages(sid)
|
||||||
|
|
||||||
async def on_disconnect(self, sid):
|
async def on_disconnect(self, sid):
|
||||||
print(f"Web client disconnected: {sid}")
|
print(f"Web client disconnected: {sid}")
|
||||||
@@ -43,6 +55,14 @@ class WebNamespace(socketio.AsyncNamespace):
|
|||||||
room=data["blenderId"],
|
room=data["blenderId"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def send_cached_messages(self, sid):
|
||||||
|
# Send cached messages to the connected web client
|
||||||
|
for blenderId, messages in blender_messages.items():
|
||||||
|
if "csv_data" in messages:
|
||||||
|
await self.emit("csv_data", {"blenderId": blenderId, "data": messages["csv_data"]}, room=sid)
|
||||||
|
if "gantt_data" in messages:
|
||||||
|
await self.emit("gantt_data", {"blenderId": blenderId, "data": messages["gantt_data"]}, room=sid)
|
||||||
|
|
||||||
|
|
||||||
# Blender namespace
|
# Blender namespace
|
||||||
class BlenderNamespace(socketio.AsyncNamespace):
|
class BlenderNamespace(socketio.AsyncNamespace):
|
||||||
@@ -65,7 +85,7 @@ class BlenderNamespace(socketio.AsyncNamespace):
|
|||||||
async def on_data(self, sid, data):
|
async def on_data(self, sid, data):
|
||||||
print(f"Data from Blender client {sid}")
|
print(f"Data from Blender client {sid}")
|
||||||
# blender_messages[sid]["default_data"] = data
|
# blender_messages[sid]["default_data"] = data
|
||||||
# await sio.emit("default_data", {"blenderId": sid, "data": data}, namespace="/web")
|
await sio.emit("default_data", {"blenderId": sid, "data": data}, namespace="/web")
|
||||||
|
|
||||||
async def on_csv_data(self, sid, data):
|
async def on_csv_data(self, sid, data):
|
||||||
print(f"CSV data from Blender client {sid}")
|
print(f"CSV data from Blender client {sid}")
|
||||||
@@ -99,10 +119,15 @@ async def gantt(request):
|
|||||||
return web.Response(text=html_content, content_type="text/html")
|
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}/")
|
||||||
|
|
||||||
|
|
||||||
app.router.add_get("/", index)
|
app.router.add_get("/", index)
|
||||||
app.router.add_get("/gantt", gantt)
|
app.router.add_get("/gantt", gantt)
|
||||||
app.router.add_static("/jsgantt/", path="../gantt", name="jsgantt")
|
app.router.add_static("/jsgantt/", path="../gantt", name="jsgantt")
|
||||||
app.router.add_static("/static/", path="./static", name="static")
|
app.router.add_static("/static/", path="./static", name="static")
|
||||||
|
app.on_startup.append(open_web_browser)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -1,19 +1,65 @@
|
|||||||
:root {
|
:root {
|
||||||
|
--font-family: Arial, sans-serif;
|
||||||
|
--margin-small: 0.625rem;
|
||||||
|
--margin-medium: 1.25rem;
|
||||||
|
--margin-large: 2.5rem;
|
||||||
|
--padding-small: 0.125rem;
|
||||||
|
--padding-medium: 1rem;
|
||||||
|
--font-size-small: 1rem;
|
||||||
|
--font-size-large: 1.2rem;
|
||||||
|
--logo-height: 2.5rem;
|
||||||
|
--folder-collapse-font-size: 0.75rem;
|
||||||
|
--folder-collapse-font-family: Courier, "Courier New", monospace;
|
||||||
|
--box-shadow: 0 0 0.7rem #5f5f5f66;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
|
--bg-color: #252525;
|
||||||
|
--primary-text-color: #e0e0e0;
|
||||||
|
--secondary-text-color: #c7c7c7;
|
||||||
|
--nav-bg-color: #121212;
|
||||||
|
--nav-border-color: #25682a;
|
||||||
|
--nav-link-color: #fff;
|
||||||
|
--nav-link-hover-color: #3fb449;
|
||||||
|
--warning-color: #FFDB8F;
|
||||||
|
--border-color: #464444;
|
||||||
|
--hover-bg-color: #3a3a3a;
|
||||||
|
--highlight-color: #009136;
|
||||||
|
--task-complete-bg-color: #777777;
|
||||||
|
--milestone-border-color: #ffffff;
|
||||||
|
--group-item-bg-color: #4b4b4b;
|
||||||
|
--input-bg-color: #3b3b3b;
|
||||||
|
--input-border-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.light {
|
||||||
|
color-scheme: light;
|
||||||
|
--bg-color: #ffffff;
|
||||||
|
--primary-text-color: #000000;
|
||||||
|
--nav-bg-color: #f8f8f8;
|
||||||
|
--nav-border-color: #cccccc;
|
||||||
|
--nav-link-color: #000000;
|
||||||
|
--nav-link-hover-color: #38a63d;
|
||||||
|
--warning-color: #FF4500;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #252525;
|
background-color: var(--bg-color);
|
||||||
color: #e0e0e0;
|
color: var(--primary-text-color);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Arial, sans-serif;
|
font-family: var(--font-family);
|
||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
margin-top: 20px;
|
margin-top: var(--margin-medium);
|
||||||
margin-left: 10px;
|
margin-left: var(--margin-small);
|
||||||
margin-right: 10px;
|
margin-right: var(--margin-small);
|
||||||
margin-bottom: 20px;
|
margin-bottom: var(--margin-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
@@ -21,79 +67,89 @@ h3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
background-color: #121212;
|
background-color: var(--nav-bg-color);
|
||||||
padding: 1em 0;
|
padding: var(--padding-medium) 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border-bottom: 2px solid var(--nav-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav .logo {
|
nav .logo {
|
||||||
margin-left: 40px;
|
margin-left: var(--margin-large);
|
||||||
height: 40px;
|
height: var(--logo-height);
|
||||||
/* Adjust height as needed */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul {
|
nav ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-left: 1px;
|
margin-left: 0.0625rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li {
|
nav ul li {
|
||||||
margin-right: 50px;
|
margin-right: 3.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li a {
|
nav ul li a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #fff;
|
color: var(--nav-link-color);
|
||||||
font-size: 1.2em;
|
font-size: var(--font-size-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li a:hover,
|
nav ul li a:hover,
|
||||||
nav ul li a.active {
|
nav ul li a.active {
|
||||||
color: #3fb449;
|
color: var(--nav-link-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav .bottom-bar {
|
.warning {
|
||||||
position: absolute;
|
color: var(--warning-color);
|
||||||
bottom: 0;
|
margin-bottom: var(--margin-small);
|
||||||
left: 0;
|
padding: var(--padding-small);
|
||||||
width: 100%;
|
display: none;
|
||||||
height: 2px;
|
|
||||||
background-color: #25682a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-description {
|
.table-description {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: var(--margin-medium);
|
||||||
width: 50%;
|
width: 50%;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
border-color: #464444;
|
border-color: var(--border-color);
|
||||||
border: 1px solid #464444;
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
box-shadow: 0 0 0.7rem rgba(95, 95, 95, 0.4);
|
box-shadow: var(--box-shadow);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-description tr {
|
.table-description tr {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
border-bottom: 1px solid #464444;
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-theme {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
margin-right: var(--margin-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-theme:focus {
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gantt-info {
|
.gantt-info {
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
font-size: 1rem;
|
font-size: var(--font-size-small);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
font-size: 1rem;
|
font-size: var(--font-size-small);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,86 +160,76 @@ nav .bottom-bar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ------------ Overwriting JSGantt CSS rules ------------ */
|
/* ------------ Overwriting JSGantt CSS rules ------------ */
|
||||||
|
:root.dark div.gantt {
|
||||||
div.gantt {
|
background-color: var(--bg-color);
|
||||||
background-color: #252525;
|
color: var(--secondary-text-color);
|
||||||
color: #c7c7c7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gantt table {
|
:root.dark .gantt table,
|
||||||
border-color: #464444;
|
:root.dark .gantt td {
|
||||||
;
|
border-color: var(--border-color);
|
||||||
}
|
|
||||||
|
|
||||||
.gantt td {
|
|
||||||
border-color: #464444;
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Headings and cell defaults */
|
/* Headings and cell defaults */
|
||||||
.gmajorheading,
|
:root.dark .gmajorheading,
|
||||||
.gminorheading,
|
:root.dark .gminorheading,
|
||||||
.gminorheadingwkend,
|
:root.dark .gminorheadingwkend,
|
||||||
.gtaskcell,
|
:root.dark .gtaskcell,
|
||||||
.gtaskcellcurrent,
|
:root.dark .gtaskcellcurrent,
|
||||||
.gtaskcellwkend,
|
:root.dark .gtaskcellwkend,
|
||||||
.gname,
|
:root.dark .gname,
|
||||||
.ggroupitem,
|
:root.dark .ggroupitem,
|
||||||
.gtaskheading {
|
:root.dark .gtaskheading {
|
||||||
background-color: #252525;
|
background-color: var(--bg-color);
|
||||||
border-color: #464444;
|
border-color: var(--border-color);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gtaskheading,
|
:root.dark .gtaskheading,
|
||||||
.gname,
|
:root.dark .gname,
|
||||||
.gtaskname,
|
:root.dark .gtaskname,
|
||||||
.gres,
|
:root.dark .gres,
|
||||||
.gdur,
|
:root.dark .gdur,
|
||||||
.gcomp,
|
:root.dark .gcomp,
|
||||||
.gstartdate,
|
:root.dark .gstartdate,
|
||||||
.gplanstartdate,
|
:root.dark .gplanstartdate,
|
||||||
.gplanenddate,
|
:root.dark .gplanenddate,
|
||||||
.gcost,
|
:root.dark .gcost,
|
||||||
.gchartlbl,
|
:root.dark .gchartlbl,
|
||||||
.gcontainercol,
|
:root.dark .gcontainercol,
|
||||||
.genddate {
|
:root.dark .genddate {
|
||||||
color: #c7c7c7;
|
color: var(--secondary-text-color);
|
||||||
border-color: #464444;
|
border-color: var(--border-color);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gtaskname>div {
|
:root.dark .gtaskname>div {
|
||||||
color: #c7c7c7;
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gtaskbarcontainer.gplan,
|
:root.dark .gtaskbarcontainer.gplan,
|
||||||
.gchartlbl.gcontainercol {
|
:root.dark .gchartlbl.gcontainercol {
|
||||||
background: #252525;
|
background: var(--bg-color);
|
||||||
border-color: #464444;
|
border-color: var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gtaskname div,
|
:root.dark .gtaskname div,
|
||||||
.gtaskheading div,
|
:root.dark .gtaskheading div,
|
||||||
.gtaskname span {
|
:root.dark .gtaskname span {
|
||||||
color: #c7c7c7;
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root.dark .gtasklist,
|
||||||
.gtasklist,
|
:root.dark .gadditional {
|
||||||
.gadditional {
|
border: var(--border-color) 1px solid;
|
||||||
border: #464444 1px solid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gchartgrid {
|
:root.dark .gchartgrid {
|
||||||
background-color: #252525;
|
background-color: var(--bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.glistgrid,
|
:root.dark .glistgrid,
|
||||||
.glistlbl {
|
:root.dark .glistlbl {
|
||||||
background-color: #252525;
|
background-color: var(--bg-color);
|
||||||
border-color: #464444;
|
border-color: var(--border-color);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .gTaskInfo {
|
/* .gTaskInfo {
|
||||||
@@ -193,88 +239,81 @@ div.gantt {
|
|||||||
} */
|
} */
|
||||||
|
|
||||||
/* Dark mode scrollbar */
|
/* Dark mode scrollbar */
|
||||||
.frame::-webkit-scrollbar-thumb {
|
:root.dark .frame::-webkit-scrollbar-thumb,
|
||||||
background-color: #252525;
|
:root.dark .frame::-webkit-scrollbar-track {
|
||||||
}
|
background-color: var(--bg-color);
|
||||||
|
|
||||||
.frame::-webkit-scrollbar-track {
|
|
||||||
background-color: #252525;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Highlight row */
|
/* Highlight row */
|
||||||
.gitemhighlight td {
|
:root.dark .gitemhighlight td {
|
||||||
background-color: #009136;
|
background-color: var(--highlight-color);
|
||||||
color: #ffffff;
|
color: var(--nav-link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Differentiate Group, Milestone and Ordinary task items (applied to row) */
|
/* Differentiate Group, Milestone and Ordinary task items (applied to row) */
|
||||||
.ggroupitem {
|
:root.dark .ggroupitem {
|
||||||
background-color: #252525;
|
background-color: var(--bg-color);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-color: #464444;
|
border-color: var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gmileitem,
|
:root.dark .gmileitem,
|
||||||
.glineitem {
|
:root.dark .glineitem {
|
||||||
background-color: #252525;
|
background-color: var(--bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Task bar caption text styles */
|
/* Task bar caption text styles */
|
||||||
.gmilecaption,
|
:root.dark .gmilecaption,
|
||||||
.ggroupcaption,
|
:root.dark .ggroupcaption,
|
||||||
.gcaption {
|
:root.dark .gcaption {
|
||||||
color: #ffffff;
|
color: var(--nav-link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Task complete %age bar */
|
/* Task complete %age bar */
|
||||||
.gtaskcomplete {
|
:root.dark .gtaskcomplete {
|
||||||
background-color: #777777;
|
background-color: var(--task-complete-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Milestones */
|
/* Milestones */
|
||||||
.gmdtop {
|
:root.dark .gmdtop,
|
||||||
border-bottom: 5px solid #ffffff;
|
:root.dark .gmdbottom {
|
||||||
|
border-bottom: 5px solid var(--milestone-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gmdbottom {
|
:root.dark .gfoldercollapse {
|
||||||
border-top: 5px solid #ffffff;
|
color: var(--secondary-text-color);
|
||||||
}
|
|
||||||
|
|
||||||
.gfoldercollapse {
|
|
||||||
color: #c7c7c7;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
font-size: var(--folder-collapse-font-size);
|
||||||
font-size: 12px;
|
font-family: var(--folder-collapse-font-family);
|
||||||
font-family: Courier, "Courier New", monospace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Highlight for collapsible row */
|
/* Highlight for collapsible row */
|
||||||
.gname.ggroupitem {
|
:root.dark .gname.ggroupitem {
|
||||||
background-color: rgb(75, 75, 75);
|
background-color: var(--group-item-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Form label and selected highlighting */
|
/* Form label and selected highlighting */
|
||||||
.gformlabel {
|
:root.dark .gformlabel {
|
||||||
background-color: #2e2e2e;
|
background-color: var(--formlabel-bg-color);
|
||||||
color: #c7c7c7;
|
color: var(--secondary-text-color);
|
||||||
border: #4a4a4a 1px solid;
|
border: var(--formlabel-border-color) 1px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.gformlabel:hover {
|
:root.dark span.gformlabel:hover {
|
||||||
background-color: #3a3a3a;
|
background-color: var(--hover-bg-color);
|
||||||
border-color: #565656;
|
border-color: var(--border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
span.gselected {
|
:root.dark span.gselected {
|
||||||
background-color: #565656;
|
background-color: var(--formlabel-selected-bg-color);
|
||||||
border-color: #777777;
|
border-color: var(--task-complete-bg-color);
|
||||||
color: #ffffff;
|
color: var(--nav-link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gantt-inputtable {
|
:root.dark .gantt-inputtable {
|
||||||
background-color: #3b3b3b;
|
background-color: var(--input-bg-color);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 1px solid #000;
|
border: 1px solid var(--input-border-color);
|
||||||
color: #c7c7c7;
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
@@ -1,27 +1,60 @@
|
|||||||
:root {
|
:root {
|
||||||
|
--font-family: Arial, sans-serif;
|
||||||
|
--margin-small: 0.625rem;
|
||||||
|
--margin-medium: 1.25rem;
|
||||||
|
--margin-large: 2.5rem;
|
||||||
|
--padding-small: 0.125rem;
|
||||||
|
--padding-medium: 1rem;
|
||||||
|
--font-size-large: 1.2rem;
|
||||||
|
--logo-height: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark {
|
||||||
color-scheme: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
: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;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: var(--base-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #252525;
|
background-color: var(--bg-color);
|
||||||
color: #e0e0e0;
|
color: var(--text-color);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Arial, sans-serif;
|
font-family: var(--font-family);
|
||||||
}
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
margin-top: 20px;
|
margin-top: var(--margin-medium);
|
||||||
margin-left: 10px;
|
margin-left: var(--margin-small);
|
||||||
margin-right: 10px;
|
margin-right: var(--margin-small);
|
||||||
margin-bottom: 20px
|
margin-bottom: var(--margin-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
margin-bottom: 40px;
|
margin-bottom: var(--margin-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
.csv-table {
|
.csv-table {
|
||||||
margin-top: 10px;
|
margin-top: var(--margin-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
@@ -29,48 +62,71 @@ h3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
background-color: #121212;
|
background-color: var(--nav-bg-color);
|
||||||
padding: 1em 0;
|
padding: var(--padding-medium) 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border-bottom: 2px solid var(--nav-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav .logo {
|
nav .logo {
|
||||||
margin-left: 40px;
|
margin-left: var(--margin-large);
|
||||||
height: 40px;
|
height: var(--logo-height);
|
||||||
/* Adjust height as needed */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul {
|
nav ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-left: 1px;
|
margin-left: 0.0625rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li {
|
nav ul li {
|
||||||
margin-right: 50px;
|
margin-right: 3.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li a {
|
nav ul li a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #fff;
|
color: var(--nav-link-color);
|
||||||
font-size: 1.2em;
|
font-size: var(--font-size-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul li a:hover,
|
nav ul li a:hover,
|
||||||
nav ul li a.active {
|
nav ul li a.active {
|
||||||
color: #3fb449;
|
color: var(--nav-link-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav .bottom-bar {
|
.warning {
|
||||||
position: absolute;
|
color: var(--warning-color);
|
||||||
bottom: 0;
|
margin-bottom: var(--margin-small);
|
||||||
left: 0;
|
padding: var(--padding-small);
|
||||||
width: 100%;
|
display: none;
|
||||||
height: 2px;
|
}
|
||||||
background-color: #25682a;
|
|
||||||
|
#toggle-theme {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
margin-right: var(--margin-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-theme:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------ Overwriting Tabulator CSS rules ------------ */
|
||||||
|
|
||||||
|
:root.light .tabulator-header,
|
||||||
|
:root.light .tabulator .tabulator-header .tabulator-col {
|
||||||
|
background: var(--nav-bg-color) !important;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.light .tabulator {
|
||||||
|
border-top: 1px solid #222;
|
||||||
|
border-bottom: 2px solid #222;
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,12 @@ const pageSizes = [
|
|||||||
|
|
||||||
// Document ready function
|
// Document ready function
|
||||||
$(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);
|
||||||
|
|
||||||
connectSocket();
|
connectSocket();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -32,6 +38,7 @@ function connectSocket() {
|
|||||||
socket.on("blender_connect", handleBlenderConnect);
|
socket.on("blender_connect", handleBlenderConnect);
|
||||||
socket.on("blender_disconnect", handleBlenderDisconnect);
|
socket.on("blender_disconnect", handleBlenderDisconnect);
|
||||||
socket.on("gantt_data", handleGanttData);
|
socket.on("gantt_data", handleGanttData);
|
||||||
|
socket.on("default_data", handleDefaultData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to handle 'blender_connect' event
|
// Function to handle 'blender_connect' event
|
||||||
@@ -61,7 +68,7 @@ function handleGanttData(data) {
|
|||||||
|
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
const filename = data["data"]["IFC_File"];
|
const filename = data["data"]["ifc_file"];
|
||||||
const ganttTasks = data["data"]["gantt_data"]["tasks"];
|
const ganttTasks = data["data"]["gantt_data"]["tasks"];
|
||||||
const ganttWorkSched = data["data"]["gantt_data"]["work_schedule"];
|
const ganttWorkSched = data["data"]["gantt_data"]["work_schedule"];
|
||||||
// const ganttWorkSched = {};
|
// const ganttWorkSched = {};
|
||||||
@@ -89,6 +96,13 @@ function handleGanttData(data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDefaultData(data) {
|
||||||
|
const blenderId = data["blenderId"];
|
||||||
|
const isDirty = data["data"]["is_dirty"];
|
||||||
|
showWarning(blenderId, isDirty);
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
|
||||||
// Function to add a new gantt with data and filename
|
// Function to add a new gantt with data and filename
|
||||||
function addGanttElement(blenderId, tasks, workSched, filename) {
|
function addGanttElement(blenderId, tasks, workSched, filename) {
|
||||||
const ganttContainer = $("<div></div>")
|
const ganttContainer = $("<div></div>")
|
||||||
@@ -101,6 +115,13 @@ function addGanttElement(blenderId, tasks, workSched, filename) {
|
|||||||
.addClass("no-print")
|
.addClass("no-print")
|
||||||
.css("margin-bottom", "10px");
|
.css("margin-bottom", "10px");
|
||||||
|
|
||||||
|
const warning = $("<div></div>")
|
||||||
|
.attr("id", "warning-" + blenderId)
|
||||||
|
.html(
|
||||||
|
"⚠ Warning: This Gantt Chart may contain outdated data due to recent changes in Blender."
|
||||||
|
)
|
||||||
|
.addClass("warning no-print");
|
||||||
|
|
||||||
const workSchedDiv = $("<div></div>").attr("id", "workSched" + blenderId);
|
const workSchedDiv = $("<div></div>").attr("id", "workSched" + blenderId);
|
||||||
|
|
||||||
const scheduleTable = $("<table></table>")
|
const scheduleTable = $("<table></table>")
|
||||||
@@ -148,6 +169,7 @@ function addGanttElement(blenderId, tasks, workSched, filename) {
|
|||||||
workSchedDiv.append(scheduleTable);
|
workSchedDiv.append(scheduleTable);
|
||||||
|
|
||||||
ganttContainer.append(ganttTitle);
|
ganttContainer.append(ganttTitle);
|
||||||
|
ganttContainer.append(warning);
|
||||||
ganttContainer.append(workSchedDiv);
|
ganttContainer.append(workSchedDiv);
|
||||||
ganttContainer.append(ganttInfoDiv);
|
ganttContainer.append(ganttInfoDiv);
|
||||||
ganttContainer.append(ganttDiv);
|
ganttContainer.append(ganttDiv);
|
||||||
@@ -233,8 +255,7 @@ function addGanttElement(blenderId, tasks, workSched, filename) {
|
|||||||
|
|
||||||
addEventListener("afterprint", (event) => {
|
addEventListener("afterprint", (event) => {
|
||||||
g.setEditable(true);
|
g.setEditable(true);
|
||||||
},);
|
});
|
||||||
|
|
||||||
|
|
||||||
let css =
|
let css =
|
||||||
"@media print {\n" +
|
"@media print {\n" +
|
||||||
@@ -280,6 +301,7 @@ function updateGanttElement(blenderId, tasks, workSched, filename) {
|
|||||||
g.Draw();
|
g.Draw();
|
||||||
|
|
||||||
$("#title-" + blenderId).text(filename);
|
$("#title-" + blenderId).text(filename);
|
||||||
|
$("#warning-" + blenderId).css("display", "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to remove gantt element
|
// Function to remove gantt element
|
||||||
@@ -287,6 +309,10 @@ function removeGanttElement(blenderId) {
|
|||||||
$("#container-" + blenderId).remove();
|
$("#container-" + blenderId).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showWarning(blenderId, isDirty) {
|
||||||
|
$("#warning-" + blenderId).css("display", "block");
|
||||||
|
}
|
||||||
|
|
||||||
// Utility function to create a tooltip for the gantt chars
|
// Utility function to create a tooltip for the gantt chars
|
||||||
function generateTooltip(task) {
|
function generateTooltip(task) {
|
||||||
var dataObject = task.getDataObject();
|
var dataObject = task.getDataObject();
|
||||||
@@ -337,3 +363,22 @@ function editValue(list, task, event, cell, column) {
|
|||||||
};
|
};
|
||||||
socket.emit("web_operator", msg);
|
socket.emit("web_operator", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ let socket;
|
|||||||
|
|
||||||
// Document ready function
|
// Document ready function
|
||||||
$(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);
|
||||||
|
|
||||||
connectSocket();
|
connectSocket();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -18,6 +24,7 @@ function connectSocket() {
|
|||||||
socket.on("blender_connect", handleBlenderConnect);
|
socket.on("blender_connect", handleBlenderConnect);
|
||||||
socket.on("blender_disconnect", handleBlenderDisconnect);
|
socket.on("blender_disconnect", handleBlenderDisconnect);
|
||||||
socket.on("csv_data", handleCsvData);
|
socket.on("csv_data", handleCsvData);
|
||||||
|
socket.on("default_data", handleDefaultData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to handle 'blender_connect' event
|
// Function to handle 'blender_connect' event
|
||||||
@@ -41,7 +48,7 @@ function handleBlenderDisconnect(blenderId) {
|
|||||||
function handleCsvData(data) {
|
function handleCsvData(data) {
|
||||||
const blenderId = data["blenderId"];
|
const blenderId = data["blenderId"];
|
||||||
const csvData = data["data"]["csv_data"];
|
const csvData = data["data"]["csv_data"];
|
||||||
const filename = data["data"]["IFC_File"];
|
const filename = data["data"]["ifc_file"];
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
if (connectedClients.hasOwnProperty(blenderId)) {
|
if (connectedClients.hasOwnProperty(blenderId)) {
|
||||||
@@ -65,6 +72,13 @@ function handleCsvData(data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDefaultData(data) {
|
||||||
|
const blenderId = data["blenderId"];
|
||||||
|
const isDirty = data["data"]["is_dirty"];
|
||||||
|
showWarning(blenderId, isDirty);
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
|
||||||
// Function to add a new table with data and filename
|
// Function to add a new table with data and filename
|
||||||
function addTableElement(blenderId, csvData, filename) {
|
function addTableElement(blenderId, csvData, filename) {
|
||||||
// store headers of the csv data
|
// store headers of the csv data
|
||||||
@@ -82,11 +96,19 @@ function addTableElement(blenderId, csvData, filename) {
|
|||||||
.text(filename)
|
.text(filename)
|
||||||
.css("margin-bottom", "10px");
|
.css("margin-bottom", "10px");
|
||||||
|
|
||||||
|
const warning = $("<div></div>")
|
||||||
|
.attr("id", "warning-" + blenderId)
|
||||||
|
.html(
|
||||||
|
"⚠ Warning: This table may contain outdated data due to recent changes in Blender."
|
||||||
|
)
|
||||||
|
.addClass("warning");
|
||||||
|
|
||||||
const tableDiv = $("<div></div>")
|
const tableDiv = $("<div></div>")
|
||||||
.addClass("csv-table")
|
.addClass("csv-table")
|
||||||
.attr("id", "table-" + blenderId);
|
.attr("id", "table-" + blenderId);
|
||||||
|
|
||||||
tableContainer.append(tableTitle);
|
tableContainer.append(tableTitle);
|
||||||
|
tableContainer.append(warning);
|
||||||
tableContainer.append(tableDiv);
|
tableContainer.append(tableDiv);
|
||||||
$("#container").append(tableContainer);
|
$("#container").append(tableContainer);
|
||||||
|
|
||||||
@@ -199,6 +221,7 @@ function updateTableElement(blenderId, csvData, filename) {
|
|||||||
connectedClients[blenderId].headers = newHeaders;
|
connectedClients[blenderId].headers = newHeaders;
|
||||||
}
|
}
|
||||||
$("#title-" + blenderId).text(filename);
|
$("#title-" + blenderId).text(filename);
|
||||||
|
$("#warning-" + blenderId).css("display", "none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +230,10 @@ function removeTableElement(blenderId) {
|
|||||||
$("#container-" + blenderId).remove();
|
$("#container-" + blenderId).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showWarning(blenderId, isDirty) {
|
||||||
|
$("#warning-" + blenderId).css("display", "block");
|
||||||
|
}
|
||||||
|
|
||||||
// Utility function to compare two csv header
|
// Utility function to compare two csv header
|
||||||
function compareHeaders(headers1, headers2) {
|
function compareHeaders(headers1, headers2) {
|
||||||
if (headers1.length !== headers2.length) {
|
if (headers1.length !== headers2.length) {
|
||||||
@@ -219,3 +246,31 @@ function compareHeaders(headers1, headers2) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setTheme(theme) {
|
||||||
|
var stylesheet = $("#tabulator-stylesheet");
|
||||||
|
if (theme === "light") {
|
||||||
|
stylesheet.attr(
|
||||||
|
"href",
|
||||||
|
"https://unpkg.com/tabulator-tables/dist/css/tabulator_site.min.css"
|
||||||
|
);
|
||||||
|
$("html").removeClass("dark").addClass("light");
|
||||||
|
$("#toggle-theme").html('<i class="fas fa-sun"></i>');
|
||||||
|
} else {
|
||||||
|
stylesheet.attr(
|
||||||
|
"href",
|
||||||
|
"https://unpkg.com/tabulator-tables/dist/css/tabulator_site_dark.min.css"
|
||||||
|
);
|
||||||
|
$("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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" class="dark">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Web Client</title>
|
<title>Web Client</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/jsgantt/jsgantt.css" />
|
<link rel="stylesheet" type="text/css" href="/jsgantt/jsgantt.css" />
|
||||||
<link rel="stylesheet" href="/static/css/gantt.css" />
|
<link rel="stylesheet" href="/static/css/gantt.css" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"
|
||||||
|
/>
|
||||||
<script
|
<script
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||||
@@ -22,12 +26,18 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="no-print">
|
<nav class="no-print">
|
||||||
<img src="https://blenderbim.org/assets/images/blender/blender-logo.png" alt="Logo" class="logo" />
|
<img
|
||||||
|
src="https://blenderbim.org/assets/images/blender/blender-logo.png"
|
||||||
|
alt="Logo"
|
||||||
|
class="logo"
|
||||||
|
/>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">IFC Data</a></li>
|
<li><a href="/">IFC Data</a></li>
|
||||||
<li><a href="/gantt" class="active">Gantt Chart</a></li>
|
<li><a href="/gantt" class="active">Gantt Chart</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="bottom-bar"></div>
|
<button id="toggle-theme" onclick="toggleTheme()">
|
||||||
|
<i class="fas fa-moon"></i>
|
||||||
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" class="dark">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
@@ -7,8 +7,13 @@
|
|||||||
<link rel="stylesheet" href="/static/css/index.css" />
|
<link rel="stylesheet" href="/static/css/index.css" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
id="tabulator-stylesheet"
|
||||||
href="https://unpkg.com/tabulator-tables/dist/css/tabulator_site_dark.min.css"
|
href="https://unpkg.com/tabulator-tables/dist/css/tabulator_site_dark.min.css"
|
||||||
/>
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"
|
||||||
|
/>
|
||||||
<script
|
<script
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
src="https://code.jquery.com/jquery-3.6.0.min.js"
|
||||||
@@ -28,12 +33,18 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
<img src="https://blenderbim.org/assets/images/blender/blender-logo.png" alt="Logo" class="logo" />
|
<img
|
||||||
|
src="https://blenderbim.org/assets/images/blender/blender-logo.png"
|
||||||
|
alt="Logo"
|
||||||
|
class="logo"
|
||||||
|
/>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/" class="active">IFC Data</a></li>
|
<li><a href="/" class="active">IFC Data</a></li>
|
||||||
<li><a href="/gantt">Gantt Chart</a></li>
|
<li><a href="/gantt">Gantt Chart</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="bottom-bar"></div>
|
<button id="toggle-theme" onclick="toggleTheme()">
|
||||||
|
<i class="fas fa-moon"></i>
|
||||||
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -167,21 +167,6 @@ class IfcExporter:
|
|||||||
def get_application_name(self) -> str:
|
def get_application_name(self) -> str:
|
||||||
return "BlenderBIM"
|
return "BlenderBIM"
|
||||||
|
|
||||||
def get_application_version(self) -> str:
|
|
||||||
version = ".".join(
|
|
||||||
[
|
|
||||||
str(x)
|
|
||||||
for x in [
|
|
||||||
addon.bl_info.get("version", (-1, -1, -1))
|
|
||||||
for addon in addon_utils.modules()
|
|
||||||
if addon.bl_info["name"] == "BlenderBIM"
|
|
||||||
][0]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if commit_hash := tool.Blender.get_last_commit_hash():
|
|
||||||
version += f"-{commit_hash}"
|
|
||||||
return version
|
|
||||||
|
|
||||||
|
|
||||||
class IfcExportSettings:
|
class IfcExportSettings:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ def redo_post(scene):
|
|||||||
|
|
||||||
def get_application(ifc: ifcopenshell.file) -> ifcopenshell.entity_instance:
|
def get_application(ifc: ifcopenshell.file) -> ifcopenshell.entity_instance:
|
||||||
# TODO: cache this for even faster application retrieval. It honestly makes a difference on long scripts.
|
# TODO: cache this for even faster application retrieval. It honestly makes a difference on long scripts.
|
||||||
version = get_application_version()
|
version = tool.Blender.get_blenderbim_version()
|
||||||
for element in ifc.by_type("IfcApplication"):
|
for element in ifc.by_type("IfcApplication"):
|
||||||
if element.ApplicationIdentifier == "BlenderBIM" and element.Version == version:
|
if element.ApplicationIdentifier == "BlenderBIM" and element.Version == version:
|
||||||
return element
|
return element
|
||||||
@@ -248,19 +248,6 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None
|
|||||||
return pao
|
return pao
|
||||||
|
|
||||||
|
|
||||||
def get_application_version() -> str:
|
|
||||||
return ".".join(
|
|
||||||
[
|
|
||||||
str(x)
|
|
||||||
for x in [
|
|
||||||
addon.bl_info.get("version", (-1, -1, -1))
|
|
||||||
for addon in addon_utils.modules()
|
|
||||||
if addon.bl_info["name"] == "BlenderBIM"
|
|
||||||
][0]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def viewport_shading_changed_callback(area):
|
def viewport_shading_changed_callback(area):
|
||||||
shading = area.spaces.active.shading.type
|
shading = area.spaces.active.shading.type
|
||||||
if shading == "RENDERED":
|
if shading == "RENDERED":
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import requests
|
|||||||
import tempfile
|
import tempfile
|
||||||
import datetime
|
import datetime
|
||||||
import addon_utils
|
import addon_utils
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
|
||||||
|
|
||||||
class AuginLogin(bpy.types.Operator):
|
class AuginLogin(bpy.types.Operator):
|
||||||
@@ -77,16 +78,7 @@ class AuginCreateNewModel(bpy.types.Operator):
|
|||||||
props.project_filename = "BBM{}{}".format(
|
props.project_filename = "BBM{}{}".format(
|
||||||
datetime.datetime.now().strftime("%Y%m%d%H%M%S"), "".join(random.choice(chars) for _ in range(6))
|
datetime.datetime.now().strftime("%Y%m%d%H%M%S"), "".join(random.choice(chars) for _ in range(6))
|
||||||
)
|
)
|
||||||
addon_version = ".".join(
|
addon_version = tool.Blender.get_blenderbim_version()
|
||||||
[
|
|
||||||
str(x)
|
|
||||||
for x in [
|
|
||||||
addon.bl_info.get("version", (-1, -1, -1))
|
|
||||||
for addon in addon_utils.modules()
|
|
||||||
if addon.bl_info["name"] == "BlenderBIM"
|
|
||||||
][0]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"user_token": props.token,
|
"user_token": props.token,
|
||||||
|
|||||||
@@ -254,9 +254,10 @@ class ExportIfcCsv(bpy.types.Operator):
|
|||||||
schedule_creator = scheduler.Scheduler()
|
schedule_creator = scheduler.Scheduler()
|
||||||
schedule_creator.schedule(self.filepath, tool.Drawing.get_path_with_ext(self.filepath, "svg"))
|
schedule_creator.schedule(self.filepath, tool.Drawing.get_path_with_ext(self.filepath, "svg"))
|
||||||
if props.format == "web":
|
if props.format == "web":
|
||||||
tool.Web.send_webui_data(
|
if not context.scene.WebProperties.is_running:
|
||||||
extra_data=ifc_csv.dataframe.to_csv(index=False), extra_data_key="csv_data", event="csv_data"
|
bpy.ops.bim.connect_websocket_server()
|
||||||
)
|
tool.Web.send_webui_data(data=ifc_csv.dataframe.to_csv(index=False), data_key="csv_data", event="csv_data")
|
||||||
|
|
||||||
self.report({"INFO"}, f"Data is exported to {props.format.upper()}.")
|
self.report({"INFO"}, f"Data is exported to {props.format.upper()}.")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ classes = (
|
|||||||
operator.ProfileImportIFC,
|
operator.ProfileImportIFC,
|
||||||
operator.PurgeHdf5Cache,
|
operator.PurgeHdf5Cache,
|
||||||
operator.PurgeUnusedElementsByClass,
|
operator.PurgeUnusedElementsByClass,
|
||||||
|
operator.RestartBlender,
|
||||||
operator.RewindInspector,
|
operator.RewindInspector,
|
||||||
operator.SelectExpressFile,
|
operator.SelectExpressFile,
|
||||||
operator.SelectHighPolygonMeshes,
|
operator.SelectHighPolygonMeshes,
|
||||||
|
|||||||
@@ -770,3 +770,15 @@ class ToggleDetailedIOSLogs(bpy.types.Operator):
|
|||||||
wrapper.turn_off_detailed_logging()
|
wrapper.turn_off_detailed_logging()
|
||||||
self.report({"INFO"}, "Detailed IfcOpenShell logs turned off.")
|
self.report({"INFO"}, "Detailed IfcOpenShell logs turned off.")
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class RestartBlender(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.restart_blender"
|
||||||
|
bl_label = "Restart Blender"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
import os
|
||||||
|
|
||||||
|
path = bpy.app.binary_path
|
||||||
|
os.execv(path, sys.argv)
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ class BIM_PT_debug(Panel):
|
|||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator("bim.toggle_detailed_ios_logs")
|
row.operator("bim.toggle_detailed_ios_logs")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.operator("bim.restart_blender")
|
||||||
|
|
||||||
row = layout.split(factor=0.5, align=True)
|
row = layout.split(factor=0.5, align=True)
|
||||||
row.operator("bim.create_shape_from_step_id").should_include_curves = False
|
row.operator("bim.create_shape_from_step_id").should_include_curves = False
|
||||||
row.operator("bim.create_shape_from_step_id", text="", icon="IPO_ELASTIC").should_include_curves = True
|
row.operator("bim.create_shape_from_step_id", text="", icon="IPO_ELASTIC").should_include_curves = True
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from blenderbim.bim.ifc import IfcStore
|
|||||||
class OpenPieClass(bpy.types.Operator):
|
class OpenPieClass(bpy.types.Operator):
|
||||||
bl_idname = "bim.open_pie_class"
|
bl_idname = "bim.open_pie_class"
|
||||||
bl_label = "Open Pie Class"
|
bl_label = "Open Pie Class"
|
||||||
|
bl_description = "Assign the IFC Class to the selected objects"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_bim_class")
|
bpy.ops.wm.call_menu_pie(name="VIEW3D_MT_PIE_bim_class")
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.assign_class"
|
bl_idname = "bim.assign_class"
|
||||||
bl_label = "Assign IFC Class"
|
bl_label = "Assign IFC Class"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Assign the IFC Class to the selected object"
|
bl_description = "Assign the IFC Class to the selected objects"
|
||||||
obj: bpy.props.StringProperty()
|
obj: bpy.props.StringProperty()
|
||||||
ifc_class: bpy.props.StringProperty()
|
ifc_class: bpy.props.StringProperty()
|
||||||
predefined_type: bpy.props.StringProperty()
|
predefined_type: bpy.props.StringProperty()
|
||||||
@@ -175,6 +175,13 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
should_add_representation: bpy.props.BoolProperty(default=True)
|
should_add_representation: bpy.props.BoolProperty(default=True)
|
||||||
ifc_representation_class: bpy.props.StringProperty()
|
ifc_representation_class: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
if not context.active_object and not context.selected_objects:
|
||||||
|
cls.poll_message_set("No object selected.")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.BIMRootProperties
|
props = context.scene.BIMRootProperties
|
||||||
objects = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects or [context.active_object]
|
objects = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects or [context.active_object]
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -31,10 +32,16 @@ class WebData:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def load(cls):
|
||||||
cls.data = {
|
cls.data = {
|
||||||
"IFC_File": cls.ifc_file_name(),
|
"ifc_file": cls.get_ifc_file_name(),
|
||||||
|
"is_dirty": cls.get_is_dirty(),
|
||||||
}
|
}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ifc_file_name(cls):
|
def get_ifc_file_name(cls):
|
||||||
return bpy.context.scene.BIMProperties.ifc_file
|
filename = os.path.basename(bpy.context.scene.BIMProperties.ifc_file)
|
||||||
|
return filename
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_is_dirty(cls):
|
||||||
|
return bpy.context.scene.BIMProperties.is_dirty
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import addon_utils
|
|
||||||
import platform
|
import platform
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ import ifcopenshell.util.element
|
|||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim
|
import blenderbim.bim
|
||||||
import addon_utils
|
|
||||||
import types
|
import types
|
||||||
|
import importlib
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from blenderbim.bim.ifc import IFC_CONNECTED_TYPE
|
from blenderbim.bim.ifc import IFC_CONNECTED_TYPE
|
||||||
@@ -920,7 +920,8 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_last_commit_hash(cls) -> Union[str, None]:
|
def get_last_commit_hash(cls) -> Union[str, None]:
|
||||||
"""Get 8 symbols of last commit hash if it's present or return None otherwise."""
|
"""Get 8 symbols of last commit hash if it's present or return None otherwise."""
|
||||||
commit_hash = blenderbim.bim.last_commit_hash
|
bbim = cls.get_bbim_extension_package()
|
||||||
|
commit_hash = bbim.last_commit_hash
|
||||||
|
|
||||||
# Commit hash is unset - user is using __init__ from repo
|
# Commit hash is unset - user is using __init__ from repo
|
||||||
# without setting up git repository.
|
# without setting up git repository.
|
||||||
@@ -930,17 +931,9 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return commit_hash[:7]
|
return commit_hash[:7]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_blenderbim_version(cls):
|
def get_blenderbim_version(cls) -> str:
|
||||||
version = ".".join(
|
bbim = cls.get_bbim_extension_package()
|
||||||
[
|
version = bbim.bbim_semver["version"]
|
||||||
str(x)
|
|
||||||
for x in [
|
|
||||||
addon.bl_info.get("version", (-1, -1, -1))
|
|
||||||
for addon in addon_utils.modules()
|
|
||||||
if addon.bl_info["name"] == "BlenderBIM"
|
|
||||||
][0]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if commit_hash := cls.get_last_commit_hash():
|
if commit_hash := cls.get_last_commit_hash():
|
||||||
version += f"-{commit_hash}"
|
version += f"-{commit_hash}"
|
||||||
return version
|
return version
|
||||||
@@ -1114,6 +1107,11 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return package_name
|
return package_name
|
||||||
return "blenderbim"
|
return "blenderbim"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_bbim_extension_package(cls) -> types.ModuleType:
|
||||||
|
name = cls.get_blender_addon_package_name()
|
||||||
|
return importlib.import_module(name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_addon_preferences(cls) -> blenderbim.bim.ui.BIM_ADDON_preferences:
|
def get_addon_preferences(cls) -> blenderbim.bim.ui.BIM_ADDON_preferences:
|
||||||
blender_package_name = cls.get_blender_addon_package_name()
|
blender_package_name = cls.get_blender_addon_package_name()
|
||||||
|
|||||||
@@ -1546,14 +1546,16 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
) -> None:
|
) -> None:
|
||||||
if bpy.context.scene.WebProperties.is_connected:
|
if bpy.context.scene.WebProperties.is_connected:
|
||||||
gantt_data = {"tasks": task_json, "work_schedule": work_schedule.get_info(recursive=True)}
|
gantt_data = {"tasks": task_json, "work_schedule": work_schedule.get_info(recursive=True)}
|
||||||
tool.Web.send_webui_data(extra_data=gantt_data, extra_data_key="gantt_data", event="gantt_data")
|
tool.Web.send_webui_data(data=gantt_data, data_key="gantt_data", event="gantt_data")
|
||||||
return
|
return
|
||||||
|
|
||||||
with open(os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"), "w") as f:
|
with open(os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"), "w") as f:
|
||||||
with open(os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.mustache"), "r") as t:
|
with open(os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.mustache"), "r") as t:
|
||||||
task_b64 = base64.b64encode(bytes(json.dumps(task_json), "utf-8")).decode("utf-8")
|
task_b64 = base64.b64encode(bytes(json.dumps(task_json), "utf-8")).decode("utf-8")
|
||||||
f.write(
|
f.write(
|
||||||
pystache.render(t.read(), {"json_data": task_b64, "data": json.dumps(work_schedule.get_info(recursive=True))})
|
pystache.render(
|
||||||
|
t.read(), {"json_data": task_b64, "data": json.dumps(work_schedule.get_info(recursive=True))}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
webbrowser.open("file://" + os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"))
|
webbrowser.open("file://" + os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"))
|
||||||
|
|
||||||
|
|||||||
@@ -21,32 +21,47 @@ from blenderbim.bim.module.web.data import WebData
|
|||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import ifcopenshell.api.sequence
|
import ifcopenshell.api.sequence
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
import socket
|
import socket
|
||||||
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import errno
|
||||||
import subprocess
|
import subprocess
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import asyncio
|
import asyncio
|
||||||
import socketio
|
import socketio
|
||||||
import threading
|
import threading
|
||||||
import queue
|
import queue
|
||||||
|
import json
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
sio = None
|
sio = None
|
||||||
ws_process = None
|
ws_process = None
|
||||||
ws_thread = None
|
ws_thread = None
|
||||||
web_operator_queue = queue.Queue()
|
web_operator_queue = queue.Queue()
|
||||||
|
|
||||||
|
RECONNECTION_ATTEMPTS = 3
|
||||||
|
RECONNECTION_DELAY = 2
|
||||||
IFC_TASK_ATTRIBUTE_MAP = {
|
IFC_TASK_ATTRIBUTE_MAP = {
|
||||||
"pStart": "ScheduleStart",
|
"pStart": "ScheduleStart",
|
||||||
"pEnd": "ScheduleFinish",
|
"pEnd": "ScheduleFinish",
|
||||||
"pName": "Name",
|
"pName": "Name",
|
||||||
"pRes": "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Web(blenderbim.core.tool.Web):
|
class Web(blenderbim.core.tool.Web):
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_port_number(cls):
|
def generate_port_number(cls) -> int:
|
||||||
|
"""
|
||||||
|
Generate a free port number.
|
||||||
|
|
||||||
|
This method creates a temporary socket to bind to a free port.
|
||||||
|
It then retrieves the port number, and returns it.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The port number that was generated.
|
||||||
|
"""
|
||||||
print("Generating port number")
|
print("Generating port number")
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
s.bind(("localhost", 0)) # Bind to a free port
|
s.bind(("localhost", 0)) # Bind to a free port
|
||||||
@@ -55,35 +70,104 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
return port
|
return port
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_port_available(cls, port):
|
def is_port_available(cls, port: int) -> bool:
|
||||||
|
"""
|
||||||
|
Attempts to connect to the specified port on localhost.
|
||||||
|
|
||||||
|
If the connection is refused, the port is available for use; otherwise, it is in use.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- port (int): The port number to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the port is available, False if it is in use.
|
||||||
|
"""
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
# connect_ex returns 0 if the connection succeeds
|
# connect_ex returns errno.SUCCESS (0) if the connection succeeds
|
||||||
# indicating the port is in use
|
# otherwise returns errno.ECONNREFUSED (111 or 10061) if no server is listening
|
||||||
# otherwise returns 10061 if no server is listening
|
return s.connect_ex(("localhost", port)) == errno.ECONNREFUSED
|
||||||
# indicating the port is available for use
|
|
||||||
return s.connect_ex(("localhost", port)) == 10061
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def start_websocket_server(cls, port):
|
def start_websocket_server(cls, port: int) -> None:
|
||||||
|
"""
|
||||||
|
Starts a WebSocket server on the specified port.
|
||||||
|
|
||||||
|
This method sets up the environment, locates paths, and starts
|
||||||
|
the WebSocket server process. It also handles the creation and updating of a PID file to keep track
|
||||||
|
of running server instances.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- port (int): The port number on which to start the WebSocket server.
|
||||||
|
"""
|
||||||
|
import addon_utils
|
||||||
|
|
||||||
global ws_process
|
global ws_process
|
||||||
|
|
||||||
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui")
|
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui")
|
||||||
|
|
||||||
ws_path = os.path.join(webui_path, "sioserver.py")
|
ws_path = os.path.join(webui_path, "sioserver.py")
|
||||||
|
|
||||||
ws_process = subprocess.Popen(["python", ws_path, "--p", str(port), "--host", "127.0.0.1"], cwd=webui_path)
|
py_version = sys.version_info
|
||||||
cls.open_web_browser(port)
|
|
||||||
|
if bpy.app.version >= (4, 2, 0):
|
||||||
|
blenderbim_lib_path = (
|
||||||
|
Path(bpy.utils.user_resource("EXTENSIONS"))
|
||||||
|
/ ".local"
|
||||||
|
/ "lib"
|
||||||
|
/ f"python{py_version.major}.{py_version.minor}"
|
||||||
|
/ "site-packages"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
addon = [a for a in addon_utils.modules() if a.bl_info["name"] == "BlenderBIM"][0]
|
||||||
|
blenderbim_path = os.path.dirname(addon.__file__)
|
||||||
|
blenderbim_lib_path = os.path.join(blenderbim_path, "libs", "site", "packages")
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["blenderbim_lib_path"] = str(blenderbim_lib_path)
|
||||||
|
|
||||||
|
ws_process = subprocess.Popen(
|
||||||
|
[sys.executable, ws_path, "--p", str(port), "--host", "127.0.0.1"],
|
||||||
|
cwd=webui_path,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
pid_file = os.path.join(webui_path, "running_pid.json")
|
||||||
|
|
||||||
|
if os.path.exists(pid_file):
|
||||||
|
with open(pid_file, "r") as f:
|
||||||
|
pids = json.load(f)
|
||||||
|
else:
|
||||||
|
pids = {}
|
||||||
|
|
||||||
|
pids[str(ws_process.pid)] = port
|
||||||
|
|
||||||
|
with open(pid_file, "w") as f:
|
||||||
|
json.dump(pids, f, indent=4)
|
||||||
|
|
||||||
cls.set_is_running(True)
|
cls.set_is_running(True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def connect_websocket_server(cls, port):
|
def connect_websocket_server(cls, port: int) -> None:
|
||||||
|
"""
|
||||||
|
Connect to a WebSocket server on the specified port.
|
||||||
|
|
||||||
|
This method sets up an asynchronous Socket.IO client with
|
||||||
|
reconnection attempts, starts an asyncio thread, connects to the WebSocket server, and sets the connection status.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- port (int): The port number to connect to the WebSocket server.
|
||||||
|
"""
|
||||||
global ws_thread, sio
|
global ws_thread, sio
|
||||||
|
|
||||||
if bpy.context.scene.WebProperties.is_connected:
|
if bpy.context.scene.WebProperties.is_connected:
|
||||||
print(f"Already connected to websocket server on port: {port}")
|
print(f"Already connected to websocket server on port: {port}")
|
||||||
return
|
return
|
||||||
|
|
||||||
sio = socketio.AsyncClient(reconnection=True, reconnection_attempts=3, reconnection_delay=2, logger=True)
|
sio = socketio.AsyncClient(
|
||||||
|
reconnection=True,
|
||||||
|
reconnection_attempts=RECONNECTION_ATTEMPTS,
|
||||||
|
reconnection_delay=RECONNECTION_DELAY,
|
||||||
|
logger=True,
|
||||||
|
)
|
||||||
|
|
||||||
ws_thread = AsyncioThread()
|
ws_thread = AsyncioThread()
|
||||||
ws_thread.daemon = True
|
ws_thread.daemon = True
|
||||||
@@ -93,10 +177,15 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
ws_thread.run_coro(cls.sio_connect(ws_url))
|
ws_thread.run_coro(cls.sio_connect(ws_url))
|
||||||
cls.set_is_connected(True)
|
cls.set_is_connected(True)
|
||||||
bpy.app.timers.register(cls.check_operator_queue)
|
bpy.app.timers.register(cls.check_operator_queue)
|
||||||
cls.send_webui_data()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def disconnect_websocket_server(cls):
|
def disconnect_websocket_server(cls) -> None:
|
||||||
|
"""
|
||||||
|
Disconnects the WebSocket server and stops the associated thread.
|
||||||
|
|
||||||
|
This method is responsible for disconnecting the WebSocket server, stopping the asyncio thread,
|
||||||
|
and resetting the global variables related to the WebSocket connection.
|
||||||
|
"""
|
||||||
global ws_thread, sio
|
global ws_thread, sio
|
||||||
ws_thread.run_coro(cls.sio_disconnect())
|
ws_thread.run_coro(cls.sio_disconnect())
|
||||||
ws_thread.stop()
|
ws_thread.stop()
|
||||||
@@ -105,7 +194,13 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
cls.set_is_connected(False)
|
cls.set_is_connected(False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def kill_websocket_server(cls):
|
def kill_websocket_server(cls) -> None:
|
||||||
|
"""
|
||||||
|
Terminate the currently running WebSocket server.
|
||||||
|
|
||||||
|
This method checks if there is an active WebSocket server process. If so, it disconnects it (if connected),
|
||||||
|
removes its PID from the PID file, terminates the process, and updates the server's running status.
|
||||||
|
"""
|
||||||
global ws_process
|
global ws_process
|
||||||
|
|
||||||
if ws_process is None:
|
if ws_process is None:
|
||||||
@@ -115,36 +210,62 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
if bpy.context.scene.WebProperties.is_connected:
|
if bpy.context.scene.WebProperties.is_connected:
|
||||||
cls.disconnect_websocket_server()
|
cls.disconnect_websocket_server()
|
||||||
|
|
||||||
sleep(0.5)
|
# sleep(0.5)
|
||||||
|
webui_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "webui")
|
||||||
|
pid_file = os.path.join(webui_path, "running_pid.json")
|
||||||
|
with open(pid_file, "r") as f:
|
||||||
|
pids = json.load(f)
|
||||||
|
|
||||||
|
if str(ws_process.pid) in pids:
|
||||||
|
del pids[str(ws_process.pid)]
|
||||||
|
|
||||||
|
# Write the updated PIDs back to the file
|
||||||
|
with open(pid_file, "w") as f:
|
||||||
|
json.dump(pids, f, indent=4)
|
||||||
|
|
||||||
ws_process.terminate()
|
ws_process.terminate()
|
||||||
ws_process.wait()
|
ws_process.wait()
|
||||||
ws_process = None
|
ws_process = None
|
||||||
|
|
||||||
cls.set_is_running(False)
|
cls.set_is_running(False)
|
||||||
|
|
||||||
print("Websocket server terminated successfully")
|
print("Websocket server terminated successfully")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def send_webui_data(
|
def send_webui_data(
|
||||||
cls, extra_data=None, extra_data_key="data", event="data", namespace="/blender", use_web_data=True
|
cls,
|
||||||
):
|
data: Optional[Any] = None,
|
||||||
|
data_key: str = "data",
|
||||||
|
event: str = "data",
|
||||||
|
namespace: str = "/blender",
|
||||||
|
use_web_data: bool = True,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Sends data to the Web UI via Websocket connection.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- data (Optional[Any]): The data to send. If None, just sends data from WebData.
|
||||||
|
- data_key (str): The key under which to store the data in the payload. Defaults to "data".
|
||||||
|
- event (str): The WebSocket event to emit. Defaults to "data".
|
||||||
|
- namespace (str): The namespace for the WebSocket event. Defaults to "/blender".
|
||||||
|
- use_web_data (bool): Whether to use data from WebData. Defaults to True.
|
||||||
|
"""
|
||||||
|
|
||||||
global ws_thread
|
global ws_thread
|
||||||
data = {}
|
payload = {}
|
||||||
|
|
||||||
if use_web_data:
|
if use_web_data:
|
||||||
if not WebData.is_loaded:
|
if not WebData.is_loaded:
|
||||||
WebData.load()
|
WebData.load()
|
||||||
data = WebData.data
|
payload = WebData.data
|
||||||
|
|
||||||
if extra_data is not None:
|
if data is not None:
|
||||||
data[extra_data_key] = extra_data
|
payload[data_key] = data
|
||||||
|
|
||||||
if ws_thread is not None and bpy.context.scene.WebProperties.is_connected:
|
if ws_thread is not None and bpy.context.scene.WebProperties.is_connected:
|
||||||
ws_thread.run_coro(cls.sio_send(data, event, namespace))
|
ws_thread.run_coro(cls.sio_send(payload, event, namespace))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_operator_queue(cls):
|
def check_operator_queue(cls) -> None | float:
|
||||||
if not bpy.context.scene.WebProperties.is_connected:
|
if not bpy.context.scene.WebProperties.is_connected:
|
||||||
with web_operator_queue.mutex:
|
with web_operator_queue.mutex:
|
||||||
web_operator_queue.queue.clear()
|
web_operator_queue.queue.clear()
|
||||||
@@ -161,7 +282,7 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_csv_operator(cls, operator_data):
|
def handle_csv_operator(cls, operator_data: dict) -> None:
|
||||||
if operator_data["type"] == "selection":
|
if operator_data["type"] == "selection":
|
||||||
bpy.ops.object.select_all(action="DESELECT")
|
bpy.ops.object.select_all(action="DESELECT")
|
||||||
guid = operator_data["globalId"]
|
guid = operator_data["globalId"]
|
||||||
@@ -171,7 +292,7 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle_gantt_operator(cls, operator_data):
|
def handle_gantt_operator(cls, operator_data: dict) -> None:
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
if operator_data["type"] == "editTask":
|
if operator_data["type"] == "editTask":
|
||||||
task_id = int(operator_data["taskId"])
|
task_id = int(operator_data["taskId"])
|
||||||
@@ -200,55 +321,81 @@ class Web(blenderbim.core.tool.Web):
|
|||||||
work_schedule = ifc_file.by_id(operator_data["workScheduleId"])
|
work_schedule = ifc_file.by_id(operator_data["workScheduleId"])
|
||||||
task_json = tool.Sequence.create_tasks_json(work_schedule)
|
task_json = tool.Sequence.create_tasks_json(work_schedule)
|
||||||
gantt_data = {"tasks": task_json, "work_schedule": work_schedule.get_info(recursive=True)}
|
gantt_data = {"tasks": task_json, "work_schedule": work_schedule.get_info(recursive=True)}
|
||||||
cls.send_webui_data(extra_data=gantt_data, extra_data_key="gantt_data", event="gantt_data")
|
cls.send_webui_data(data=gantt_data, data_key="gantt_data", event="gantt_data")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def open_web_browser(cls, port):
|
def open_web_browser(cls, port: int) -> None:
|
||||||
webbrowser.open(f"http://127.0.0.1:{port}/")
|
webbrowser.open(f"http://127.0.0.1:{port}/")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def sio_connect(cls, url):
|
async def sio_connect(cls, url: str) -> None:
|
||||||
await sio.connect(url, transports=["websocket"], namespaces="/blender")
|
await sio.connect(url, transports=["websocket"], namespaces="/blender")
|
||||||
sio.on("web_operator", cls.sio_listen_web_operator, namespace="/blender")
|
sio.on("web_operator", cls.sio_listen_web_operator, namespace="/blender")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def sio_disconnect(cls):
|
async def sio_disconnect(cls) -> None:
|
||||||
await sio.disconnect()
|
await sio.disconnect()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def sio_send(cls, data, event="data", namespace="/blender"):
|
async def sio_send(cls, data: Any, event: str = "data", namespace: str = "/blender") -> None:
|
||||||
await sio.emit(event, data, namespace=namespace)
|
await sio.emit(event, data, namespace=namespace)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def sio_listen_web_operator(cls, data):
|
async def sio_listen_web_operator(cls, data: dict) -> None:
|
||||||
try:
|
try:
|
||||||
web_operator_queue.put_nowait(data)
|
web_operator_queue.put_nowait(data)
|
||||||
except queue.Full:
|
except queue.Full:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_is_running(cls, is_running):
|
def set_is_running(cls, is_running: bool) -> None:
|
||||||
bpy.context.scene.WebProperties.is_running = is_running
|
bpy.context.scene.WebProperties.is_running = is_running
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_is_connected(cls, is_connected):
|
def set_is_connected(cls, is_connected: bool) -> None:
|
||||||
bpy.context.scene.WebProperties.is_connected = is_connected
|
bpy.context.scene.WebProperties.is_connected = is_connected
|
||||||
|
|
||||||
|
|
||||||
class AsyncioThread(threading.Thread):
|
class AsyncioThread(threading.Thread):
|
||||||
def __init__(self, *args, loop=None, **kwargs):
|
def __init__(self, *args, loop=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize an instance of AsyncioThread.
|
||||||
|
|
||||||
|
This class represents a thread that runs an asyncio event loop. It is used to handle asynchronous tasks
|
||||||
|
in a separate thread from the main thread.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- *args: Variable length argument list. These arguments are passed to the superclass constructor.
|
||||||
|
- loop: An existing asyncio event loop. If None, a new event loop is created.
|
||||||
|
- **kwargs: Arbitrary keyword arguments. These arguments are passed to the superclass constructor.
|
||||||
|
"""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.loop = loop or asyncio.new_event_loop()
|
self.loop = loop or asyncio.new_event_loop()
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""
|
||||||
|
Start the asyncio event loop and mark the thread as running.
|
||||||
|
"""
|
||||||
self.running = True
|
self.running = True
|
||||||
self.loop.run_forever()
|
self.loop.run_forever()
|
||||||
|
|
||||||
def run_coro(self, coro):
|
def run_coro(self, coro) -> Any:
|
||||||
|
"""
|
||||||
|
Run a coroutine in the asyncio event loop from a separate thread.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- coro: The coroutine to be run.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The result of the coroutine.
|
||||||
|
"""
|
||||||
return asyncio.run_coroutine_threadsafe(coro, loop=self.loop).result()
|
return asyncio.run_coroutine_threadsafe(coro, loop=self.loop).result()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self) -> None:
|
||||||
|
"""
|
||||||
|
Stop the asyncio event loop and join the thread.
|
||||||
|
"""
|
||||||
self.loop.call_soon_threadsafe(self.loop.stop)
|
self.loop.call_soon_threadsafe(self.loop.stop)
|
||||||
self.join()
|
self.join()
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ You will need to choose which build to download.
|
|||||||
- If you are on Blender >=4.1, choose py311
|
- If you are on Blender >=4.1, choose py311
|
||||||
- If you are on Blender >=3.1 and <=4.0, choose py310
|
- If you are on Blender >=3.1 and <=4.0, choose py310
|
||||||
- If you are on Blender >=2.93 and <3.1, choose py39
|
- If you are on Blender >=2.93 and <3.1, choose py39
|
||||||
- Choose ``linux``, ``macos`` (Apple Intel), ``macosm1`` (Apple Silicon), or
|
- Choose ``linux-x64``, ``macos-x64`` (Apple Intel), ``macos-arm64`` (Apple Silicon), or
|
||||||
``win`` depending on your operating system
|
``windows-x64`` depending on your operating system
|
||||||
|
|
||||||
For users who don't follow the `VFX Platform <https://vfxplatform.com/>`_
|
For users who don't follow the `VFX Platform <https://vfxplatform.com/>`_
|
||||||
standard, we also provide py312 builds.
|
standard, we also provide py312 builds.
|
||||||
@@ -75,138 +75,17 @@ restart Blender to see changes).
|
|||||||
|
|
||||||
For Linux or Mac:
|
For Linux or Mac:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. literalinclude:: ../../scripts/installation/dev_environment.sh
|
||||||
|
:language: bash
|
||||||
git clone https://github.com/IfcOpenShell/IfcOpenShell.git
|
:caption: dev_environment.sh
|
||||||
cd IfcOpenShell
|
|
||||||
|
|
||||||
# path to BlenderBIM addon
|
|
||||||
# default path on Mac: "/Users/$USER/Library/Application Support/Blender/X.X/scripts/addons/blenderbim"
|
|
||||||
# default path on Linux: "$HOME/.config/blender/X.X/"
|
|
||||||
BLENDER_ADDON_PATH="/path/to/blender/X.XX/scripts/addons/blenderbim"
|
|
||||||
|
|
||||||
# Remove the Blender add-on Python code
|
|
||||||
rm -r $BLENDER_ADDON_PATH/core/
|
|
||||||
rm -r $BLENDER_ADDON_PATH/tool/
|
|
||||||
rm -r $BLENDER_ADDON_PATH/bim/
|
|
||||||
|
|
||||||
# Replace them with links to the Git repository
|
|
||||||
ln -s $PWD/src/blenderbim/blenderbim/core $BLENDER_ADDON_PATH/core
|
|
||||||
ln -s $PWD/src/blenderbim/blenderbim/tool $BLENDER_ADDON_PATH/tool
|
|
||||||
ln -s $PWD/src/blenderbim/blenderbim/bim $BLENDER_ADDON_PATH/bim
|
|
||||||
|
|
||||||
# Copy over compiled IfcOpenShell files
|
|
||||||
cp $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/*_wrapper* $PWD/src/ifcopenshell-python/ifcopenshell/
|
|
||||||
|
|
||||||
# Remove the IfcOpenShell dependency
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell
|
|
||||||
|
|
||||||
# Replace them with links to the Git repository
|
|
||||||
ln -s $PWD/src/ifcopenshell-python/ifcopenshell $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell
|
|
||||||
|
|
||||||
# Remove and link other IfcOpenShell utilities
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/bcf
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcclash
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcpatch
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifctester
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcfm
|
|
||||||
rm -r $BLENDER_ADDON_PATH/libs/Desktop
|
|
||||||
|
|
||||||
ln -s $PWD/src/ifccsv/ifccsv.py $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
|
|
||||||
ln -s $PWD/src/ifcdiff/ifcdiff.py $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
|
|
||||||
ln -s $PWD/src/bsdd/bsdd.py $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
|
|
||||||
ln -s $PWD/src/bcf/src/bcf $BLENDER_ADDON_PATH/libs/site/packages/bcf
|
|
||||||
ln -s $PWD/src/ifc4d/ifc4d $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
|
|
||||||
ln -s $PWD/src/ifc5d/ifc5d $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
|
|
||||||
ln -s $PWD/src/ifccityjson/ifccityjson $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
|
|
||||||
ln -s $PWD/src/ifcclash/ifcclash $BLENDER_ADDON_PATH/libs/site/packages/ifcclash
|
|
||||||
ln -s $PWD/src/ifcpatch/ifcpatch $BLENDER_ADDON_PATH/libs/site/packages/ifcpatch
|
|
||||||
ln -s $PWD/src/ifctester/ifctester $BLENDER_ADDON_PATH/libs/site/packages/ifctester
|
|
||||||
ln -s $PWD/src/ifcfm/ifcfm $BLENDER_ADDON_PATH/libs/site/packages/ifcfm
|
|
||||||
ln -s $PWD/src/blenderbim/blenderbim/libs/desktop $BLENDER_ADDON_PATH/libs/Desktop
|
|
||||||
|
|
||||||
# Manually download some third party dependencies
|
|
||||||
cd $BLENDER_ADDON_PATH/bim/data/gantt
|
|
||||||
wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
|
|
||||||
wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
|
||||||
cd $BLENDER_ADDON_PATH/bim/schema
|
|
||||||
wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
|
||||||
|
|
||||||
Or, if you're on Windows, you can use the batch script below. You need to run
|
Or, if you're on Windows, you can use the batch script below. You need to run
|
||||||
it as an administrator. Before running it follow the instructions descibed
|
it as an administrator. Before running it follow the instructions descibed
|
||||||
in the `rem` tags.
|
in the `rem` tags.
|
||||||
|
|
||||||
.. code-block:: bat
|
.. literalinclude:: ../../scripts/installation/dev_environment.bat
|
||||||
|
:language: bat
|
||||||
@echo off
|
:caption: dev_environment.bat
|
||||||
|
|
||||||
rem SETUP BLENDER-BIM LIVE DEVELOPMENT ENVIRONMENT
|
|
||||||
rem Setup blenderbim addon location below (probably just need to change "x.x" for your Blender version).
|
|
||||||
rem Put the script to the folder where IfcOpenShell git repository is located
|
|
||||||
rem (script will try to clone IfcOpenShell.git if it's not present).
|
|
||||||
SET blenderbim=%appdata%\Blender Foundation\Blender\x.x\scripts\addons\blenderbim
|
|
||||||
|
|
||||||
git clone https://github.com/IfcOpenShell/IfcOpenShell.git
|
|
||||||
cd IfcOpenShell
|
|
||||||
|
|
||||||
echo Removing the Blender add-on Python code...
|
|
||||||
rd /S /Q "%blenderbim%\core\"
|
|
||||||
rd /S /Q "%blenderbim%\tool\"
|
|
||||||
rd /S /Q "%blenderbim%\bim\"
|
|
||||||
|
|
||||||
echo Replacing them with links to the Git repository...
|
|
||||||
mklink /D "%blenderbim%\core" "%cd%\src\blenderbim\blenderbim\core"
|
|
||||||
mklink /D "%blenderbim%\tool" "%cd%\src\blenderbim\blenderbim\tool"
|
|
||||||
mklink /D "%blenderbim%\bim" "%cd%\src\blenderbim\blenderbim\bim"
|
|
||||||
|
|
||||||
echo Copy over compiled IfcOpenShell files...
|
|
||||||
copy "%blenderbim%\libs\site\packages\ifcopenshell\*_wrapper*" "%cd%\src\ifcopenshell-python\ifcopenshell\"
|
|
||||||
|
|
||||||
echo Remove the IfcOpenShell dependency...
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\ifcopenshell"
|
|
||||||
|
|
||||||
echo Replace them with links to the Git repository...
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\ifcopenshell" "%cd%\src\ifcopenshell-python\ifcopenshell"
|
|
||||||
|
|
||||||
echo Remove and link other IfcOpenShell utilities...
|
|
||||||
del "%blenderbim%\libs\site\packages\ifccsv.py"
|
|
||||||
del "%blenderbim%\libs\site\packages\ifcdiff.py"
|
|
||||||
del "%blenderbim%\libs\site\packages\bsdd.py"
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\bcf"
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\ifc4d"
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\ifc5d"
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\ifccityjson"
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\ifcclash"
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\ifcpatch"
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\ifctester"
|
|
||||||
rd /S /Q "%blenderbim%\libs\site\packages\ifcfm"
|
|
||||||
rd /S /Q "%blenderbim%\libs\desktop"
|
|
||||||
|
|
||||||
mklink "%blenderbim%\libs\site\packages\ifccsv.py" "%cd%\src\ifccsv\ifccsv.py"
|
|
||||||
mklink "%blenderbim%\libs\site\packages\ifcdiff.py" "%cd%\src\ifcdiff\ifcdiff.py"
|
|
||||||
mklink "%blenderbim%\libs\site\packages\bsdd.py" "%cd%\src\bsdd\bsdd.py"
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\bcf" "%cd%\src\bcf\src\bcf"
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\ifc4d" "%cd%\src\ifc4d\ifc4d"
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\ifc5d" "%cd%\src\ifc5d\ifc5d"
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\ifccityjson" "%cd%\src\ifccityjson\ifccityjson"
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\ifcclash" "%cd%\src\ifcclash\ifcclash"
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\ifcpatch" "%cd%\src\ifcpatch\ifcpatch"
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\ifctester" "%cd%\src\ifctester\ifctester"
|
|
||||||
mklink /D "%blenderbim%\libs\site\packages\ifcfm" "%cd%\src\ifcfm\ifcfm"
|
|
||||||
mklink /D "%blenderbim%\libs\desktop" "%cd%\src\blenderbim\blenderbim\libs\desktop"
|
|
||||||
|
|
||||||
echo Manually downloading some third party dependencies...
|
|
||||||
curl https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js -o "%blenderbim%\bim\data\gantt\jsgantt.js"
|
|
||||||
curl https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css -o "%blenderbim%\bim\data\gantt\jsgantt.css"
|
|
||||||
curl -L https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl -o "%blenderbim%\bim\schema\Brick.ttl"
|
|
||||||
|
|
||||||
pause
|
|
||||||
|
|
||||||
After you modify your code in the Git repository, you will need to restart
|
After you modify your code in the Git repository, you will need to restart
|
||||||
Blender for the changes to take effect.
|
Blender for the changes to take effect.
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
try:
|
try:
|
||||||
import bpy
|
import bpy
|
||||||
|
# Ensure it's not fake-bpy-module.
|
||||||
|
if not hasattr(bpy, "context"):
|
||||||
|
raise ModuleNotFoundError
|
||||||
import bl_i18n_utils
|
import bl_i18n_utils
|
||||||
import addon_utils
|
import addon_utils
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
rem SETUP BLENDER-BIM LIVE DEVELOPMENT ENVIRONMENT
|
||||||
|
rem Update BLENDER_VERSION, PYTHON_VERSION, BBIM_REPO in the script bellow.
|
||||||
|
rem Put the script to the folder where IfcOpenShell git repository is located
|
||||||
|
rem (script will try to clone IfcOpenShell.git if it's not present).
|
||||||
|
SET BLENDER_VERSION=4.2
|
||||||
|
SET PYTHON_VERSION=3.11
|
||||||
|
SET BBIM_REPO=user_default
|
||||||
|
|
||||||
|
SET BLENDER=%appdata%\Blender Foundation\Blender\%BLENDER_VERSION%
|
||||||
|
SET BLENDER_LOCAL=%BLENDER%\extensions\.local\lib\python%PYTHON_VERSION%\site-packages
|
||||||
|
SET BLENDERBIM=%BLENDER_LOCAL%\blenderbim
|
||||||
|
SET BLENDER_EXT=%BLENDER%\extensions\%BBIM_REPO%\blenderbim
|
||||||
|
|
||||||
|
git clone https://github.com/IfcOpenShell/IfcOpenShell.git
|
||||||
|
cd IfcOpenShell
|
||||||
|
|
||||||
|
echo Removing the Blender add-on Python code...
|
||||||
|
del "%BLENDER_EXT%\__init__.py"
|
||||||
|
rd /S /Q "%BLENDERBIM%"
|
||||||
|
|
||||||
|
echo Replacing them with links to the Git repository...
|
||||||
|
mklink "%BLENDER_EXT%\__init__.py" "%CD%\src\blenderbim\blenderbim\__init__.py"
|
||||||
|
mklink /D "%BLENDERBIM%" "%CD%\src\blenderbim\blenderbim"
|
||||||
|
|
||||||
|
echo Copy over compiled IfcOpenShell files...
|
||||||
|
copy "%BLENDER_LOCAL%\ifcopenshell\*_wrapper*" "%CD%\src\ifcopenshell-python\ifcopenshell\"
|
||||||
|
|
||||||
|
echo Remove the IfcOpenShell dependency...
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\ifcopenshell"
|
||||||
|
|
||||||
|
echo Replace them with links to the Git repository...
|
||||||
|
mklink /D "%BLENDER_LOCAL%\ifcopenshell" "%CD%\src\ifcopenshell-python\ifcopenshell"
|
||||||
|
|
||||||
|
echo Remove and link other IfcOpenShell utilities...
|
||||||
|
del "%BLENDER_LOCAL%\ifccsv.py"
|
||||||
|
del "%BLENDER_LOCAL%\ifcdiff.py"
|
||||||
|
del "%BLENDER_LOCAL%\bsdd.py"
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\bcf"
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\ifc4d"
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\ifc5d"
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\ifccityjson"
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\ifcclash"
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\ifcpatch"
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\ifctester"
|
||||||
|
rd /S /Q "%BLENDER_LOCAL%\ifcfm"
|
||||||
|
|
||||||
|
mklink "%BLENDER_LOCAL%\ifccsv.py" "%CD%\src\ifccsv\ifccsv.py"
|
||||||
|
mklink "%BLENDER_LOCAL%\ifcdiff.py" "%CD%\src\ifcdiff\ifcdiff.py"
|
||||||
|
mklink "%BLENDER_LOCAL%\bsdd.py" "%CD%\src\bsdd\bsdd.py"
|
||||||
|
mklink /D "%BLENDER_LOCAL%\bcf" "%CD%\src\bcf\bcf"
|
||||||
|
mklink /D "%BLENDER_LOCAL%\ifc4d" "%CD%\src\ifc4d\ifc4d"
|
||||||
|
mklink /D "%BLENDER_LOCAL%\ifc5d" "%CD%\src\ifc5d\ifc5d"
|
||||||
|
mklink /D "%BLENDER_LOCAL%\ifccityjson" "%CD%\src\ifccityjson\ifccityjson"
|
||||||
|
mklink /D "%BLENDER_LOCAL%\ifcclash" "%CD%\src\ifcclash\ifcclash"
|
||||||
|
mklink /D "%BLENDER_LOCAL%\ifcpatch" "%CD%\src\ifcpatch\ifcpatch"
|
||||||
|
mklink /D "%BLENDER_LOCAL%\ifctester" "%CD%\src\ifctester\ifctester"
|
||||||
|
mklink /D "%BLENDER_LOCAL%\ifcfm" "%CD%\src\ifcfm\ifcfm"
|
||||||
|
|
||||||
|
echo Manually downloading some third party dependencies...
|
||||||
|
curl https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js -o "%BLENDERBIM%\bim\data\gantt\jsgantt.js"
|
||||||
|
curl https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css -o "%BLENDERBIM%\bim\data\gantt\jsgantt.css"
|
||||||
|
curl -L https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl -o "%BLENDERBIM%\bim\schema\Brick.ttl"
|
||||||
|
|
||||||
|
pause
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
git clone https://github.com/IfcOpenShell/IfcOpenShell.git
|
||||||
|
cd IfcOpenShell
|
||||||
|
|
||||||
|
# path to BlenderBIM addon
|
||||||
|
# default path on Mac: "/Users/$USER/Library/Application Support/Blender/X.X/scripts/addons/blenderbim"
|
||||||
|
# default path on Linux: "$HOME/.config/blender/X.X/"
|
||||||
|
BLENDER_ADDON_PATH="/path/to/blender/X.XX/scripts/addons/blenderbim"
|
||||||
|
|
||||||
|
# Remove the Blender add-on Python code
|
||||||
|
rm -r $BLENDER_ADDON_PATH/core/
|
||||||
|
rm -r $BLENDER_ADDON_PATH/tool/
|
||||||
|
rm -r $BLENDER_ADDON_PATH/bim/
|
||||||
|
|
||||||
|
# Replace them with links to the Git repository
|
||||||
|
ln -s $PWD/src/blenderbim/blenderbim/core $BLENDER_ADDON_PATH/core
|
||||||
|
ln -s $PWD/src/blenderbim/blenderbim/tool $BLENDER_ADDON_PATH/tool
|
||||||
|
ln -s $PWD/src/blenderbim/blenderbim/bim $BLENDER_ADDON_PATH/bim
|
||||||
|
|
||||||
|
# Copy over compiled IfcOpenShell files
|
||||||
|
cp $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell/*_wrapper* $PWD/src/ifcopenshell-python/ifcopenshell/
|
||||||
|
|
||||||
|
# Remove the IfcOpenShell dependency
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell
|
||||||
|
|
||||||
|
# Replace them with links to the Git repository
|
||||||
|
ln -s $PWD/src/ifcopenshell-python/ifcopenshell $BLENDER_ADDON_PATH/libs/site/packages/ifcopenshell
|
||||||
|
|
||||||
|
# Remove and link other IfcOpenShell utilities
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/bcf
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcclash
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcpatch
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifctester
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/site/packages/ifcfm
|
||||||
|
rm -r $BLENDER_ADDON_PATH/libs/Desktop
|
||||||
|
|
||||||
|
ln -s $PWD/src/ifccsv/ifccsv.py $BLENDER_ADDON_PATH/libs/site/packages/ifccsv.py
|
||||||
|
ln -s $PWD/src/ifcdiff/ifcdiff.py $BLENDER_ADDON_PATH/libs/site/packages/ifcdiff.py
|
||||||
|
ln -s $PWD/src/bsdd/bsdd.py $BLENDER_ADDON_PATH/libs/site/packages/bsdd.py
|
||||||
|
ln -s $PWD/src/bcf/bcf $BLENDER_ADDON_PATH/libs/site/packages/bcf
|
||||||
|
ln -s $PWD/src/ifc4d/ifc4d $BLENDER_ADDON_PATH/libs/site/packages/ifc4d
|
||||||
|
ln -s $PWD/src/ifc5d/ifc5d $BLENDER_ADDON_PATH/libs/site/packages/ifc5d
|
||||||
|
ln -s $PWD/src/ifccityjson/ifccityjson $BLENDER_ADDON_PATH/libs/site/packages/ifccityjson
|
||||||
|
ln -s $PWD/src/ifcclash/ifcclash $BLENDER_ADDON_PATH/libs/site/packages/ifcclash
|
||||||
|
ln -s $PWD/src/ifcpatch/ifcpatch $BLENDER_ADDON_PATH/libs/site/packages/ifcpatch
|
||||||
|
ln -s $PWD/src/ifctester/ifctester $BLENDER_ADDON_PATH/libs/site/packages/ifctester
|
||||||
|
ln -s $PWD/src/ifcfm/ifcfm $BLENDER_ADDON_PATH/libs/site/packages/ifcfm
|
||||||
|
ln -s $PWD/src/blenderbim/blenderbim/libs/desktop $BLENDER_ADDON_PATH/libs/Desktop
|
||||||
|
|
||||||
|
# Manually download some third party dependencies
|
||||||
|
cd $BLENDER_ADDON_PATH/bim/data/gantt
|
||||||
|
wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.js
|
||||||
|
wget https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/master/dist/jsgantt.css
|
||||||
|
cd $BLENDER_ADDON_PATH/bim/schema
|
||||||
|
wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
IS_STABLE:=FALSE
|
||||||
PYTHON:=python3.11
|
PYTHON:=python3.11
|
||||||
PIP:=pip3.11
|
PIP:=pip3.11
|
||||||
VERSION:=$(shell cat ../../VERSION)
|
VERSION:=$(shell cat ../../VERSION)
|
||||||
|
VERSION_DATE:=$(shell date '+%y%m%d')
|
||||||
SED:=sed -i
|
SED:=sed -i
|
||||||
VENV_BIN:=bin
|
VENV_BIN:=bin
|
||||||
|
|
||||||
@@ -23,11 +25,20 @@ dist:
|
|||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
cp -r $(PACKAGE_NAME) build/
|
cp -r $(PACKAGE_NAME) build/
|
||||||
cp pyproject.toml build/
|
cp pyproject.toml build/
|
||||||
|
ifeq ($(IS_STABLE), TRUE)
|
||||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
||||||
ifdef IS_MODULE
|
ifdef IS_MODULE
|
||||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/$(PACKAGE_NAME)
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/$(PACKAGE_NAME)
|
||||||
else
|
else
|
||||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/$(PACKAGE_NAME)/__init__.py
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/$(PACKAGE_NAME)/__init__.py
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)a$(VERSION_DATE)"/' build/pyproject.toml
|
||||||
|
ifdef IS_MODULE
|
||||||
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/$(PACKAGE_NAME)
|
||||||
|
else
|
||||||
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/$(PACKAGE_NAME)/__init__.py
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
cd build && $(PYTHON) -m venv env && . env/$(VENV_ACTIVATE) && $(PIP) install build
|
cd build && $(PYTHON) -m venv env && . env/$(VENV_ACTIVATE) && $(PIP) install build
|
||||||
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
|
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ IfcGeom::OpenCascadeKernel::faceset_helper::faceset_helper(
|
|||||||
loops.push_back(l);
|
loops.push_back(l);
|
||||||
for (auto& e : l->children) {
|
for (auto& e : l->children) {
|
||||||
// @todo make sure only cartesian points are provided here
|
// @todo make sure only cartesian points are provided here
|
||||||
auto& p = boost::get<ifcopenshell::geometry::taxonomy::point3::ptr>(e->start);
|
auto& p = boost::get<ifcopenshell::geometry::taxonomy::point3::ptr>(e->orientation.get_value_or(true) ? e->start : e->end);
|
||||||
if (point_identities_visited.find(p->identity()) == point_identities_visited.end()) {
|
if (point_identities_visited.find(p->identity()) == point_identities_visited.end()) {
|
||||||
point_identities_visited.insert(p->identity());
|
point_identities_visited.insert(p->identity());
|
||||||
points.push_back(p);
|
points.push_back(p);
|
||||||
@@ -201,10 +201,10 @@ void IfcGeom::OpenCascadeKernel::faceset_helper::loop_(const ifcopenshell::geome
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto a = boost::get<ifcopenshell::geometry::taxonomy::point3::ptr>(ps->children.back()->start);
|
auto a = boost::get<ifcopenshell::geometry::taxonomy::point3::ptr>(ps->children.back()->orientation.get_value_or(true) ? ps->children.back()->start : ps->children.back()->end);
|
||||||
auto A = a->identity();
|
auto A = a->identity();
|
||||||
for (auto& b : ps->children) {
|
for (auto& b : ps->children) {
|
||||||
auto B = boost::get<ifcopenshell::geometry::taxonomy::point3::ptr>(b->start)->identity();
|
auto B = boost::get<ifcopenshell::geometry::taxonomy::point3::ptr>(b->orientation.get_value_or(true) ? b->start : b->end)->identity();
|
||||||
auto C = vertex_mapping_[A], D = vertex_mapping_[B];
|
auto C = vertex_mapping_[A], D = vertex_mapping_[B];
|
||||||
bool fwd = C < D;
|
bool fwd = C < D;
|
||||||
if (!b->orientation) {
|
if (!b->orientation) {
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
|
|||||||
auto pwf = taxonomy::dcast<taxonomy::piecewise_function>(dir);
|
auto pwf = taxonomy::dcast<taxonomy::piecewise_function>(dir);
|
||||||
if (!pwf) {
|
if (!pwf) {
|
||||||
// Only implement on alignment curves
|
// Only implement on alignment curves
|
||||||
return nullptr;
|
Logger::Warning("IfcSectionedSolidHorizontal is only implemented for piecewise function Directrix curves", inst);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -111,6 +112,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSectionedSolidHorizontal* in
|
|||||||
loft->axis = nullptr;
|
loft->axis = nullptr;
|
||||||
|
|
||||||
// @todo currently only the case is handled where directrix returns a piecewise_function
|
// @todo currently only the case is handled where directrix returns a piecewise_function
|
||||||
|
// @todo this "if" statement is not really required because the function returns at the start if the Directrix is not a piecewise function
|
||||||
if (pwf) {
|
if (pwf) {
|
||||||
double start = std::max(0., cross_sections.front().dist_along);
|
double start = std::max(0., cross_sections.front().dist_along);
|
||||||
double end = std::min(pwf->length(), cross_sections.back().dist_along);
|
double end = std::min(pwf->length(), cross_sections.back().dist_along);
|
||||||
|
|||||||
@@ -341,10 +341,10 @@ namespace {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (item->declaration().is(IfcSchema::IfcBooleanClippingResult::Class())) {
|
while (auto booleanresult = item->as<IfcSchema::IfcBooleanClippingResult>()) {
|
||||||
// All instantiations of IfcBooleanOperand (type of FirstOperand) are subtypes of
|
// All instantiations of IfcBooleanOperand (type of FirstOperand) are subtypes of
|
||||||
// IfcGeometricRepresentationItem
|
// IfcGeometricRepresentationItem
|
||||||
item = (IfcSchema::IfcGeometricRepresentationItem*) ((IfcSchema::IfcBooleanClippingResult*) item)->FirstOperand();
|
item = booleanresult->FirstOperand()->as<IfcSchema::IfcRepresentationItem>();
|
||||||
if (item->StyledByItem()->size()) {
|
if (item->StyledByItem()->size()) {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@@ -756,6 +756,14 @@ void mapping::initialize_units_() {
|
|||||||
if (!angle_unit_encountered) {
|
if (!angle_unit_encountered) {
|
||||||
Logger::Warning("No plane angle unit encountered");
|
Logger::Warning("No plane angle unit encountered");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo move to a more descriptive function
|
||||||
|
if (settings_.get<settings::BuildingLocalPlacement>().get()) {
|
||||||
|
placement_rel_to_type_ = file_->schema()->declaration_by_name("IfcBuilding");
|
||||||
|
}
|
||||||
|
if (settings_.get<settings::SiteLocalPlacement>().get()) {
|
||||||
|
placement_rel_to_type_ = file_->schema()->declaration_by_name("IfcSite");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mapping::initialize_settings() {
|
void mapping::initialize_settings() {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
IS_STABLE:=FALSE
|
||||||
VERSION:=$(shell cat ../../VERSION)
|
VERSION:=$(shell cat ../../VERSION)
|
||||||
|
VERSION_DATE:=$(shell date '+%y%m%d')
|
||||||
PYVERSION:=py311
|
PYVERSION:=py311
|
||||||
PLATFORM:=linux64
|
PLATFORM:=linux64
|
||||||
|
|
||||||
@@ -120,11 +122,20 @@ endif
|
|||||||
cp -r build/botbuild/ifcopenshell/*ifcopenshell_wrapper* build/ifcopenshell/
|
cp -r build/botbuild/ifcopenshell/*ifcopenshell_wrapper* build/ifcopenshell/
|
||||||
cp ../../README.md build/
|
cp ../../README.md build/
|
||||||
cp pyproject.toml build/
|
cp pyproject.toml build/
|
||||||
|
ifeq ($(IS_STABLE), TRUE)
|
||||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/pyproject.toml
|
||||||
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/ifcopenshell/__init__.py
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)"/' build/ifcopenshell/__init__.py
|
||||||
|
else
|
||||||
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)a$(VERSION_DATE)"/' build/pyproject.toml
|
||||||
|
$(SED) 's/version = "0.0.0"/version = "$(VERSION)-alpha$(VERSION_DATE)"/' build/ifcopenshell/__init__.py
|
||||||
|
endif
|
||||||
|
|
||||||
cd build && $(PYTHON) -m venv env && . env/$(VENV_ACTIVATE) && $(PIP) install build
|
cd build && $(PYTHON) -m venv env && . env/$(VENV_ACTIVATE) && $(PIP) install build
|
||||||
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
|
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m build
|
||||||
|
|
||||||
mv build/dist/ifcopenshell-$(VERSION)-py3-none-any.whl dist/ifcopenshell-$(VERSION)-$(PYVERSION)-none-$(PLATFORMTAG).whl
|
ifeq ($(IS_STABLE), TRUE)
|
||||||
rm -rf build
|
mv build/dist/ifcopenshell-*.whl dist/ifcopenshell-$(VERSION)-$(PYVERSION)-none-$(PLATFORMTAG).whl
|
||||||
|
else
|
||||||
|
cp build/dist/ifcopenshell-*.whl dist/ifcopenshell-$(VERSION)a$(VERSION_DATE)-$(PYVERSION)-none-$(PLATFORMTAG).whl
|
||||||
|
endif
|
||||||
|
# rm -rf build
|
||||||
|
|||||||
@@ -81,10 +81,10 @@ autoapi_add_toctree_entry = True
|
|||||||
autoapi_type = "python"
|
autoapi_type = "python"
|
||||||
|
|
||||||
# autoapi works by reading source code instead of importing modules
|
# autoapi works by reading source code instead of importing modules
|
||||||
autoapi_dirs = ['../ifcopenshell', '../../bcf/src', '../../bsdd', '../../ifccsv', '../../ifcdiff', '../../ifcpatch/ifcpatch', '../../ifctester/ifctester']
|
autoapi_dirs = ['../ifcopenshell', '../../bcf/bcf', '../../bsdd', '../../ifccsv', '../../ifcdiff', '../../ifcpatch/ifcpatch', '../../ifctester/ifctester']
|
||||||
# autoapi_dirs = ['../../ifcdiff']
|
# autoapi_dirs = ['../../ifcdiff']
|
||||||
# autoapi_dirs = ['../../ifcdiff', '../ifcopenshell/util']
|
# autoapi_dirs = ['../../ifcdiff', '../ifcopenshell/util']
|
||||||
# autoapi_dirs = ['../../bcf/src', '../../bsdd', '../../ifccsv', '../../ifcdiff', '../../ifcpatch/ifcpatch', '../../ifctester/ifctester']
|
# autoapi_dirs = ['../../bcf/bcf', '../../bsdd', '../../ifccsv', '../../ifcdiff', '../../ifcpatch/ifcpatch', '../../ifctester/ifctester']
|
||||||
|
|
||||||
# These are auto-generated based on the IFC schema, so exclude them
|
# These are auto-generated based on the IFC schema, so exclude them
|
||||||
autoapi_ignore = ['*ifcopenshell/express/rules*']
|
autoapi_ignore = ['*ifcopenshell/express/rules*']
|
||||||
|
|||||||
@@ -21,6 +21,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Capping"
|
"name": "Capping"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Screed",
|
||||||
|
"predefined_type": "FLOORING"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Screed",
|
||||||
|
"predefined_type": "TOPPING"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"IfcDistributionElement": [
|
"IfcDistributionElement": [
|
||||||
|
|||||||
Reference in New Issue
Block a user