|
|
|
@@ -178,9 +178,18 @@ class SelectLibraryFile(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
|
|
|
|
bl_description = (
|
|
|
|
|
"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"})
|
|
|
|
|
append_all: bpy.props.BoolProperty(default=False)
|
|
|
|
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
|
|
|
|
filter_glob: bpy.props.StringProperty(
|
|
|
|
|
default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"}
|
|
|
|
|
) # 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
|
|
|
|
|
|
|
|
|
@@ -558,7 +567,11 @@ class AppendEntireLibrary(bpy.types.Operator, tool.Ifc.Operator):
|
|
|
|
|
class AppendLibraryElementByQuery(bpy.types.Operator, tool.Ifc.Operator):
|
|
|
|
|
bl_idname = "bim.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
|
|
|
|
|
def poll(cls, context):
|
|
|
|
@@ -587,9 +600,16 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
|
|
|
|
"Append element to the current project.\n\n"
|
|
|
|
|
"ALT+CLICK to skip reusing materials, profiles, styles based on their name (may result in duplicates)"
|
|
|
|
|
)
|
|
|
|
|
definition: bpy.props.IntProperty()
|
|
|
|
|
prop_index: bpy.props.IntProperty()
|
|
|
|
|
assume_unique_by_name: bpy.props.BoolProperty(name="Assume Unique By Name", default=True, options={"SKIP_SAVE"})
|
|
|
|
|
definition: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
|
|
|
|
|
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"}
|
|
|
|
|
) # pyright: ignore[reportRedeclaration]
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
definition: int
|
|
|
|
|
prop_index: int
|
|
|
|
|
assume_unique_by_name: bool
|
|
|
|
|
|
|
|
|
|
file: ifcopenshell.file
|
|
|
|
|
|
|
|
|
@@ -939,24 +959,28 @@ class LoadProject(bpy.types.Operator, IFCFileSelector, ImportHelper):
|
|
|
|
|
bl_label = "Load Project"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
bl_description = "Load an existing IFC project"
|
|
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE"})
|
|
|
|
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcsqlite", options={"HIDDEN"})
|
|
|
|
|
is_advanced: bpy.props.BoolProperty(
|
|
|
|
|
filepath: bpy.props.StringProperty(
|
|
|
|
|
subtype="FILE_PATH", options={"SKIP_SAVE"}
|
|
|
|
|
) # 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",
|
|
|
|
|
description="Load IFC file with advanced settings. Checking this option will skip loading IFC file and will open advanced load settings",
|
|
|
|
|
default=False,
|
|
|
|
|
)
|
|
|
|
|
use_relative_path: bpy.props.BoolProperty(
|
|
|
|
|
use_relative_path: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
|
|
|
|
name="Use Relative Path",
|
|
|
|
|
description="Store the IFC project path relative to the .blend file. Requires .blend file to be saved",
|
|
|
|
|
default=False,
|
|
|
|
|
)
|
|
|
|
|
should_start_fresh_session: bpy.props.BoolProperty(
|
|
|
|
|
should_start_fresh_session: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
|
|
|
|
name="Should Start Fresh Session",
|
|
|
|
|
description="Clear current Blender session before loading IFC. Not supported with 'Use Relative Path' option",
|
|
|
|
|
default=True,
|
|
|
|
|
)
|
|
|
|
|
import_without_ifc_data: bpy.props.BoolProperty(
|
|
|
|
|
import_without_ifc_data: bpy.props.BoolProperty( # pyright: ignore[reportRedeclaration]
|
|
|
|
|
name="Import Without IFC Data",
|
|
|
|
|
description=(
|
|
|
|
|
"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,
|
|
|
|
|
)
|
|
|
|
|
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"
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
def description(cls, context, properties):
|
|
|
|
|
tooltip = cls.bl_description
|
|
|
|
@@ -1265,7 +1300,10 @@ class ToggleFilterCategories(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.toggle_filter_categories"
|
|
|
|
|
bl_label = "Toggle Filter Categories"
|
|
|
|
|
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):
|
|
|
|
|
props = tool.Project.get_project_props()
|
|
|
|
@@ -1289,6 +1327,14 @@ class LinkIfc(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
|
|
|
|
default=False,
|
|
|
|
|
)
|
|
|
|
|
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"
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
@@ -1298,20 +1344,25 @@ class LinkIfc(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
|
|
|
|
filter_glob: str
|
|
|
|
|
use_relative_path: bool
|
|
|
|
|
use_cache: bool
|
|
|
|
|
query: str
|
|
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
assert self.layout
|
|
|
|
|
pprops = tool.Project.get_project_props()
|
|
|
|
|
row = self.layout.row()
|
|
|
|
|
row.prop(self, "use_relative_path")
|
|
|
|
|
row = self.layout.row()
|
|
|
|
|
row.prop(self, "use_cache")
|
|
|
|
|
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":
|
|
|
|
|
row = self.layout.row()
|
|
|
|
|
row.prop(pprops, "false_origin")
|
|
|
|
|
row = self.layout.row()
|
|
|
|
|
row.prop(pprops, "project_north")
|
|
|
|
|
self.layout.prop(self, "query", placeholder="IfcElement")
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
|
|
|
|
start = time.time()
|
|
|
|
@@ -1344,7 +1395,7 @@ class LinkIfc(bpy.types.Operator, ImportHelper, tool.Ifc.Operator):
|
|
|
|
|
new.ifc_definition_id = reference.id()
|
|
|
|
|
new.name = 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):
|
|
|
|
@@ -1352,7 +1403,11 @@ class UnlinkIfc(bpy.types.Operator, tool.Ifc.Operator):
|
|
|
|
|
bl_label = "Unlink IFC"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
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):
|
|
|
|
|
props = tool.Project.get_project_props()
|
|
|
|
@@ -1372,7 +1427,11 @@ class UnloadLink(bpy.types.Operator, tool.Ifc.Operator):
|
|
|
|
|
bl_label = "Unload Link"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
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):
|
|
|
|
|
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]
|
|
|
|
|
use_cache: bpy.props.BoolProperty(name="Use Cache", default=True) # pyright: ignore[reportRedeclaration]
|
|
|
|
|
query: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
link_index: int
|
|
|
|
|
use_cache: bool
|
|
|
|
|
query: str
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
|
|
|
|
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]:
|
|
|
|
|
blend_filepath = self.filepath_.with_suffix(".ifc.cache.blend")
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
if not blend_filepath.exists():
|
|
|
|
@@ -1471,7 +1544,7 @@ def run():
|
|
|
|
|
pprops.project_north = "{pprops.project_north}"
|
|
|
|
|
# Use absolute path to be safe from cwd changes.
|
|
|
|
|
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:
|
|
|
|
|
# Operator failed (returned CANCELLED with error report)
|
|
|
|
|
print(f"Failed to load linked project: {{e}}")
|
|
|
|
@@ -1557,7 +1630,11 @@ class ReloadLink(bpy.types.Operator):
|
|
|
|
|
bl_label = "Reload Link"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
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):
|
|
|
|
|
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_options = {"REGISTER", "UNDO"}
|
|
|
|
|
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):
|
|
|
|
|
props = tool.Project.get_project_props()
|
|
|
|
@@ -1739,7 +1820,11 @@ class SelectLinkHandle(bpy.types.Operator):
|
|
|
|
|
bl_label = "Select Link Handle"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
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):
|
|
|
|
|
props = tool.Project.get_project_props()
|
|
|
|
@@ -1797,11 +1882,28 @@ class ExportIFC(bpy.types.Operator, ExportHelper):
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
filename_ext = ".ifc"
|
|
|
|
|
supported_filexts = (".ifc", ".ifczip", ".ifcjson")
|
|
|
|
|
filter_glob: bpy.props.StringProperty(default=";".join(f"*{ext}" for ext in supported_filexts), options={"HIDDEN"})
|
|
|
|
|
json_version: bpy.props.EnumProperty(items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version")
|
|
|
|
|
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
|
|
|
|
|
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False, options={"HIDDEN"})
|
|
|
|
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
|
|
|
|
filter_glob: bpy.props.StringProperty(
|
|
|
|
|
default=";".join(f"*{ext}" for ext in supported_filexts), options={"HIDDEN"}
|
|
|
|
|
) # pyright: ignore[reportRedeclaration]
|
|
|
|
|
json_version: bpy.props.EnumProperty(
|
|
|
|
|
items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version"
|
|
|
|
|
) # pyright: ignore[reportRedeclaration]
|
|
|
|
|
json_compact: bpy.props.BoolProperty(
|
|
|
|
|
name="Export Compact IFCJSON", default=False
|
|
|
|
|
) # 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
|
|
|
|
|
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_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
|
|
query: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
|
|
|
|
"""See ``bim.link_ifc``."""
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
query: str
|
|
|
|
|
|
|
|
|
|
file: ifcopenshell.file
|
|
|
|
|
meshes: dict[str, bpy.types.Mesh]
|
|
|
|
|
# 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.gross_context_settings = tool.Loader.create_settings(is_gross=True)
|
|
|
|
|
|
|
|
|
|
self.elements = set(self.file.by_type("IfcElement"))
|
|
|
|
|
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"))
|
|
|
|
|
if self.query:
|
|
|
|
|
self.elements = ifcopenshell.util.selector.filter_elements(self.file, self.query)
|
|
|
|
|
else:
|
|
|
|
|
self.elements |= set(self.file.by_type("IfcSpatialElement"))
|
|
|
|
|
self.elements -= set(self.file.by_type("IfcFeatureElement"))
|
|
|
|
|
self.elements = set(self.file.by_type("IfcElement"))
|
|
|
|
|
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:
|
|
|
|
|
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": pprops.false_origin,
|
|
|
|
|
"project_north": pprops.project_north,
|
|
|
|
|
"query": self.query,
|
|
|
|
|
}
|
|
|
|
|
with open(self.json_filepath, "w") as f:
|
|
|
|
|
json.dump(data, f)
|
|
|
|
@@ -2446,7 +2558,7 @@ class EnableCulling(bpy.types.Operator):
|
|
|
|
|
self.total_mousemoves = 0
|
|
|
|
|
self.cullable_objects = []
|
|
|
|
|
|
|
|
|
|
def modal(self, context, event):
|
|
|
|
|
def modal(self, context, event) -> set["rna_enums.OperatorReturnItems"]:
|
|
|
|
|
if not LinksData.enable_culling:
|
|
|
|
|
for obj in bpy.context.visible_objects:
|
|
|
|
|
if obj.type == "MESH" and obj.name.startswith("Ifc"):
|
|
|
|
@@ -2477,7 +2589,7 @@ class EnableCulling(bpy.types.Operator):
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
projection_matrix = context.region_data.window_matrix
|
|
|
|
|
vp_matrix = projection_matrix @ view_matrix
|
|
|
|
@@ -2492,7 +2604,7 @@ class EnableCulling(bpy.types.Operator):
|
|
|
|
|
return True
|
|
|
|
|
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
|
|
|
|
|
view_matrix = context.region_data.view_matrix
|
|
|
|
|
projection_matrix = context.region_data.window_matrix
|
|
|
|
@@ -2519,7 +2631,7 @@ class EnableCulling(bpy.types.Operator):
|
|
|
|
|
return False
|
|
|
|
|
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
|
|
|
|
|
self.cullable_objects = []
|
|
|
|
|
for obj in bpy.context.visible_objects:
|
|
|
|
@@ -2806,8 +2918,16 @@ class IFCFileHandlerOperator(bpy.types.Operator):
|
|
|
|
|
bl_label = "Import .ifc file"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
|
|
|
|
|
|
|
|
|
directory: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"})
|
|
|
|
|
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"})
|
|
|
|
|
directory: bpy.props.StringProperty(
|
|
|
|
|
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):
|
|
|
|
|
# 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_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
|
|
measure_type: bpy.props.StringProperty()
|
|
|
|
|
measure_type: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
measure_type: str
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
@@ -2954,7 +3077,10 @@ class MeasureFaceAreaTool(bpy.types.Operator, PolylineOperator):
|
|
|
|
|
bl_label = "Measure Face Area Tool"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
|
|
measure_type: bpy.props.StringProperty()
|
|
|
|
|
measure_type: bpy.props.StringProperty() # pyright: ignore[reportRedeclaration]
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
measure_type: str
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
@@ -3058,7 +3184,10 @@ class ClearMeasurement(bpy.types.Operator):
|
|
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
|
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):
|
|
|
|
|
polyline_props = tool.Model.get_polyline_props()
|
|
|
|
@@ -3162,7 +3291,7 @@ class ImageScalingTool(bpy.types.Operator, PolylineOperator):
|
|
|
|
|
super().invoke(context, event)
|
|
|
|
|
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)
|
|
|
|
|
if hasattr(self, "tool_state"):
|
|
|
|
|
self.tool_state.plane_method = None
|
|
|
|
@@ -3170,7 +3299,7 @@ class ImageScalingTool(bpy.types.Operator, PolylineOperator):
|
|
|
|
|
tool.Blender.update_viewport()
|
|
|
|
|
return {"CANCELLED"}
|
|
|
|
|
|
|
|
|
|
def handle_custom_instructions(self, context):
|
|
|
|
|
def handle_custom_instructions(self, context: bpy.types.Context) -> None:
|
|
|
|
|
if len(self.selected_points) == 0:
|
|
|
|
|
instruction_text = "Click First Point on Image"
|
|
|
|
|
elif len(self.selected_points) == 1:
|
|
|
|
@@ -3185,14 +3314,14 @@ class ImageScalingTool(bpy.types.Operator, PolylineOperator):
|
|
|
|
|
|
|
|
|
|
context.workspace.status_text_set(text=instruction_text)
|
|
|
|
|
|
|
|
|
|
def calculate_distance(self):
|
|
|
|
|
def calculate_distance(self) -> None:
|
|
|
|
|
if len(self.selected_points) == 2:
|
|
|
|
|
point1 = self.selected_points[0]
|
|
|
|
|
point2 = self.selected_points[1]
|
|
|
|
|
distance_3d = (point2 - point1).length
|
|
|
|
|
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:
|
|
|
|
|
self.report({"ERROR"}, "Two points must be selected")
|
|
|
|
|
return {"CANCELLED"}
|
|
|
|
@@ -3250,7 +3379,10 @@ class LoadBlendMetadataAndIFC(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.load_blend_metadata_and_ifc"
|
|
|
|
|
bl_label = "Load Blend Metadata and IFC"
|
|
|
|
|
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):
|
|
|
|
|
ifc_file = self.filepath
|
|
|
|
@@ -3284,3 +3416,19 @@ class LoadBlendMetadataAndIFC(bpy.types.Operator):
|
|
|
|
|
bpy.app.handlers.load_post.append(load_handler)
|
|
|
|
|
bpy.ops.wm.open_mainfile(filepath=metadata_path)
|
|
|
|
|
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"}
|
|
|
|
|