mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-28 11:18:18 +00:00
Merge branch 'v0.8.0' into ifctester-improvements
This commit is contained in:
@@ -13,6 +13,7 @@ import hashlib
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
from typing import NoReturn
|
from typing import NoReturn
|
||||||
from urllib import request
|
from urllib import request
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ from github import Github
|
|||||||
|
|
||||||
|
|
||||||
def get_repo_tag_names() -> list[str]:
|
def get_repo_tag_names() -> list[str]:
|
||||||
git_return = os.popen("git tag -l").read()
|
git_return = subprocess.check_output("git tag -l", text=True)
|
||||||
tag_names = [tag_name for tag_name in git_return.split("\n") if tag_name]
|
tag_names = [tag_name for tag_name in git_return.split("\n") if tag_name]
|
||||||
print(f"{len(tag_names)} tag_names found in repo")
|
print(f"{len(tag_names)} tag_names found in repo")
|
||||||
return tag_names
|
return tag_names
|
||||||
@@ -78,6 +79,10 @@ def get_release_zip(tag: str) -> tuple[str, str]:
|
|||||||
raise Exception(f"Couldn't find the release matching '{python_version}' and '{TARGET_OS}' in tag '{tag}'.")
|
raise Exception(f"Couldn't find the release matching '{python_version}' and '{TARGET_OS}' in tag '{tag}'.")
|
||||||
|
|
||||||
|
|
||||||
|
def run(command: str) -> None:
|
||||||
|
subprocess.check_output(command)
|
||||||
|
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
|
|
||||||
URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
|
URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender"
|
||||||
@@ -97,7 +102,7 @@ should_release = False
|
|||||||
target_release_tag = ""
|
target_release_tag = ""
|
||||||
TARGET_OS = "windows-x64"
|
TARGET_OS = "windows-x64"
|
||||||
|
|
||||||
git_status = os.popen("git status").read()
|
git_status = subprocess.check_output("git status", text=True)
|
||||||
print(git_status)
|
print(git_status)
|
||||||
|
|
||||||
for tag_name in get_repo_tag_names():
|
for tag_name in get_repo_tag_names():
|
||||||
@@ -147,7 +152,7 @@ blenderbim_build_version = target_release_tag.replace("blenderbim-", "")
|
|||||||
|
|
||||||
# url_blenderbim_py3x_win_zip
|
# url_blenderbim_py3x_win_zip
|
||||||
release_zip_file_name, url_blenderbim_py3x_win_zip = get_release_zip(target_release_tag)
|
release_zip_file_name, url_blenderbim_py3x_win_zip = get_release_zip(target_release_tag)
|
||||||
os.popen(f"wget {url_blenderbim_py3x_win_zip} --no-verbose").read()
|
subprocess.check_call(f"wget {url_blenderbim_py3x_win_zip} --no-verbose")
|
||||||
|
|
||||||
# sha256sum_blenderbim_py310_win_zip
|
# sha256sum_blenderbim_py310_win_zip
|
||||||
sha256sum_blenderbim_py3x_win_zip = get_file_sha256_hash(release_zip_file_name)
|
sha256sum_blenderbim_py3x_win_zip = get_file_sha256_hash(release_zip_file_name)
|
||||||
@@ -201,13 +206,13 @@ print("[INFO] inserting dynamic chocolatey package parameters successful")
|
|||||||
print("\n_____ build choco.exe with mono")
|
print("\n_____ build choco.exe with mono")
|
||||||
|
|
||||||
choco_version = "1.1.0"
|
choco_version = "1.1.0"
|
||||||
os.popen(f"wget https://github.com/chocolatey/choco/archive/refs/tags/{choco_version}.tar.gz --quiet").read()
|
run(f"wget https://github.com/chocolatey/choco/archive/refs/tags/{choco_version}.tar.gz --quiet")
|
||||||
os.popen(f"tar -xzf {choco_version}.tar.gz").read()
|
run(f"tar -xzf {choco_version}.tar.gz")
|
||||||
print("choco tar unpack successful")
|
print("choco tar unpack successful")
|
||||||
os.chdir("choco-1.1.0")
|
os.chdir("choco-1.1.0")
|
||||||
os.popen("./build.sh").read()
|
run("./build.sh")
|
||||||
|
|
||||||
os.popen("cp -r build_output/chocolatey /opt/chocolatey").read()
|
run("cp -r build_output/chocolatey /opt/chocolatey")
|
||||||
os.chdir(BLENDERBIM_DIR)
|
os.chdir(BLENDERBIM_DIR)
|
||||||
|
|
||||||
if pathlib.Path("/opt/chocolatey/choco.exe").exists():
|
if pathlib.Path("/opt/chocolatey/choco.exe").exists():
|
||||||
@@ -215,11 +220,15 @@ if pathlib.Path("/opt/chocolatey/choco.exe").exists():
|
|||||||
|
|
||||||
print("\n_____ build choco pack")
|
print("\n_____ build choco pack")
|
||||||
|
|
||||||
os.popen("mono /opt/chocolatey/choco.exe pack --allow-unofficial").read()
|
run("mono /opt/chocolatey/choco.exe pack --allow-unofficial")
|
||||||
os.popen('mono /opt/chocolatey/choco.exe setapikey --key="{choco_token}" --source="https://push.chocolatey.org/" --allow-unofficial').read()
|
run(
|
||||||
|
'mono /opt/chocolatey/choco.exe setapikey --key="{choco_token}" --source="https://push.chocolatey.org/" --allow-unofficial'
|
||||||
|
)
|
||||||
|
|
||||||
print("\n_____ build choco push")
|
print("\n_____ build choco push")
|
||||||
os.popen('mono /opt/chocolatey/choco.exe push --source="https://push.chocolatey.org/" --key="$CHOCO_TOKEN" --allow-unofficial --verbose').read()
|
run(
|
||||||
|
'mono /opt/chocolatey/choco.exe push --source="https://push.chocolatey.org/" --key="$CHOCO_TOKEN" --allow-unofficial --verbose'
|
||||||
|
)
|
||||||
|
|
||||||
print(f"choco push of version: {target_release_tag} successful!")
|
print(f"choco push of version: {target_release_tag} successful!")
|
||||||
print(f"it took: {datetime.datetime.now() - start}")
|
print(f"it took: {datetime.datetime.now() - start}")
|
||||||
|
|||||||
@@ -78,6 +78,17 @@ ignore = [
|
|||||||
"UP032", # Replace .format with f-string
|
"UP032", # Replace .format with f-string
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.ty.rules]
|
||||||
|
# We only use `ty` currently to check for deprecated methods.
|
||||||
|
all = "ignore"
|
||||||
|
deprecated = "error"
|
||||||
|
|
||||||
|
[tool.ty.src]
|
||||||
|
exclude = [
|
||||||
|
"src/ifc2ca/templates",
|
||||||
|
"src/svgfill/3rdparty",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.poe.tasks]
|
[tool.poe.tasks]
|
||||||
|
|
||||||
ruff-main = "ruff check --extend-exclude nix/build-all.py"
|
ruff-main = "ruff check --extend-exclude nix/build-all.py"
|
||||||
@@ -87,6 +98,8 @@ ruff.sequence = ["ruff-main", "ruff-old"]
|
|||||||
|
|
||||||
black = "black ."
|
black = "black ."
|
||||||
|
|
||||||
|
ty = "ty check"
|
||||||
|
|
||||||
format.sequence = ["black", "ruff-main", "ruff-old"]
|
format.sequence = ["black", "ruff-main", "ruff-old"]
|
||||||
|
|
||||||
cmake-format = "gersemi . --in-place"
|
cmake-format = "gersemi . --in-place"
|
||||||
|
|||||||
+1
-2
@@ -232,8 +232,7 @@ endif
|
|||||||
cd build/bonsai/bim/data/brick/ && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
cd build/bonsai/bim/data/brick/ && wget https://github.com/BrickSchema/Brick/releases/download/nightly/Brick.ttl
|
||||||
|
|
||||||
# Required for hipped roof generation
|
# Required for hipped roof generation
|
||||||
# TODO: Use official repo once https://github.com/prochitecture/bpypolyskel/pull/22 is merged.
|
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m pip wheel "git+https://github.com/prochitecture/bpypolyskel" --no-deps -w wheels/
|
||||||
cd build && . env/$(VENV_ACTIVATE) && $(PYTHON) -m pip wheel "git+https://github.com/Andrej730/bpypolyskel.git@pyproject_toml" --no-deps -w wheels/
|
|
||||||
|
|
||||||
# folder for executable files
|
# folder for executable files
|
||||||
mkdir -p build/bonsai/libs/bin
|
mkdir -p build/bonsai/libs/bin
|
||||||
|
|||||||
@@ -72,9 +72,7 @@ class IfcExporter:
|
|||||||
|
|
||||||
def set_header(self):
|
def set_header(self):
|
||||||
self.file.header.file_name.name = os.path.basename(self.ifc_export_settings.output_file)
|
self.file.header.file_name.name = os.path.basename(self.ifc_export_settings.output_file)
|
||||||
self.file.header.file_name.time_stamp = (
|
self.file.header.file_name.time_stamp = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
|
||||||
datetime.datetime.utcnow().replace(tzinfo=datetime.UTC).astimezone().replace(microsecond=0).isoformat()
|
|
||||||
)
|
|
||||||
self.file.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
|
self.file.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
|
||||||
self.file.header.file_name.originating_system = "{} {}".format(
|
self.file.header.file_name.originating_system = "{} {}".format(
|
||||||
self.get_application_name(), tool.Blender.get_bonsai_version()
|
self.get_application_name(), tool.Blender.get_bonsai_version()
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import ifcopenshell.api.pset
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
BoolProperty,
|
BoolProperty,
|
||||||
BoolVectorProperty,
|
|
||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
EnumProperty,
|
EnumProperty,
|
||||||
FloatProperty,
|
FloatProperty,
|
||||||
|
|||||||
@@ -267,6 +267,8 @@ class BIMGeoreferenceProperties(PropertyGroup):
|
|||||||
x_axis_ordinate: str
|
x_axis_ordinate: str
|
||||||
x_axis_is_null: bool
|
x_axis_is_null: bool
|
||||||
|
|
||||||
|
model_is_georeferenced: bool
|
||||||
|
model_crs: str
|
||||||
model_origin: str
|
model_origin: str
|
||||||
model_origin_si: str
|
model_origin_si: str
|
||||||
model_project_north: str
|
model_project_north: str
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ classes = (
|
|||||||
operator.UnlinkIfc,
|
operator.UnlinkIfc,
|
||||||
operator.UnloadLink,
|
operator.UnloadLink,
|
||||||
workspace.ExploreHotkey,
|
workspace.ExploreHotkey,
|
||||||
workspace.GenerateUVMap,
|
operator.GenerateUVMap,
|
||||||
prop.LibraryBreadcrumb,
|
prop.LibraryBreadcrumb,
|
||||||
prop.LibraryElement,
|
prop.LibraryElement,
|
||||||
prop.FilterCategory,
|
prop.FilterCategory,
|
||||||
|
|||||||
@@ -178,9 +178,18 @@ class SelectLibraryFile(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
|||||||
bl_description = (
|
bl_description = (
|
||||||
"Select an IFC file that can be used as a library.\n\nALT+click to reload the current loaded library file."
|
"Select an IFC file that can be used as a library.\n\nALT+click to reload the current loaded library file."
|
||||||
)
|
)
|
||||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(
|
||||||
append_all: bpy.props.BoolProperty(default=False)
|
default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"}
|
||||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
append_all: bpy.props.BoolProperty(default=False) # pyright: ignore[reportRedeclaration]
|
||||||
|
use_relative_path: bpy.props.BoolProperty(
|
||||||
|
name="Use Relative Path", default=False
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
filter_glob: str
|
||||||
|
append_all: bool
|
||||||
|
use_relative_path: bool
|
||||||
|
|
||||||
reload_previous_file = False
|
reload_previous_file = False
|
||||||
|
|
||||||
@@ -558,7 +567,11 @@ class AppendEntireLibrary(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
class AppendLibraryElementByQuery(bpy.types.Operator, tool.Ifc.Operator):
|
class AppendLibraryElementByQuery(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.append_library_element_by_query"
|
bl_idname = "bim.append_library_element_by_query"
|
||||||
bl_label = "Append Library Element By Query"
|
bl_label = "Append Library Element By Query"
|
||||||
query: bpy.props.StringProperty(name="Query")
|
|
||||||
|
query: bpy.props.StringProperty(name="Query") # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
query: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -587,9 +600,16 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
"Append element to the current project.\n\n"
|
"Append element to the current project.\n\n"
|
||||||
"ALT+CLICK to skip reusing materials, profiles, styles based on their name (may result in duplicates)"
|
"ALT+CLICK to skip reusing materials, profiles, styles based on their name (may result in duplicates)"
|
||||||
)
|
)
|
||||||
definition: bpy.props.IntProperty()
|
definition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||||
prop_index: bpy.props.IntProperty()
|
prop_index: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
||||||
assume_unique_by_name: bpy.props.BoolProperty(name="Assume Unique By Name", default=True, options={"SKIP_SAVE"})
|
assume_unique_by_name: bpy.props.BoolProperty(
|
||||||
|
name="Assume Unique By Name", default=True, options={"SKIP_SAVE"}
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
definition: int
|
||||||
|
prop_index: int
|
||||||
|
assume_unique_by_name: bool
|
||||||
|
|
||||||
file: ifcopenshell.file
|
file: ifcopenshell.file
|
||||||
|
|
||||||
@@ -939,24 +959,28 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
|||||||
bl_label = "Load Project"
|
bl_label = "Load Project"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Load an existing IFC project"
|
bl_description = "Load an existing IFC project"
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE"})
|
filepath: bpy.props.StringProperty(
|
||||||
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"})
|
subtype="FILE_PATH", options={"SKIP_SAVE"}
|
||||||
is_advanced: bpy.props.BoolProperty(
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
filter_glob: bpy.props.StringProperty(
|
||||||
|
default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"}
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
is_advanced: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||||
name="Enable Advanced Mode",
|
name="Enable Advanced Mode",
|
||||||
description="Load IFC file with advanced settings. Checking this option will skip loading IFC file and will open advanced load settings",
|
description="Load IFC file with advanced settings. Checking this option will skip loading IFC file and will open advanced load settings",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
use_relative_path: bpy.props.BoolProperty(
|
use_relative_path: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||||
name="Use Relative Path",
|
name="Use Relative Path",
|
||||||
description="Store the IFC project path relative to the .blend file. Requires .blend file to be saved",
|
description="Store the IFC project path relative to the .blend file. Requires .blend file to be saved",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
should_start_fresh_session: bpy.props.BoolProperty(
|
should_start_fresh_session: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||||
name="Should Start Fresh Session",
|
name="Should Start Fresh Session",
|
||||||
description="Clear current Blender session before loading IFC. Not supported with 'Use Relative Path' option",
|
description="Clear current Blender session before loading IFC. Not supported with 'Use Relative Path' option",
|
||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
import_without_ifc_data: bpy.props.BoolProperty(
|
import_without_ifc_data: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
||||||
name="Import Without IFC Data",
|
name="Import Without IFC Data",
|
||||||
description=(
|
description=(
|
||||||
"Import IFC objects as Blender objects without any IFC metadata and authoring capabilities."
|
"Import IFC objects as Blender objects without any IFC metadata and authoring capabilities."
|
||||||
@@ -964,9 +988,20 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
|||||||
),
|
),
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
use_detailed_tooltip: bpy.props.BoolProperty(default=False, options={"HIDDEN"})
|
use_detailed_tooltip: bpy.props.BoolProperty(
|
||||||
|
default=False, options={"HIDDEN"}
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
filename_ext = ".ifc"
|
filename_ext = ".ifc"
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
filepath: str
|
||||||
|
filter_glob: str
|
||||||
|
is_advanced: bool
|
||||||
|
use_relative_path: bool
|
||||||
|
should_start_fresh_session: bool
|
||||||
|
import_without_ifc_data: bool
|
||||||
|
use_detailed_tooltip: bool
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def description(cls, context, properties):
|
def description(cls, context, properties):
|
||||||
tooltip = cls.bl_description
|
tooltip = cls.bl_description
|
||||||
@@ -1265,7 +1300,10 @@ class ToggleFilterCategories(bpy.types.Operator):
|
|||||||
bl_idname = "bim.toggle_filter_categories"
|
bl_idname = "bim.toggle_filter_categories"
|
||||||
bl_label = "Toggle Filter Categories"
|
bl_label = "Toggle Filter Categories"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
should_select: bpy.props.BoolProperty(name="Should Select", default=True)
|
should_select: bpy.props.BoolProperty(name="Should Select", default=True) # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
should_select: bool
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = tool.Project.get_project_props()
|
props = tool.Project.get_project_props()
|
||||||
@@ -1289,6 +1327,14 @@ class LinkIfc(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
|||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
|
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True)
|
||||||
|
query: bpy.props.StringProperty( # pyright: ignore[reportRedeclaration]
|
||||||
|
name="Query",
|
||||||
|
description=(
|
||||||
|
"Custom selector query to use to load element from a linked model. E.g. 'IfcElement'.\n\n"
|
||||||
|
"Default query - IfcElement, but excluding IfcProxy, IfcSpatialStructureElement, IfcSpatialElement, IfcFeatureElement."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
filename_ext = ".ifc"
|
filename_ext = ".ifc"
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -1298,20 +1344,25 @@ class LinkIfc(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
|||||||
filter_glob: str
|
filter_glob: str
|
||||||
use_relative_path: bool
|
use_relative_path: bool
|
||||||
use_cache: bool
|
use_cache: bool
|
||||||
|
query: str
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
assert self.layout
|
||||||
pprops = tool.Project.get_project_props()
|
pprops = tool.Project.get_project_props()
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(self, "use_relative_path")
|
row.prop(self, "use_relative_path")
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(self, "use_cache")
|
row.prop(self, "use_cache")
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "false_origin_mode")
|
row.label(text="False Origin Mode:")
|
||||||
|
row = self.layout.row()
|
||||||
|
row.prop(pprops, "false_origin_mode", text="")
|
||||||
if pprops.false_origin_mode == "MANUAL":
|
if pprops.false_origin_mode == "MANUAL":
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "false_origin")
|
row.prop(pprops, "false_origin")
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.prop(pprops, "project_north")
|
row.prop(pprops, "project_north")
|
||||||
|
self.layout.prop(self, "query", placeholder="IfcElement")
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
start = time.time()
|
start = time.time()
|
||||||
@@ -1344,7 +1395,7 @@ class LinkIfc(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
|||||||
new.ifc_definition_id = reference.id()
|
new.ifc_definition_id = reference.id()
|
||||||
new.name = filepath
|
new.name = filepath
|
||||||
new.filepath = filepath
|
new.filepath = filepath
|
||||||
bpy.ops.bim.load_link(link_index=-1, use_cache=self.use_cache)
|
bpy.ops.bim.load_link(link_index=-1, use_cache=self.use_cache, query=self.query)
|
||||||
|
|
||||||
|
|
||||||
class UnlinkIfc(bpy.types.Operator, tool.Ifc.Operator):
|
class UnlinkIfc(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -1352,7 +1403,11 @@ class UnlinkIfc(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Unlink IFC"
|
bl_label = "Unlink IFC"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Remove the selected file from the link list"
|
bl_description = "Remove the selected file from the link list"
|
||||||
link_index: bpy.props.IntProperty(name="Link Index")
|
|
||||||
|
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
link_index: int
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = tool.Project.get_project_props()
|
props = tool.Project.get_project_props()
|
||||||
@@ -1372,7 +1427,11 @@ class UnloadLink(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_label = "Unload Link"
|
bl_label = "Unload Link"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Unload the selected linked file"
|
bl_description = "Unload the selected linked file"
|
||||||
link_index: bpy.props.IntProperty(name="Link Index")
|
|
||||||
|
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
link_index: int
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
link = tool.Project.get_project_props().links[self.link_index]
|
link = tool.Project.get_project_props().links[self.link_index]
|
||||||
@@ -1397,10 +1456,12 @@ class LoadLink(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||||
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True) # pyright: ignore[reportRedeclaration]
|
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True) # pyright: ignore[reportRedeclaration]
|
||||||
|
query: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
link_index: int
|
link_index: int
|
||||||
use_cache: bool
|
use_cache: bool
|
||||||
|
query: str
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.link = tool.Project.get_project_props().links[self.link_index]
|
self.link = tool.Project.get_project_props().links[self.link_index]
|
||||||
@@ -1442,8 +1503,20 @@ class LoadLink(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
def link_ifc(self) -> Union[set[str], None]:
|
def link_ifc(self) -> Union[set[str], None]:
|
||||||
blend_filepath = self.filepath_.with_suffix(".ifc.cache.blend")
|
blend_filepath = self.filepath_.with_suffix(".ifc.cache.blend")
|
||||||
h5_filepath = self.filepath_.with_suffix(".ifc.cache.h5")
|
h5_filepath = self.filepath_.with_suffix(".ifc.cache.h5")
|
||||||
|
json_filepath = self.filepath_.with_suffix(".ifc.cache.json")
|
||||||
|
|
||||||
if not self.use_cache and blend_filepath.exists():
|
def should_clear_cache() -> bool:
|
||||||
|
if not self.use_cache:
|
||||||
|
return True
|
||||||
|
if not blend_filepath.exists():
|
||||||
|
return False
|
||||||
|
data = json.loads(json_filepath.read_text())
|
||||||
|
# Empty 'query' - model loaded without custom query.
|
||||||
|
# Missing 'query' - model was loaded before custom queries were introduced in Bonsai.
|
||||||
|
query = data.get("query", "")
|
||||||
|
return query != self.query
|
||||||
|
|
||||||
|
if should_clear_cache():
|
||||||
os.remove(blend_filepath)
|
os.remove(blend_filepath)
|
||||||
|
|
||||||
if not blend_filepath.exists():
|
if not blend_filepath.exists():
|
||||||
@@ -1471,7 +1544,7 @@ def run():
|
|||||||
pprops.project_north = "{pprops.project_north}"
|
pprops.project_north = "{pprops.project_north}"
|
||||||
# Use absolute path to be safe from cwd changes.
|
# Use absolute path to be safe from cwd changes.
|
||||||
try:
|
try:
|
||||||
bpy.ops.bim.load_linked_project(filepath=r"{str(self.filepath_)}")
|
bpy.ops.bim.load_linked_project(filepath=r"{str(self.filepath_)}", query={repr(self.query)})
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
# Operator failed (returned CANCELLED with error report)
|
# Operator failed (returned CANCELLED with error report)
|
||||||
print(f"Failed to load linked project: {{e}}")
|
print(f"Failed to load linked project: {{e}}")
|
||||||
@@ -1557,7 +1630,11 @@ class ReloadLink(bpy.types.Operator):
|
|||||||
bl_label = "Reload Link"
|
bl_label = "Reload Link"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Reload the selected file"
|
bl_description = "Reload the selected file"
|
||||||
link_index: bpy.props.IntProperty(name="Link Index")
|
|
||||||
|
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
link_index: int
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bpy.ops.bim.unload_link(link_index=self.link_index)
|
bpy.ops.bim.unload_link(link_index=self.link_index)
|
||||||
@@ -1569,7 +1646,11 @@ class ToggleLinkSelectability(bpy.types.Operator):
|
|||||||
bl_label = "Toggle Link Selectability"
|
bl_label = "Toggle Link Selectability"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Toggle selectability"
|
bl_description = "Toggle selectability"
|
||||||
link_index: bpy.props.IntProperty(name="Link Index")
|
|
||||||
|
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
link_index: int
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = tool.Project.get_project_props()
|
props = tool.Project.get_project_props()
|
||||||
@@ -1739,7 +1820,11 @@ class SelectLinkHandle(bpy.types.Operator):
|
|||||||
bl_label = "Select Link Handle"
|
bl_label = "Select Link Handle"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Select link empty object handle"
|
bl_description = "Select link empty object handle"
|
||||||
link_index: bpy.props.IntProperty(name="Link Index")
|
|
||||||
|
link_index: bpy.props.IntProperty(name="Link Index") # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
link_index: int
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = tool.Project.get_project_props()
|
props = tool.Project.get_project_props()
|
||||||
@@ -1797,11 +1882,28 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filename_ext = ".ifc"
|
filename_ext = ".ifc"
|
||||||
supported_filexts = (".ifc", ".ifczip", ".ifcjson")
|
supported_filexts = (".ifc", ".ifczip", ".ifcjson")
|
||||||
filter_glob: bpy.props.StringProperty(default=";".join(f"*{ext}" for ext in supported_filexts), options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(
|
||||||
json_version: bpy.props.EnumProperty(items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version")
|
default=";".join(f"*{ext}" for ext in supported_filexts), options={"HIDDEN"}
|
||||||
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
|
) # pyright: ignore[reportRedeclaration]
|
||||||
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False, options={"HIDDEN"})
|
json_version: bpy.props.EnumProperty(
|
||||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version"
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
json_compact: bpy.props.BoolProperty(
|
||||||
|
name="Export Compact IFCJSON", default=False
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
should_save_as: bpy.props.BoolProperty(
|
||||||
|
name="Should Save As", default=False, options={"HIDDEN"}
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
use_relative_path: bpy.props.BoolProperty(
|
||||||
|
name="Use Relative Path", default=False
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
filter_glob: str
|
||||||
|
json_version: str
|
||||||
|
json_compact: bool
|
||||||
|
should_save_as: bool
|
||||||
|
use_relative_path: bool
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -1951,6 +2053,12 @@ class LoadLinkedProject(bpy.types.Operator, ImportHelper):
|
|||||||
bl_description = "Operator is used to load a project .cache.blend to then link it to the IFC file."
|
bl_description = "Operator is used to load a project .cache.blend to then link it to the IFC file."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
query: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||||
|
"""See ``bim.link_ifc``."""
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
query: str
|
||||||
|
|
||||||
file: ifcopenshell.file
|
file: ifcopenshell.file
|
||||||
meshes: dict[str, bpy.types.Mesh]
|
meshes: dict[str, bpy.types.Mesh]
|
||||||
# Material names is derived from diffuse as in 'r-g-b-a'.
|
# Material names is derived from diffuse as in 'r-g-b-a'.
|
||||||
@@ -2000,14 +2108,17 @@ class LoadLinkedProject(bpy.types.Operator, ImportHelper):
|
|||||||
tool.Loader.settings.context_settings = tool.Loader.create_settings()
|
tool.Loader.settings.context_settings = tool.Loader.create_settings()
|
||||||
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
|
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
|
||||||
|
|
||||||
self.elements = set(self.file.by_type("IfcElement"))
|
if self.query:
|
||||||
if self.file.schema in ("IFC2X3", "IFC4"):
|
self.elements = ifcopenshell.util.selector.filter_elements(self.file, self.query)
|
||||||
self.elements |= set(self.file.by_type("IfcProxy"))
|
|
||||||
if self.file.schema == "IFC2X3":
|
|
||||||
self.elements |= set(self.file.by_type("IfcSpatialStructureElement"))
|
|
||||||
else:
|
else:
|
||||||
self.elements |= set(self.file.by_type("IfcSpatialElement"))
|
self.elements = set(self.file.by_type("IfcElement"))
|
||||||
self.elements -= set(self.file.by_type("IfcFeatureElement"))
|
if self.file.schema in ("IFC2X3", "IFC4"):
|
||||||
|
self.elements |= set(self.file.by_type("IfcProxy"))
|
||||||
|
if self.file.schema == "IFC2X3":
|
||||||
|
self.elements |= set(self.file.by_type("IfcSpatialStructureElement"))
|
||||||
|
else:
|
||||||
|
self.elements |= set(self.file.by_type("IfcSpatialElement"))
|
||||||
|
self.elements -= set(self.file.by_type("IfcFeatureElement"))
|
||||||
|
|
||||||
if tool.Loader.settings.false_origin_mode == "MANUAL" and tool.Loader.settings.false_origin:
|
if tool.Loader.settings.false_origin_mode == "MANUAL" and tool.Loader.settings.false_origin:
|
||||||
tool.Loader.set_manual_blender_offset(self.file)
|
tool.Loader.set_manual_blender_offset(self.file)
|
||||||
@@ -2032,6 +2143,7 @@ class LoadLinkedProject(bpy.types.Operator, ImportHelper):
|
|||||||
"false_origin_mode": pprops.false_origin_mode,
|
"false_origin_mode": pprops.false_origin_mode,
|
||||||
"false_origin": pprops.false_origin,
|
"false_origin": pprops.false_origin,
|
||||||
"project_north": pprops.project_north,
|
"project_north": pprops.project_north,
|
||||||
|
"query": self.query,
|
||||||
}
|
}
|
||||||
with open(self.json_filepath, "w") as f:
|
with open(self.json_filepath, "w") as f:
|
||||||
json.dump(data, f)
|
json.dump(data, f)
|
||||||
@@ -2446,7 +2558,7 @@ class EnableCulling(bpy.types.Operator):
|
|||||||
self.total_mousemoves = 0
|
self.total_mousemoves = 0
|
||||||
self.cullable_objects = []
|
self.cullable_objects = []
|
||||||
|
|
||||||
def modal(self, context, event):
|
def modal(self, context, event) -> set["rna_enums.OperatorReturnItems"]:
|
||||||
if not LinksData.enable_culling:
|
if not LinksData.enable_culling:
|
||||||
for obj in bpy.context.visible_objects:
|
for obj in bpy.context.visible_objects:
|
||||||
if obj.type == "MESH" and obj.name.startswith("Ifc"):
|
if obj.type == "MESH" and obj.name.startswith("Ifc"):
|
||||||
@@ -2477,7 +2589,7 @@ class EnableCulling(bpy.types.Operator):
|
|||||||
|
|
||||||
return {"PASS_THROUGH"}
|
return {"PASS_THROUGH"}
|
||||||
|
|
||||||
def is_view_changed(self, context):
|
def is_view_changed(self, context: bpy.types.Context) -> bool:
|
||||||
view_matrix = context.region_data.view_matrix
|
view_matrix = context.region_data.view_matrix
|
||||||
projection_matrix = context.region_data.window_matrix
|
projection_matrix = context.region_data.window_matrix
|
||||||
vp_matrix = projection_matrix @ view_matrix
|
vp_matrix = projection_matrix @ view_matrix
|
||||||
@@ -2492,7 +2604,7 @@ class EnableCulling(bpy.types.Operator):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_object_in_view(self, obj, context, camera_position):
|
def is_object_in_view(self, obj: bpy.types.Object, context: bpy.types.Context, camera_position: Vector) -> bool:
|
||||||
# Get the view matrix and the projection matrix from the active viewport
|
# Get the view matrix and the projection matrix from the active viewport
|
||||||
view_matrix = context.region_data.view_matrix
|
view_matrix = context.region_data.view_matrix
|
||||||
projection_matrix = context.region_data.window_matrix
|
projection_matrix = context.region_data.window_matrix
|
||||||
@@ -2519,7 +2631,7 @@ class EnableCulling(bpy.types.Operator):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -> set["rna_enums.OperatorReturnItems"]:
|
||||||
LinksData.enable_culling = True
|
LinksData.enable_culling = True
|
||||||
self.cullable_objects = []
|
self.cullable_objects = []
|
||||||
for obj in bpy.context.visible_objects:
|
for obj in bpy.context.visible_objects:
|
||||||
@@ -2806,8 +2918,16 @@ class IFCFileHandlerOperator(bpy.types.Operator):
|
|||||||
bl_label = "Import .ifc file"
|
bl_label = "Import .ifc file"
|
||||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||||
|
|
||||||
directory: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"})
|
directory: bpy.props.StringProperty(
|
||||||
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"})
|
subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"}
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
files: bpy.props.CollectionProperty(
|
||||||
|
type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"}
|
||||||
|
) # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
directory: str
|
||||||
|
files: list[bpy.types.OperatorFileListElement]
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
# Keeping code in .invoke() as we'll probably add some
|
# Keeping code in .invoke() as we'll probably add some
|
||||||
@@ -2858,7 +2978,10 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
|
|||||||
bl_label = "Measure Tool"
|
bl_label = "Measure Tool"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
measure_type: bpy.props.StringProperty()
|
measure_type: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
measure_type: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -2954,7 +3077,10 @@ class MeasureFaceAreaTool(bpy.types.Operator, PolylineOperator):
|
|||||||
bl_label = "Measure Face Area Tool"
|
bl_label = "Measure Face Area Tool"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
measure_type: bpy.props.StringProperty()
|
measure_type: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
measure_type: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
@@ -3058,7 +3184,10 @@ class ClearMeasurement(bpy.types.Operator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
polyline_props = tool.Model.get_polyline_props()
|
polyline_props = tool.Model.get_polyline_props()
|
||||||
return len(polyline_props.measurement_polyline) > 0
|
if len(polyline_props.measurement_polyline) > 0:
|
||||||
|
return True
|
||||||
|
cls.poll_message_set("No measurement to clear.")
|
||||||
|
return False
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
polyline_props = tool.Model.get_polyline_props()
|
polyline_props = tool.Model.get_polyline_props()
|
||||||
@@ -3162,7 +3291,7 @@ class ImageScalingTool(bpy.types.Operator, PolylineOperator):
|
|||||||
super().invoke(context, event)
|
super().invoke(context, event)
|
||||||
return {"RUNNING_MODAL"}
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
def cancel_tool(self, context):
|
def cancel_tool(self, context: bpy.types.Context) -> set["rna_enums.OperatorReturnItems"]:
|
||||||
context.workspace.status_text_set(text=None)
|
context.workspace.status_text_set(text=None)
|
||||||
if hasattr(self, "tool_state"):
|
if hasattr(self, "tool_state"):
|
||||||
self.tool_state.plane_method = None
|
self.tool_state.plane_method = None
|
||||||
@@ -3170,7 +3299,7 @@ class ImageScalingTool(bpy.types.Operator, PolylineOperator):
|
|||||||
tool.Blender.update_viewport()
|
tool.Blender.update_viewport()
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
def handle_custom_instructions(self, context):
|
def handle_custom_instructions(self, context: bpy.types.Context) -> None:
|
||||||
if len(self.selected_points) == 0:
|
if len(self.selected_points) == 0:
|
||||||
instruction_text = "Click First Point on Image"
|
instruction_text = "Click First Point on Image"
|
||||||
elif len(self.selected_points) == 1:
|
elif len(self.selected_points) == 1:
|
||||||
@@ -3185,14 +3314,14 @@ class ImageScalingTool(bpy.types.Operator, PolylineOperator):
|
|||||||
|
|
||||||
context.workspace.status_text_set(text=instruction_text)
|
context.workspace.status_text_set(text=instruction_text)
|
||||||
|
|
||||||
def calculate_distance(self):
|
def calculate_distance(self) -> None:
|
||||||
if len(self.selected_points) == 2:
|
if len(self.selected_points) == 2:
|
||||||
point1 = self.selected_points[0]
|
point1 = self.selected_points[0]
|
||||||
point2 = self.selected_points[1]
|
point2 = self.selected_points[1]
|
||||||
distance_3d = (point2 - point1).length
|
distance_3d = (point2 - point1).length
|
||||||
self.calculated_distance = distance_3d / self.unit_scale
|
self.calculated_distance = distance_3d / self.unit_scale
|
||||||
|
|
||||||
def apply_scaling(self, context):
|
def apply_scaling(self, context: bpy.types.Context) -> set["rna_enums.OperatorReturnItems"]:
|
||||||
if len(self.selected_points) != 2:
|
if len(self.selected_points) != 2:
|
||||||
self.report({"ERROR"}, "Two points must be selected")
|
self.report({"ERROR"}, "Two points must be selected")
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
@@ -3250,7 +3379,10 @@ class LoadBlendMetadataAndIFC(bpy.types.Operator):
|
|||||||
bl_idname = "bim.load_blend_metadata_and_ifc"
|
bl_idname = "bim.load_blend_metadata_and_ifc"
|
||||||
bl_label = "Load Blend Metadata and IFC"
|
bl_label = "Load Blend Metadata and IFC"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
filepath: bpy.props.StringProperty(name="IFC File Path", default="")
|
filepath: bpy.props.StringProperty(name="IFC File Path", default="") # pyright: ignore[reportRedeclaration]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
filepath: str
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
ifc_file = self.filepath
|
ifc_file = self.filepath
|
||||||
@@ -3284,3 +3416,19 @@ class LoadBlendMetadataAndIFC(bpy.types.Operator):
|
|||||||
bpy.app.handlers.load_post.append(load_handler)
|
bpy.app.handlers.load_post.append(load_handler)
|
||||||
bpy.ops.wm.open_mainfile(filepath=metadata_path)
|
bpy.ops.wm.open_mainfile(filepath=metadata_path)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateUVMap(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.generate_uv_map"
|
||||||
|
bl_label = "Generate UV Map"
|
||||||
|
bl_description = "Generate UV map for selected mesh."
|
||||||
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
obj = context.active_object
|
||||||
|
if not obj or not isinstance(obj.data, bpy.types.Mesh):
|
||||||
|
self.report({"ERROR"}, "No valid mesh selected.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
tool.Loader.load_generated_uv_map(obj.data)
|
||||||
|
self.report({"INFO"}, "Generated UV map for selected mesh.")
|
||||||
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ class BIM_PT_links(Panel):
|
|||||||
row.operator("bim.reload_link", text="", icon="FILE_REFRESH").link_index = index
|
row.operator("bim.reload_link", text="", icon="FILE_REFRESH").link_index = index
|
||||||
else:
|
else:
|
||||||
row.operator("bim.load_link", text="", icon="LINKED").link_index = index
|
row.operator("bim.load_link", text="", icon="LINKED").link_index = index
|
||||||
row.operator("bim.unlink_ifc", text="", icon="X").link_index = index
|
row.operator("bim.unlink_ifc", text="", icon="X").link_index = index
|
||||||
self.layout.template_list("BIM_UL_links", "", self.props, "links", self.props, "active_link_index")
|
self.layout.template_list("BIM_UL_links", "", self.props, "links", self.props, "active_link_index")
|
||||||
|
|
||||||
if LinksData.enable_culling:
|
if LinksData.enable_culling:
|
||||||
|
|||||||
@@ -71,24 +71,26 @@ class ExploreTool(bpy.types.WorkSpaceTool):
|
|||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
row.label(text="", icon="EVENT_M")
|
row.label(text="", icon="EVENT_M")
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.explore_hotkey", text="Measure Tool", icon="CON_DISTLIMIT")
|
op = row.operator("bim.explore_hotkey", text="Measure Tool", icon="CON_DISTLIMIT")
|
||||||
op.hotkey = "S_M"
|
op.hotkey = "S_M"
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(prop, "measurement_type", text="Measure Type", expand=True, icon_only=True, emboss=True)
|
row.prop(prop, "measurement_type", text="Measure Type", expand=True, icon_only=True, emboss=True)
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.clear_measurement", text="", icon="X")
|
op = row.operator("bim.clear_measurement", text="", icon="X")
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
row.label(text="", icon="EVENT_S")
|
row.label(text="", icon="EVENT_S")
|
||||||
row = layout.row(align=True)
|
|
||||||
op = row.operator("bim.explore_hotkey", text="Image Scaling Tool", icon="IMAGE_PLANE")
|
op = row.operator("bim.explore_hotkey", text="Image Scaling Tool", icon="IMAGE_PLANE")
|
||||||
op.hotkey = "S_S"
|
op.hotkey = "S_S"
|
||||||
op.description = "Scale Image Annotation. Allows to scale an IfcReferenceImage. Select image, select tool. Check lower left corner instructions to select two points and provide real distance between them"
|
op.description = (
|
||||||
|
"Scale Image Annotation.\n\n"
|
||||||
|
"Allows to scale an IfcReferenceImage.\n\n"
|
||||||
|
"Select image, select tool. "
|
||||||
|
"Check lower left corner instructions to select two points and provide real distance between them"
|
||||||
|
)
|
||||||
|
|
||||||
op = row.operator("bim.generate_uv_map", text="Generate UV Map", icon="UV")
|
row = layout.row(align=True)
|
||||||
op.description = "Generate UV map for selected mesh."
|
row.operator("bim.generate_uv_map", icon="UV")
|
||||||
|
|
||||||
|
|
||||||
class ExploreHotkey(bpy.types.Operator):
|
class ExploreHotkey(bpy.types.Operator):
|
||||||
@@ -156,20 +158,3 @@ class ExploreHotkey(bpy.types.Operator):
|
|||||||
|
|
||||||
def hotkey_A_H(self) -> None:
|
def hotkey_A_H(self) -> None:
|
||||||
bpy.ops.bim.hide_queried_linked_element(unhide_all=True)
|
bpy.ops.bim.hide_queried_linked_element(unhide_all=True)
|
||||||
|
|
||||||
|
|
||||||
class GenerateUVMap(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.generate_uv_map"
|
|
||||||
bl_label = "Generate UV Map"
|
|
||||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
|
||||||
|
|
||||||
description: bpy.props.StringProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
obj = context.active_object
|
|
||||||
if not obj or not hasattr(obj, "data") or not hasattr(obj.data, "polygons"):
|
|
||||||
self.report({"ERROR"}, "No valid mesh selected.")
|
|
||||||
return {"CANCELLED"}
|
|
||||||
tool.Loader.load_generated_uv_map(obj.data)
|
|
||||||
self.report({"INFO"}, "Generated UV map for selected mesh.")
|
|
||||||
return {"FINISHED"}
|
|
||||||
@@ -365,7 +365,7 @@ class TestLoadingIfcSqlite(NewFile):
|
|||||||
sql_type="SQLite",
|
sql_type="SQLite",
|
||||||
)
|
)
|
||||||
patcher.patch()
|
patcher.patch()
|
||||||
tmp_file = Path(tempfile.mktemp(suffix=".ifcsqlite"))
|
tmp_file = Path(tempfile.mkstemp(suffix=".ifcsqlite")[1])
|
||||||
ifcpatch.write(patcher.get_output(), tmp_file)
|
ifcpatch.write(patcher.get_output(), tmp_file)
|
||||||
|
|
||||||
elements_with_meshes = [
|
elements_with_meshes = [
|
||||||
|
|||||||
@@ -53,9 +53,7 @@ def create_file(version: ifcopenshell.util.schema.IFC_SCHEMA = "IFC4") -> ifcope
|
|||||||
"""
|
"""
|
||||||
file = ifcopenshell.file(schema=version)
|
file = ifcopenshell.file(schema=version)
|
||||||
file.header.file_name.name = "/dev/null" # Hehehe
|
file.header.file_name.name = "/dev/null" # Hehehe
|
||||||
file.header.file_name.time_stamp = (
|
file.header.file_name.time_stamp = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
|
||||||
datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()
|
|
||||||
)
|
|
||||||
file.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
|
file.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
|
||||||
file.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version)
|
file.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version)
|
||||||
file.header.file_name.authorization = "Nobody"
|
file.header.file_name.authorization = "Nobody"
|
||||||
|
|||||||
@@ -57,13 +57,7 @@ class Patcher:
|
|||||||
|
|
||||||
def patch(self):
|
def patch(self):
|
||||||
self.file.header.file_name.name = "Rabbit"
|
self.file.header.file_name.name = "Rabbit"
|
||||||
self.file.header.file_name.time_stamp = (
|
self.file.header.file_name.time_stamp = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
|
||||||
datetime.datetime.utcnow()
|
|
||||||
.replace(tzinfo=datetime.timezone.utc)
|
|
||||||
.astimezone()
|
|
||||||
.replace(microsecond=0)
|
|
||||||
.isoformat()
|
|
||||||
)
|
|
||||||
self.file.header.file_name.preprocessor_version = "Rabbit"
|
self.file.header.file_name.preprocessor_version = "Rabbit"
|
||||||
self.file.header.file_name.originating_system = "Rabbit"
|
self.file.header.file_name.originating_system = "Rabbit"
|
||||||
|
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ class Test:
|
|||||||
assert output.by_type("IfcProject")[0].GlobalId == project.GlobalId
|
assert output.by_type("IfcProject")[0].GlobalId == project.GlobalId
|
||||||
assert output.by_type("IfcWall")[0].GlobalId == wall.GlobalId
|
assert output.by_type("IfcWall")[0].GlobalId == wall.GlobalId
|
||||||
|
|
||||||
output_path = Path(tempfile.mktemp())
|
output_path = Path(tempfile.mkstemp()[1])
|
||||||
try:
|
try:
|
||||||
assert not output_path.exists()
|
assert output_path.stat().st_size == 0
|
||||||
ifcpatch.write(patcher.get_output(), output_path)
|
ifcpatch.write(patcher.get_output(), output_path)
|
||||||
assert output_path.exists()
|
assert output_path.stat().st_size != 0
|
||||||
finally:
|
finally:
|
||||||
output_path.unlink()
|
output_path.unlink()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
from database.neo4j import db
|
from database.neo4j import db
|
||||||
from fastapi import Depends, HTTPException, Security, status
|
from fastapi import Depends, HTTPException, Security, status
|
||||||
@@ -33,10 +33,8 @@ credentials_exception = HTTPException(
|
|||||||
|
|
||||||
def create_access_token(data: dict, expires_delta: timedelta | None = None):
|
def create_access_token(data: dict, expires_delta: timedelta | None = None):
|
||||||
payload = data.copy()
|
payload = data.copy()
|
||||||
if expires_delta:
|
expires_delta = expires_delta or timedelta(minutes=15)
|
||||||
expire = datetime.utcnow() + expires_delta
|
expire = datetime.now(timezone.utc) + expires_delta
|
||||||
else:
|
|
||||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
|
||||||
payload.update({"expires": str(expire)})
|
payload.update({"expires": str(expire)})
|
||||||
encoded_jwt = jwt.encode(payload, secrets["security_secret_key"], algorithm=os.environ["SECURITY_ALGORITHM"])
|
encoded_jwt = jwt.encode(payload, secrets["security_secret_key"], algorithm=os.environ["SECURITY_ALGORITHM"])
|
||||||
return encoded_jwt
|
return encoded_jwt
|
||||||
|
|||||||
Reference in New Issue
Block a user