mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-14 03:14:23 +00:00
Merge branch 'v0.8.0' into orientation_slots
This commit is contained in:
@@ -115,10 +115,8 @@ classes = [
|
||||
operator.ReloadIfcFile,
|
||||
operator.RemoveIfcFile,
|
||||
operator.RevertClippingPlaneCut,
|
||||
operator.SelectDataDir,
|
||||
operator.SelectCacheDir,
|
||||
operator.SelectDir,
|
||||
operator.SelectIfcFile,
|
||||
operator.SelectSchemaDir,
|
||||
operator.SelectURIAttribute,
|
||||
operator.SetTab,
|
||||
operator.SwitchTab,
|
||||
|
||||
@@ -132,7 +132,8 @@ class IfcExporter:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
def has_changed_materials(self, obj: bpy.types.Object) -> bool:
|
||||
checksum = obj.data.BIMMeshProperties.material_checksum
|
||||
mprops = tool.Geometry.get_mesh_props(obj.data)
|
||||
checksum = mprops.material_checksum
|
||||
return checksum != tool.Geometry.get_material_checksum(obj)
|
||||
|
||||
def sync_object_placement(self, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
|
||||
@@ -60,7 +60,7 @@ def name_callback(obj: Union[bpy.types.Object, bpy.types.Material], data: str) -
|
||||
if props.is_renaming:
|
||||
props.is_renmaing = False
|
||||
return
|
||||
IfcStore.get_file().by_id(ifc_definition_id).Name = obj.name
|
||||
tool.Ifc.get().by_id(ifc_definition_id).Name = obj.name
|
||||
refresh_ui_data()
|
||||
return
|
||||
|
||||
@@ -71,7 +71,7 @@ def name_callback(obj: Union[bpy.types.Object, bpy.types.Material], data: str) -
|
||||
obj.BIMObjectProperties.is_renaming = False
|
||||
return
|
||||
|
||||
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
element = tool.Ifc.get().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
if "/" in obj.name:
|
||||
object_name = obj.name
|
||||
element_name = obj.name.split("/", 1)[1]
|
||||
@@ -221,35 +221,38 @@ def refresh_ui_data():
|
||||
if isinstance(tool.Ifc.get(), ifcopenshell.sqlite):
|
||||
tool.Ifc.get().clear_cache()
|
||||
|
||||
bpy.context.scene.DocProperties.should_draw_decorations = bpy.context.scene.DocProperties.should_draw_decorations
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.should_draw_decorations = props.should_draw_decorations
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
tool.Web.send_webui_data()
|
||||
|
||||
|
||||
@persistent
|
||||
def loadIfcStore(scene):
|
||||
def loadIfcStore(scene: bpy.types.Scene) -> None:
|
||||
IfcStore.purge()
|
||||
refresh_ui_data()
|
||||
if not IfcStore.get_file():
|
||||
if not tool.Ifc.get():
|
||||
return
|
||||
IfcStore.get_schema()
|
||||
tool.Ifc.schema()
|
||||
IfcStore.relink_all_objects()
|
||||
|
||||
|
||||
@persistent
|
||||
def undo_post(scene):
|
||||
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
|
||||
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||
IfcStore.undo(until_key=bpy.context.scene.BIMProperties.last_transaction)
|
||||
def undo_post(scene: bpy.types.Scene) -> None:
|
||||
props = tool.Blender.get_bim_props()
|
||||
if IfcStore.last_transaction != props.last_transaction:
|
||||
IfcStore.last_transaction = props.last_transaction
|
||||
IfcStore.undo(until_key=props.last_transaction)
|
||||
refresh_ui_data()
|
||||
tool.Ifc.rebuild_element_maps()
|
||||
|
||||
|
||||
@persistent
|
||||
def redo_post(scene):
|
||||
if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction:
|
||||
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||
IfcStore.redo(until_key=bpy.context.scene.BIMProperties.last_transaction)
|
||||
def redo_post(scene: bpy.types.Scene) -> None:
|
||||
props = tool.Blender.get_bim_props()
|
||||
if IfcStore.last_transaction != props.last_transaction:
|
||||
IfcStore.last_transaction = props.last_transaction
|
||||
IfcStore.redo(until_key=props.last_transaction)
|
||||
refresh_ui_data()
|
||||
tool.Ifc.rebuild_element_maps()
|
||||
|
||||
@@ -282,7 +285,7 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None
|
||||
return pao
|
||||
|
||||
|
||||
def viewport_shading_changed_callback(area):
|
||||
def viewport_shading_changed_callback(area: bpy.types.Area) -> None:
|
||||
shading = area.spaces.active.shading.type
|
||||
if shading == "RENDERED":
|
||||
bpy.context.scene.BIMStylesProperties.active_style_type = "External"
|
||||
@@ -340,10 +343,11 @@ def load_post(scene):
|
||||
tool.Blender.setup_tabs()
|
||||
|
||||
if tool.Ifc.get() and bpy.data.is_saved:
|
||||
bpy.context.scene.BIMProperties.has_blend_warning = True
|
||||
props = tool.Blender.get_bim_props()
|
||||
props.has_blend_warning = True
|
||||
|
||||
# Bonsai overlays
|
||||
georeference_props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
georeference_props = tool.Georeference.get_georeference_props()
|
||||
aggregate_props = bpy.context.scene.BIMAggregateProperties
|
||||
nest_props = bpy.context.scene.BIMNestProperties
|
||||
model_props = tool.Model.get_model_props()
|
||||
|
||||
@@ -27,12 +27,12 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.doc import get_attribute_doc, get_predefined_type_doc, get_property_doc
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from typing import Optional, Callable, Any, Union, Iterable, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bonsai.bim.prop
|
||||
from bonsai.bim.prop import Attribute
|
||||
from bonsai.bim.module.search.prop import BIMFilterGroup
|
||||
|
||||
# ImportCallback return values:
|
||||
# - None - property should be imported by default workflow
|
||||
@@ -119,7 +119,8 @@ def import_attributes(
|
||||
data: dict[str, Any],
|
||||
callback: Optional[ImportCallback] = None,
|
||||
) -> None:
|
||||
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
|
||||
schema = tool.Ifc.schema()
|
||||
for attribute in schema.declaration_by_name(ifc_class).all_attributes():
|
||||
import_attribute(attribute, props, data, callback=callback)
|
||||
|
||||
|
||||
@@ -372,11 +373,16 @@ def convert_property_group_from_si(property_group: bpy.types.PropertyGroup, skip
|
||||
setattr(property_group, prop_name, prop_value)
|
||||
|
||||
|
||||
def draw_filter(layout: bpy.types.UILayout, filter_groups, data, module: str) -> None:
|
||||
def draw_filter(
|
||||
layout: bpy.types.UILayout,
|
||||
filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup],
|
||||
data,
|
||||
module: str,
|
||||
) -> None:
|
||||
if not data.is_loaded:
|
||||
data.load()
|
||||
|
||||
sprops = bpy.context.scene.BIMSearchProperties
|
||||
sprops = tool.Search.get_search_props()
|
||||
|
||||
if tool.Ifc.get():
|
||||
row = layout.row(align=True)
|
||||
|
||||
@@ -105,7 +105,8 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def get_file():
|
||||
if IfcStore.file is None:
|
||||
IfcStore.path = cast(str, bpy.context.scene.BIMProperties.ifc_file)
|
||||
props = tool.Blender.get_bim_props()
|
||||
IfcStore.path = props.ifc_file
|
||||
# Interpret relative paths as relative to .blend file.
|
||||
if IfcStore.path and not os.path.isabs(IfcStore.path):
|
||||
IfcStore.path = os.path.abspath(os.path.join(bpy.path.abspath("//"), IfcStore.path))
|
||||
@@ -119,10 +120,11 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def get_cache():
|
||||
if IfcStore.cache is None and IfcStore.path:
|
||||
props = tool.Blender.get_bim_props()
|
||||
ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp
|
||||
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
|
||||
os.makedirs(bpy.context.scene.BIMProperties.cache_dir, exist_ok=True)
|
||||
IfcStore.cache_path = os.path.join(bpy.context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5")
|
||||
os.makedirs(props.cache_dir, exist_ok=True)
|
||||
IfcStore.cache_path = os.path.join(props.cache_dir, f"{ifc_hash}.h5")
|
||||
cache_path = Path(IfcStore.cache_path)
|
||||
cache_settings = ifcopenshell.geom.settings()
|
||||
serializer_settings = ifcopenshell.geom.serializer_settings()
|
||||
@@ -162,7 +164,8 @@ class IfcStore:
|
||||
assert IfcStore.file
|
||||
ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp
|
||||
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
|
||||
new_cache_path = os.path.join(bpy.context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5")
|
||||
props = tool.Blender.get_bim_props()
|
||||
new_cache_path = os.path.join(props.cache_dir, f"{ifc_hash}.h5")
|
||||
IfcStore.cache = None
|
||||
try:
|
||||
shutil.move(IfcStore.cache_path, new_cache_path)
|
||||
@@ -178,6 +181,7 @@ class IfcStore:
|
||||
if not os.path.isfile(path):
|
||||
return
|
||||
extension = path.split(".")[-1]
|
||||
props = tool.Project.get_project_props()
|
||||
if extension.lower() == "ifczip":
|
||||
with tempfile.TemporaryDirectory() as unzipped_path:
|
||||
with zipfile.ZipFile(path, "r") as zip_ref:
|
||||
@@ -187,7 +191,7 @@ class IfcStore:
|
||||
return
|
||||
elif extension.lower() == "ifcxml":
|
||||
IfcStore.file = ifcopenshell.file(ifcopenshell.ifcopenshell_wrapper.parse_ifcxml(path))
|
||||
elif bpy.context.scene.BIMProjectProperties.should_stream:
|
||||
elif props.should_stream:
|
||||
IfcStore.file = ifcopenshell.open(path, should_stream=True)
|
||||
else:
|
||||
IfcStore.file = ifcopenshell.open(path)
|
||||
@@ -195,9 +199,8 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def get_schema() -> ifcopenshell.ifcopenshell_wrapper.schema_definition:
|
||||
if IfcStore.file is None:
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(
|
||||
bpy.context.scene.BIMProjectProperties.export_schema
|
||||
)
|
||||
props = tool.Project.get_project_props()
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(props.export_schema)
|
||||
elif IfcStore.schema is None:
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema_identifier)
|
||||
return IfcStore.schema
|
||||
@@ -247,7 +250,7 @@ class IfcStore:
|
||||
# refactor this class and deprecate usage of IfcStore in favour of
|
||||
# tools.
|
||||
if not isinstance(obj, (bpy.types.Object, bpy.types.Material)):
|
||||
obj.BIMMeshProperties.ifc_definition_id = element.id()
|
||||
tool.Geometry.get_mesh_props(obj).ifc_definition_id = element.id()
|
||||
return
|
||||
|
||||
existing_obj = IfcStore.id_map.get(element.id(), None)
|
||||
@@ -414,7 +417,8 @@ class IfcStore:
|
||||
method: Literal["EXECUTE", "INVOKE", "MODAL"] = "EXECUTE",
|
||||
) -> set[str]:
|
||||
bonsai.last_actions.append({"type": "operator", "name": operator.bl_idname})
|
||||
bpy.context.scene.BIMProperties.is_dirty = True
|
||||
props = tool.Blender.get_bim_props()
|
||||
props.is_dirty = True
|
||||
# Modals don't nest, and Blender handles the loop that continuously calls modal()
|
||||
is_top_level_operator = not bool(IfcStore.current_transaction) or (method == "MODAL")
|
||||
|
||||
@@ -493,7 +497,8 @@ class IfcStore:
|
||||
) -> None:
|
||||
key = getattr(operator, "transaction_key", None)
|
||||
data = getattr(operator, "transaction_data", None)
|
||||
bpy.context.scene.BIMProperties.last_transaction = key
|
||||
props = tool.Blender.get_bim_props()
|
||||
props.last_transaction = key
|
||||
IfcStore.last_transaction = key
|
||||
rollback = rollback or getattr(operator, "rollback", lambda data: True)
|
||||
commit = commit or getattr(operator, "commit", lambda data: True)
|
||||
|
||||
@@ -69,6 +69,9 @@ class MaterialCreator:
|
||||
if isinstance(mesh, bpy.types.Curve):
|
||||
return
|
||||
|
||||
if mesh.get("has_layer_styles", None) == True:
|
||||
return
|
||||
|
||||
self.mesh = mesh
|
||||
self.obj = obj
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
@@ -90,7 +93,8 @@ class MaterialCreator:
|
||||
return # Already has materials assign to the representation itself
|
||||
# Otherwise, we need to check for material styles on the element, since
|
||||
# create_shape on types only works on representations.
|
||||
context = tool.Ifc.get().by_id(self.mesh.BIMMeshProperties.ifc_definition_id).ContextOfItems
|
||||
mprops = tool.Geometry.get_mesh_props(self.mesh)
|
||||
context = tool.Ifc.get().by_id(mprops.ifc_definition_id).ContextOfItems
|
||||
for material in ifcopenshell.util.element.get_materials(element):
|
||||
if style := ifcopenshell.util.representation.get_material_style(material, context):
|
||||
self.mesh["ios_materials"] = (style.id(),)
|
||||
@@ -218,7 +222,7 @@ class IfcImporter:
|
||||
self.progress = 0
|
||||
|
||||
self.material_creator = MaterialCreator(ifc_import_settings, self)
|
||||
classes_to_wireframe_str = bpy.context.scene.DocProperties.classes_to_wireframe
|
||||
classes_to_wireframe_str = tool.Drawing.get_document_props().classes_to_wireframe
|
||||
self.classes_to_wireframe_list = [word.strip() for word in classes_to_wireframe_str.split(",")]
|
||||
|
||||
def profile_code(self, message: str) -> None:
|
||||
@@ -434,7 +438,7 @@ class IfcImporter:
|
||||
return False
|
||||
|
||||
def calculate_model_offset(self) -> None:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if self.ifc_import_settings.false_origin_mode == "MANUAL":
|
||||
tool.Loader.set_manual_blender_offset(self.file)
|
||||
elif self.ifc_import_settings.false_origin_mode == "AUTOMATIC":
|
||||
@@ -878,17 +882,19 @@ class IfcImporter:
|
||||
|
||||
def load_file(self):
|
||||
self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file)
|
||||
if not bpy.context.scene.BIMProperties.ifc_file:
|
||||
bpy.context.scene.BIMProperties.ifc_file = self.ifc_import_settings.input_file
|
||||
self.file = IfcStore.get_file()
|
||||
props = tool.Blender.get_bim_props()
|
||||
if not props.ifc_file:
|
||||
props.ifc_file = self.ifc_import_settings.input_file
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
def calculate_unit_scale(self):
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
tool.Loader.set_unit_scale(self.unit_scale)
|
||||
|
||||
def set_units(self):
|
||||
def set_units(self) -> None:
|
||||
if not (assignment := self.file.by_type("IfcProject")[0].UnitsInContext):
|
||||
return # Geometry is optional in IFC
|
||||
props = tool.Blender.get_bim_props()
|
||||
for unit in assignment.Units:
|
||||
if unit.is_a("IfcNamedUnit") and unit.UnitType == "LENGTHUNIT":
|
||||
if unit.is_a("IfcSIUnit"):
|
||||
@@ -908,19 +914,19 @@ class IfcImporter:
|
||||
elif unit.is_a("IfcNamedUnit") and unit.UnitType == "AREAUNIT":
|
||||
name = unit.Name if unit.is_a("IfcSIUnit") else unit.Name.lower()
|
||||
try:
|
||||
bpy.context.scene.BIMProperties.area_unit = "{}{}".format(
|
||||
props.area_unit = "{}{}".format(
|
||||
unit.Prefix + "/" if hasattr(unit, "Prefix") and unit.Prefix else "", name
|
||||
)
|
||||
except: # Probably an invalid unit.
|
||||
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||
props.area_unit = "SQUARE_METRE"
|
||||
elif unit.is_a("IfcNamedUnit") and unit.UnitType == "VOLUMEUNIT":
|
||||
name = unit.Name if unit.is_a("IfcSIUnit") else unit.Name.lower()
|
||||
try:
|
||||
bpy.context.scene.BIMProperties.volume_unit = "{}{}".format(
|
||||
props.volume_unit = "{}{}".format(
|
||||
unit.Prefix + "/" if hasattr(unit, "Prefix") and unit.Prefix else "", name
|
||||
)
|
||||
except: # Probably an invalid unit.
|
||||
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||
props.volume_unit = "CUBIC_METRE"
|
||||
|
||||
def create_project(self):
|
||||
project = self.file.by_type("IfcProject")[0]
|
||||
@@ -964,10 +970,10 @@ class IfcImporter:
|
||||
tool.Collector.assign(obj, should_clean_users_collection=False)
|
||||
|
||||
def is_curve_annotation(self, element: ifcopenshell.entity_instance) -> bool:
|
||||
object_type = element.ObjectType
|
||||
object_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
return (
|
||||
object_type in tool.Drawing.ANNOTATION_TYPES_DATA
|
||||
and tool.Drawing.ANNOTATION_TYPES_DATA[object_type][3] == "curve"
|
||||
and tool.Drawing.ANNOTATION_TYPES_DATA[object_type].data_type == "curve"
|
||||
)
|
||||
|
||||
def get_drawing_group(self, element):
|
||||
@@ -1056,12 +1062,13 @@ class IfcImporter:
|
||||
else:
|
||||
mesh["has_cartesian_point_offset"] = False
|
||||
|
||||
return tool.Loader.convert_geometry_to_mesh(
|
||||
mesh = tool.Loader.convert_geometry_to_mesh(
|
||||
geometry,
|
||||
mesh,
|
||||
verts=verts,
|
||||
load_indexed_maps=self.ifc_import_settings.load_indexed_maps,
|
||||
)
|
||||
return tool.Loader.slice_layerset_mesh(element, mesh)
|
||||
except:
|
||||
self.ifc_import_settings.logger.error("Could not create mesh for %s", element)
|
||||
import traceback
|
||||
@@ -1069,9 +1076,10 @@ class IfcImporter:
|
||||
print(traceback.format_exc())
|
||||
|
||||
def set_default_context(self):
|
||||
rprops = tool.Root.get_root_props()
|
||||
for subcontext in self.file.by_type("IfcGeometricRepresentationSubContext"):
|
||||
if subcontext.ContextIdentifier == "Body":
|
||||
bpy.context.scene.BIMRootProperties.contexts = str(subcontext.id())
|
||||
rprops.contexts = str(subcontext.id())
|
||||
break
|
||||
|
||||
def link_element(self, element: ifcopenshell.entity_instance, obj: IFC_CONNECTED_TYPE) -> None:
|
||||
|
||||
@@ -23,7 +23,6 @@ import ifcopenshell.util.element
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.aggregate as core
|
||||
import bonsai.core.spatial
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_OT_aggregate_assign_object(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -39,7 +39,7 @@ class BIM_PT_aggregate(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
return False
|
||||
@@ -120,9 +120,9 @@ class BIM_PT_linked_aggregate(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
if not tool.Ifc.get().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.attribute as core
|
||||
import bonsai.core.spatial
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
def get_objs_for_operation(operator_properties, context):
|
||||
@@ -117,7 +116,7 @@ class EditAttributes(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
obj = tool.Blender.get_active_object(is_selected=False)
|
||||
if not (element := tool.Ifc.get_entity(obj)):
|
||||
return
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bonsai.bim.helper
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.attribute.data import AttributesData
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ class AuginCreateNewModel(bpy.types.Operator):
|
||||
context.scene.collection.objects.link(cam_obj)
|
||||
context.scene.camera = cam_obj
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
tmpdir = tempfile.mkdtemp(dir=tool.Blender.get_addon_preferences().tmp_dir or None)
|
||||
thumb_path = os.path.join(tmpdir, "thumb.png")
|
||||
context.scene.render.image_settings.file_format = "PNG"
|
||||
context.scene.render.filepath = thumb_path
|
||||
@@ -122,15 +122,16 @@ class AuginCreateNewModel(bpy.types.Operator):
|
||||
context.scene.render.image_settings.file_format = old_file_format
|
||||
context.scene.render.filepath = old_filepath
|
||||
|
||||
client.upload_file(context.scene.BIMProperties.ifc_file, result["s3_bucket"], result["model_path"])
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
client.upload_file(bim_props.ifc_file, result["s3_bucket"], result["model_path"])
|
||||
client.upload_file(thumb_path, result["s3_bucket"], result["thumb_path"])
|
||||
|
||||
# Notify done
|
||||
url = "https://server.auge.pro.br/API/v3/augin_rest.php/files_uploaded"
|
||||
payload = {
|
||||
"user_token": props.token,
|
||||
"ifc_filesize": os.path.getsize(context.scene.BIMProperties.ifc_file),
|
||||
"model_filesize": os.path.getsize(context.scene.BIMProperties.ifc_file),
|
||||
"ifc_filesize": os.path.getsize(bim_props.ifc_file),
|
||||
"model_filesize": os.path.getsize(bim_props.ifc_file),
|
||||
"thumb_filesize": os.path.getsize(thumb_path),
|
||||
"model_upload_path": result["model_path"],
|
||||
"thumb_upload_path": result["thumb_path"],
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy.types
|
||||
import bonsai.tool as tool
|
||||
|
||||
|
||||
class BIM_PT_augin(bpy.types.Panel):
|
||||
@@ -47,7 +48,8 @@ class BIM_PT_augin(bpy.types.Panel):
|
||||
row = layout.row()
|
||||
row.label(text="Logged in as " + props.username)
|
||||
|
||||
if not context.scene.BIMProperties.ifc_file:
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if not bim_props.ifc_file:
|
||||
row = layout.row()
|
||||
row.label(text="No IFC Found")
|
||||
return
|
||||
|
||||
@@ -43,7 +43,6 @@ import bonsai.tool as tool
|
||||
import bonsai.bim.module.bcf.prop as bcf_prop
|
||||
import bonsai.bim.module.bcf.bcfstore as bcfstore
|
||||
from pathlib import Path
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from math import radians, degrees, atan, tan, cos, sin
|
||||
from mathutils import Vector, Matrix, Euler, geometry
|
||||
from xsdata.models.datatype import XmlDateTime
|
||||
@@ -1206,7 +1205,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||
assert bcfxml
|
||||
|
||||
@@ -1316,7 +1315,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
[0, 0, 0, 1],
|
||||
)
|
||||
)
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
matrix = ifcopenshell.util.geolocation.global2local(
|
||||
@@ -1355,7 +1354,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
|
||||
objs: list[bpy.types.Object] = []
|
||||
for global_id in exception_global_ids:
|
||||
obj = IfcStore.get_element(global_id)
|
||||
obj = tool.Ifc.get_object_by_identifier(global_id)
|
||||
if obj and context.view_layer.objects.get(obj.name):
|
||||
assert isinstance(obj, bpy.types.Object)
|
||||
objs.append(obj)
|
||||
@@ -1414,7 +1413,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
return
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
for global_id in selected_global_ids:
|
||||
obj = IfcStore.get_element(global_id)
|
||||
obj = tool.Ifc.get_object_by_identifier(global_id)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
obj.hide_set(False)
|
||||
@@ -1427,7 +1426,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
for acomponent in acoloring.component:
|
||||
global_id_colours.setdefault(acomponent.ifc_guid, acoloring.color)
|
||||
for global_id, color in global_id_colours.items():
|
||||
obj = IfcStore.get_element(global_id)
|
||||
obj = tool.Ifc.get_object_by_identifier(global_id)
|
||||
if obj:
|
||||
obj.color = self.hex_to_rgb(color)
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import mathutils
|
||||
import numpy as np
|
||||
import multiprocessing
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.boundary
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.shape
|
||||
@@ -113,7 +114,7 @@ class Loader:
|
||||
bm.edges.new((verts[-1], verts[0]))
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
mesh.BIMMeshProperties.ifc_definition_id = surface.id()
|
||||
tool.Ifc.link(surface, mesh)
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
matrix = mathutils.Matrix(
|
||||
ifcopenshell.util.placement.get_axis2placement(surface.BasisSurface.Position).tolist()
|
||||
@@ -416,7 +417,7 @@ class UpdateBoundaryGeometry(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
tool.Boundary.move_origin_to_space_origin(context.active_object)
|
||||
settings = tool.Boundary.get_assign_connection_geometry_settings(context.active_object)
|
||||
ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings)
|
||||
ifcopenshell.api.boundary.assign_connection_geometry(tool.Ifc.get(), **settings)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.boundary.data import SpaceBoundariesData
|
||||
|
||||
@@ -34,7 +33,7 @@ class BIM_PT_SceneBoundaries(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
@@ -58,9 +57,9 @@ class BIM_PT_Boundary(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
entity = IfcStore.get_file().by_id(props.ifc_definition_id)
|
||||
entity = tool.Ifc.get().by_id(props.ifc_definition_id)
|
||||
return entity.is_a("IfcRelSpaceBoundary")
|
||||
|
||||
def draw(self, context):
|
||||
@@ -134,7 +133,7 @@ class BIM_PT_SpaceBoundaries(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
for ifc_class in ("IfcSpace", "IfcExternalSpatialElement"):
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_bsdd(Panel):
|
||||
|
||||
@@ -69,10 +69,12 @@ class CadTool(WorkSpaceTool):
|
||||
("bim.cad_hotkey", {"type": "X", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_X")]}),
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, workspace_tool):
|
||||
def draw_settings(
|
||||
context: bpy.types.Context, layout: bpy.types.UILayout, workspace_tool: bpy.types.WorkSpaceTool
|
||||
) -> None:
|
||||
ui_context = str(context.region.type)
|
||||
obj = context.active_object
|
||||
if not obj or not obj.data:
|
||||
if not obj or not (data := obj.data):
|
||||
return
|
||||
is_profile = tool.Geometry.is_profile_object_active()
|
||||
if is_profile:
|
||||
@@ -122,7 +124,10 @@ class CadTool(WorkSpaceTool):
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Reset Vertex", "S_X", bpy.ops.bim.reset_vertex.__doc__, ui_context)
|
||||
|
||||
elif hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "AXIS":
|
||||
elif (
|
||||
isinstance(data, tool.Geometry.TYPES_WITH_MESH_PROPERTIES)
|
||||
and tool.Geometry.get_mesh_props(data).subshape_type == "AXIS"
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout, "Edit Axis", "bim.edit_extrusion_axis", "bim.disable_editing_extrusion_axis", ui_context
|
||||
)
|
||||
@@ -141,7 +146,7 @@ class CadTool(WorkSpaceTool):
|
||||
if (
|
||||
(RailingData.is_loaded or not RailingData.load())
|
||||
and RailingData.data["pset_data"]
|
||||
and context.active_object.BIMRailingProperties.is_editing_path
|
||||
and obj.BIMRailingProperties.is_editing_path
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout,
|
||||
@@ -154,7 +159,7 @@ class CadTool(WorkSpaceTool):
|
||||
elif (
|
||||
(RoofData.is_loaded or not RoofData.load())
|
||||
and RoofData.data["pset_data"]
|
||||
and context.active_object.BIMRoofProperties.is_editing_path
|
||||
and obj.BIMRoofProperties.is_editing_path
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout, "Edit Roof Path", "bim.finish_editing_roof_path", "bim.cancel_editing_roof_path", ui_context
|
||||
@@ -252,15 +257,27 @@ class CadHotkey(bpy.types.Operator):
|
||||
bpy.ops.bim.cad_offset(distance=self.props.distance / si_conversion)
|
||||
|
||||
def hotkey_S_Q(self):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if bpy.context.active_object.data.BIMMeshProperties.subshape_type == "PROFILE":
|
||||
obj = bpy.context.active_object
|
||||
|
||||
if not obj:
|
||||
return
|
||||
|
||||
if not tool.Geometry.has_mesh_properties(data := obj.data):
|
||||
return
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
if mprops.subshape_type == "PROFILE":
|
||||
if element.is_a("IfcProfileDef"):
|
||||
bpy.ops.bim.edit_arbitrary_profile()
|
||||
elif element.is_a("IfcRelSpaceBoundary"):
|
||||
bpy.ops.bim.edit_boundary_geometry()
|
||||
else:
|
||||
bpy.ops.bim.edit_extrusion_profile()
|
||||
elif bpy.context.active_object.data.BIMMeshProperties.subshape_type == "AXIS":
|
||||
elif mprops.subshape_type == "AXIS":
|
||||
bpy.ops.bim.edit_extrusion_axis()
|
||||
|
||||
def hotkey_S_R(self):
|
||||
|
||||
@@ -300,7 +300,7 @@ class SelectIfcClashResults(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
# TODO refactor into new clash results system
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
||||
with open(self.filepath) as f:
|
||||
clash_sets = json.load(f)
|
||||
@@ -321,8 +321,9 @@ class SelectIfcClashResults(bpy.types.Operator):
|
||||
|
||||
ifc_file = ""
|
||||
for scene in obj.users_scene:
|
||||
if scene.BIMProperties.ifc_file:
|
||||
ifc_file = scene.BIMProperties.ifc_file
|
||||
bim_props = tool.Blender.get_bim_props(scene)
|
||||
if bim_props.ifc_file:
|
||||
ifc_file = bim_props.ifc_file
|
||||
if scene.library:
|
||||
break
|
||||
|
||||
@@ -478,7 +479,7 @@ class SelectSmartGroup(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
|
||||
return tool.Ifc.get() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
|
||||
|
||||
def execute(self, context):
|
||||
selected_smart_group = context.scene.BIMClashProperties.active_smart_group
|
||||
|
||||
@@ -201,7 +201,8 @@ class EnableEditingClassification(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
def callback(name, prop, data):
|
||||
if name == "ReferenceTokens":
|
||||
new = bpy.context.scene.BIMGeoreferenceProperties.projected_crs.add()
|
||||
geo_props = tool.Georeference.get_georeference_props()
|
||||
new = geo_props.projected_crs.add()
|
||||
new.name = name
|
||||
new.data_type = "string"
|
||||
new.is_null = data[name] is None
|
||||
@@ -239,7 +240,7 @@ class RemoveClassification(bpy.types.Operator, tool.Ifc.Operator):
|
||||
classification: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"classification.remove_classification",
|
||||
tool.Ifc.get(),
|
||||
|
||||
@@ -21,7 +21,6 @@ import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.module.classification.prop as classification_prop
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.classification.data import (
|
||||
ClassificationsData,
|
||||
ClassificationReferencesData,
|
||||
@@ -124,7 +123,7 @@ class ReferenceUI:
|
||||
self.sprops = context.scene.BIMClassificationProperties
|
||||
self.bprops = context.scene.BIMBSDDProperties
|
||||
self.props = context.scene.BIMClassificationReferenceProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
self.draw_add_ui(context)
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import ifcopenshell.api.constraint
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class LoadObjectives(bpy.types.Operator):
|
||||
@@ -84,7 +83,7 @@ class AddObjective(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
result = ifcopenshell.api.run("constraint.add_objective", IfcStore.get_file())
|
||||
result = ifcopenshell.api.run("constraint.add_objective", tool.Ifc.get())
|
||||
bpy.ops.bim.load_objectives()
|
||||
bpy.ops.bim.enable_editing_constraint(constraint=result.id())
|
||||
return {"FINISHED"}
|
||||
@@ -116,7 +115,7 @@ class RemoveConstraint(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMConstraintProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"constraint.remove_constraint", self.file, **{"constraint": self.file.by_id(self.constraint)}
|
||||
)
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.constraint.data import ConstraintsData, ObjectConstraintsData
|
||||
|
||||
@@ -33,7 +33,7 @@ class BIM_PT_constraints(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not ConstraintsData.is_loaded:
|
||||
@@ -81,7 +81,7 @@ class BIM_PT_object_constraints(Panel):
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
return False
|
||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
@@ -93,7 +93,7 @@ class BIM_PT_object_constraints(Panel):
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.sprops = context.scene.BIMConstraintProperties
|
||||
self.props = obj.BIMObjectConstraintProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.classification.data import CostClassificationsData
|
||||
from bonsai.bim.module.cost.data import CostSchedulesData, CostItemRatesData, CostItemQuantitiesData
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
@@ -83,7 +82,7 @@ def update_cost_item_identification(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
if not props.is_cost_update_enabled or self.identification == "XXX":
|
||||
return
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item",
|
||||
self.file,
|
||||
@@ -98,7 +97,7 @@ def update_cost_item_name(self, context):
|
||||
props = context.scene.BIMCostProperties
|
||||
if not props.is_cost_update_enabled or self.name == "Unnamed":
|
||||
return
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"cost.edit_cost_item",
|
||||
self.file,
|
||||
|
||||
@@ -21,7 +21,6 @@ import bonsai.bim.helper
|
||||
import bonsai.bim.module.cost.prop as CostProp
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.cost.data import CostSchedulesData
|
||||
from typing import Any
|
||||
|
||||
@@ -37,7 +36,7 @@ class BIM_PT_cost_schedules(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = IfcStore.get_file()
|
||||
file = tool.Ifc.get()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
@@ -20,8 +20,8 @@ import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import bonsai.tool as tool
|
||||
from math import degrees, atan2
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
from .api import Api
|
||||
|
||||
@@ -92,7 +92,7 @@ class RunAnalysis(bpy.types.Operator):
|
||||
bl_label = "Run Analysis"
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
self.inputs = {
|
||||
"floors": [],
|
||||
"walls": [],
|
||||
|
||||
@@ -27,7 +27,6 @@ import ifcopenshell
|
||||
import ifcopenshell.util.selector
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.module.drawing.scheduler as scheduler
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.handler import refresh_ui_data
|
||||
from typing import TYPE_CHECKING
|
||||
from collections import Counter
|
||||
@@ -212,7 +211,7 @@ class ExportIfcCsv(bpy.types.Operator):
|
||||
props = context.scene.CsvProperties
|
||||
self.filepath = bpy.path.ensure_ext(self.filepath, f".{props.format}")
|
||||
if props.should_load_from_memory:
|
||||
ifc_file = IfcStore.get_file()
|
||||
ifc_file = tool.Ifc.get()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
results = ifcopenshell.util.selector.filter_elements(
|
||||
@@ -314,7 +313,7 @@ class ImportIfcCsv(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
props = context.scene.CsvProperties
|
||||
if props.should_load_from_memory:
|
||||
ifc_file = IfcStore.get_file()
|
||||
ifc_file = tool.Ifc.get()
|
||||
else:
|
||||
ifc_file = ifcopenshell.open(props.csv_ifc_file)
|
||||
ifc_csv = ifccsv.IfcCsv()
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.search.data import SearchData
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class BIM_PT_ifccsv(Panel):
|
||||
scene = context.scene
|
||||
props = scene.CsvProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "should_load_from_memory")
|
||||
row.operator("bim.import_csv_attributes", icon="IMPORT", text="")
|
||||
@@ -49,7 +49,7 @@ class BIM_PT_ifccsv(Panel):
|
||||
row.operator("bim.export_csv_attributes", icon="EXPORT", text="")
|
||||
row.prop(props, "should_show_settings", icon="PREFERENCES", text="")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
if not tool.Ifc.get() or not props.should_load_from_memory:
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "csv_ifc_file")
|
||||
row.operator("bim.select_csv_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
@@ -78,10 +78,10 @@ class PrintIfcFile(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
print(IfcStore.get_file().wrapped_data.to_string())
|
||||
print(tool.Ifc.get().wrapped_data.to_string())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -101,13 +101,14 @@ class ConvertToBlender(bpy.types.Operator):
|
||||
if tool.Geometry.has_mesh_properties(data):
|
||||
if data.library:
|
||||
continue
|
||||
data.BIMMeshProperties.ifc_definition_id = 0
|
||||
tool.Geometry.get_mesh_props(data).ifc_definition_id = 0
|
||||
for material in bpy.data.materials:
|
||||
if material.library:
|
||||
continue
|
||||
tool.Ifc.unlink(obj=material)
|
||||
context.scene.BIMProperties.ifc_file = ""
|
||||
context.scene.BIMDebugProperties.attributes.clear()
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
bim_props.ifc_file = ""
|
||||
tool.Debug.get_debug_props().attributes.clear()
|
||||
IfcStore.purge()
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
@@ -160,7 +161,8 @@ class ProfileImportIFC(bpy.types.Operator):
|
||||
if not tool.Ifc.get():
|
||||
cls.poll_message_set("No IFC file loaded.")
|
||||
return False
|
||||
if not context.scene.BIMProperties.ifc_file:
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if not bim_props.ifc_file:
|
||||
cls.poll_message_set("Current IFC file is not saved.")
|
||||
return False
|
||||
return True
|
||||
@@ -184,10 +186,10 @@ class CreateAllShapes(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
elements = self.file.by_type("IfcElement") + self.file.by_type("IfcSpace")
|
||||
|
||||
total = len(elements)
|
||||
@@ -256,7 +258,7 @@ class CreateShapeFromStepId(bpy.types.Operator):
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
||||
self.file = tool.Ifc.get()
|
||||
element = self.file.by_id(self.step_id or int(context.scene.BIMDebugProperties.step_id))
|
||||
element = self.file.by_id(self.step_id or int(tool.Debug.get_debug_props().step_id))
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("keep-bounding-boxes", True)
|
||||
if self.should_include_curves:
|
||||
@@ -309,7 +311,7 @@ class RewindInspector(bpy.types.Operator):
|
||||
bl_description = "Rewind the Inspector to the previously inspected element"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
total_breadcrumbs = len(props.step_id_breadcrumb)
|
||||
if total_breadcrumbs < 2:
|
||||
return {"FINISHED"}
|
||||
@@ -328,13 +330,13 @@ class InspectFromStepId(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
debug_props = context.scene.BIMDebugProperties
|
||||
self.file = tool.Ifc.get()
|
||||
debug_props = tool.Debug.get_debug_props()
|
||||
debug_props.active_step_id = self.step_id
|
||||
crumb = context.scene.BIMDebugProperties.step_id_breadcrumb.add()
|
||||
crumb = debug_props.step_id_breadcrumb.add()
|
||||
crumb.name = str(self.step_id)
|
||||
element = self.file.by_id(self.step_id)
|
||||
debug_props.attributes.clear()
|
||||
@@ -385,7 +387,7 @@ class InspectFromObject(bpy.types.Operator):
|
||||
if (
|
||||
(data := obj.data)
|
||||
and tool.Geometry.has_mesh_properties(data)
|
||||
and (ifc_id := data.BIMMeshProperties.ifc_definition_id)
|
||||
and (ifc_id := tool.Geometry.get_mesh_props(data).ifc_definition_id)
|
||||
):
|
||||
return ifc_id
|
||||
|
||||
@@ -422,7 +424,7 @@ class PrintObjectPlacement(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
placement = ifcopenshell.util.placement.get_local_placement(IfcStore.get_file().by_id(self.step_id))
|
||||
placement = ifcopenshell.util.placement.get_local_placement(tool.Ifc.get().by_id(self.step_id))
|
||||
if self.create_empty_object:
|
||||
bpy.ops.object.empty_add(type="ARROWS")
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
@@ -438,7 +440,8 @@ class ParseExpress(bpy.types.Operator):
|
||||
bl_label = "Parse Express"
|
||||
|
||||
def execute(self, context):
|
||||
core.parse_express(tool.Debug, context.scene.BIMDebugProperties.express_file)
|
||||
props = tool.Debug.get_debug_props()
|
||||
core.parse_express(tool.Debug, props.express_file)
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -452,8 +455,9 @@ class SelectExpressFile(bpy.types.Operator):
|
||||
filter_glob: bpy.props.StringProperty(default="*.exp", options={"HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
props = tool.Debug.get_debug_props()
|
||||
if os.path.exists(self.filepath) and "exp" in os.path.splitext(self.filepath)[1]:
|
||||
context.scene.BIMDebugProperties.express_file = self.filepath
|
||||
props.express_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -497,7 +501,7 @@ class PrintUnusedElementStats(bpy.types.Operator):
|
||||
ignore_styled_items: bpy.props.BoolProperty(name="Ignore Styled Items", default=True)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
# ignore some classes that could have zero 0 inverse references by their nature
|
||||
ignore_classes = []
|
||||
if self.ignore_contexts:
|
||||
@@ -543,7 +547,7 @@ class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
if props.ifc_class_purge:
|
||||
purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge)
|
||||
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.")
|
||||
@@ -803,7 +807,7 @@ class DebugActiveDrawing(bpy.types.Operator):
|
||||
)
|
||||
|
||||
def execute(self, context: bpy.types.Context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_item = props.drawings[props.active_drawing_index]
|
||||
drawing = tool.Ifc.get().by_id(drawing_item.ifc_definition_id)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -28,6 +29,9 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Literal, get_args
|
||||
|
||||
DisplayType = Literal["BOUNDS", "WIRE", "SOLID", "TEXTURED"]
|
||||
|
||||
|
||||
class BIMDebugProperties(PropertyGroup):
|
||||
@@ -41,14 +45,23 @@ class BIMDebugProperties(PropertyGroup):
|
||||
inverse_references: CollectionProperty(name="Inverse References", type=Attribute)
|
||||
express_file: StringProperty(name="Express File")
|
||||
display_type: EnumProperty(
|
||||
items=[
|
||||
("BOUNDS", "Bounds", ""),
|
||||
("WIRE", "Wire", ""),
|
||||
("SOLID", "Solid", ""),
|
||||
("TEXTURED", "Textured", ""),
|
||||
],
|
||||
items=[(display_type, display_type.capitalize(), "") for display_type in get_args(DisplayType)],
|
||||
name="Display Type",
|
||||
default="BOUNDS",
|
||||
)
|
||||
ifc_class_purge: StringProperty(name="Unused Elements IFC Class", default="")
|
||||
package_name: StringProperty(name="Package Name", default="")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
step_id: int
|
||||
number_of_polygons: int
|
||||
percentile_of_polygons: int
|
||||
active_step_id: int
|
||||
step_id_breadcrumb: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
inverse_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
inverse_references: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
express_file: str
|
||||
display_type: str
|
||||
ifc_class_purge: str
|
||||
package_name: str
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
|
||||
|
||||
@@ -32,10 +33,11 @@ class BIM_PT_debug(Panel):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(context.scene.BIMProperties, "ifc_file", text="")
|
||||
row.prop(bim_props, "ifc_file", text="")
|
||||
row.operator("bim.validate_ifc_file", icon="CHECKMARK", text="")
|
||||
row.operator("bim.select_ifc_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
@@ -87,27 +89,25 @@ class BIM_PT_debug(Panel):
|
||||
row.prop(props, "step_id", text="")
|
||||
|
||||
row = layout.split(factor=0.7, align=True)
|
||||
row.operator("bim.select_high_polygon_meshes").threshold = context.scene.BIMDebugProperties.number_of_polygons
|
||||
row.operator("bim.select_high_polygon_meshes").threshold = props.number_of_polygons
|
||||
row.prop(props, "number_of_polygons", text="")
|
||||
row = layout.split(factor=0.7, align=True)
|
||||
row.operator("bim.select_highest_polygon_meshes").percentile = (
|
||||
context.scene.BIMDebugProperties.percentile_of_polygons
|
||||
)
|
||||
row.operator("bim.select_highest_polygon_meshes").percentile = props.percentile_of_polygons
|
||||
row.prop(props, "percentile_of_polygons", text="")
|
||||
|
||||
row = layout.split(factor=0.5, align=True)
|
||||
row.prop(props, "display_type", text="")
|
||||
row.operator("bim.override_display_type").display = context.scene.BIMDebugProperties.display_type
|
||||
row.operator("bim.override_display_type").display = props.display_type
|
||||
|
||||
layout.operator("bim.purge_unused_representations")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
|
||||
row.prop(props, "ifc_class_purge", text="")
|
||||
row.operator("bim.purge_unused_elements_by_class", text="Purge Orphaned", icon="TRASH")
|
||||
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")
|
||||
|
||||
if context.active_object and context.active_object.data:
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if context.active_object and (data := context.active_object.data):
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
row = layout.row()
|
||||
row.operator("bim.get_representation_ifc_parameters")
|
||||
for index, ifc_parameter in enumerate(mprops.ifc_parameters):
|
||||
@@ -123,7 +123,7 @@ class BIM_PT_debug(Panel):
|
||||
row.operator("bim.rewind_inspector", icon="FRAME_PREV", text="")
|
||||
row.prop(props, "active_step_id", text="")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.inspect_from_step_id").step_id = context.scene.BIMDebugProperties.active_step_id
|
||||
row.operator("bim.inspect_from_step_id").step_id = props.active_step_id
|
||||
row.operator("bim.inspect_from_object")
|
||||
|
||||
if props.attributes:
|
||||
|
||||
@@ -70,8 +70,9 @@ class VisualiseDiff(bpy.types.Operator):
|
||||
|
||||
ifc_file = ""
|
||||
for scene in obj.users_scene:
|
||||
if scene.BIMProperties.ifc_file:
|
||||
ifc_file = scene.BIMProperties.ifc_file
|
||||
bim_props = tool.Blender.get_bim_props(scene)
|
||||
if bim_props.ifc_file:
|
||||
ifc_file = bim_props.ifc_file
|
||||
if scene.library:
|
||||
break
|
||||
|
||||
@@ -257,8 +258,9 @@ class SelectDiffObjects(bpy.types.Operator):
|
||||
|
||||
ifc_file = ""
|
||||
for scene in obj.users_scene:
|
||||
if scene.BIMProperties.ifc_file:
|
||||
ifc_file = scene.BIMProperties.ifc_file
|
||||
bim_props = tool.Blender.get_bim_props(scene)
|
||||
if bim_props.ifc_file:
|
||||
ifc_file = bim_props.ifc_file
|
||||
if scene.library:
|
||||
break
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.diff.data import DiffData
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
|
||||
@@ -52,7 +52,7 @@ class DocumentData:
|
||||
|
||||
@classmethod
|
||||
def parent_document(cls):
|
||||
props = bpy.context.scene.BIMDocumentProperties
|
||||
props = tool.Document.get_document_props()
|
||||
if len(props.breadcrumbs):
|
||||
parent = tool.Ifc.get().by_id(int(props.breadcrumbs[-1].name))
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
|
||||
@@ -24,7 +24,6 @@ import ifcopenshell.util.element
|
||||
import bonsai.bim.handler
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.document as core
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class LoadProjectDocuments(bpy.types.Operator):
|
||||
@@ -116,7 +115,7 @@ class EditDocument(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDocumentProperties
|
||||
props = tool.Document.get_document_props()
|
||||
core.edit_document(tool.Ifc, tool.Document, document=tool.Ifc.get().by_id(props.active_document_id))
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
def update_document_name(self: "Document", context: bpy.types.Context) -> None:
|
||||
@@ -57,6 +58,11 @@ class Document(PropertyGroup):
|
||||
)
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
identification: str
|
||||
is_information: bool
|
||||
ifc_definition_id: int
|
||||
|
||||
|
||||
class BIMDocumentProperties(PropertyGroup):
|
||||
document_attributes: CollectionProperty(name="Document Attributes", type=Attribute)
|
||||
@@ -65,3 +71,11 @@ class BIMDocumentProperties(PropertyGroup):
|
||||
breadcrumbs: CollectionProperty(name="Breadcrumbs", type=StrProperty)
|
||||
active_document_index: IntProperty(name="Active Document Index")
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
document_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
active_document_id: int
|
||||
documents: bpy.types.bpy_prop_collection_idprop[Document]
|
||||
breadcrumbs: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
active_document_index: int
|
||||
is_editing: bool
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.document.data import DocumentData, ObjectDocumentData
|
||||
|
||||
@@ -33,13 +33,13 @@ class BIM_PT_documents(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not DocumentData.is_loaded:
|
||||
DocumentData.load()
|
||||
|
||||
self.props = context.scene.BIMDocumentProperties
|
||||
self.props = tool.Document.get_document_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="{} Documents Found".format(DocumentData.data["total_information"]), icon="FILE")
|
||||
@@ -92,7 +92,7 @@ class BIM_PT_object_documents(Panel):
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
return False
|
||||
return bool(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
|
||||
@@ -102,8 +102,8 @@ class BIM_PT_object_documents(Panel):
|
||||
|
||||
obj = context.active_object
|
||||
self.oprops = obj.BIMObjectProperties
|
||||
self.props = context.scene.BIMDocumentProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.props = tool.Document.get_document_props()
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
self.draw_add_ui()
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import bpy
|
||||
import math
|
||||
@@ -125,7 +126,9 @@ class Annotator:
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def get_annotation_obj(drawing: ifcopenshell.entity_instance, object_type: str, data_type: str) -> bpy.types.Object:
|
||||
def get_annotation_obj(
|
||||
drawing: ifcopenshell.entity_instance, object_type: str, data_type: tool.Drawing.ANNOTATION_DATA_TYPE
|
||||
) -> bpy.types.Object:
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
co1, _, _, _ = Annotator.get_placeholder_coords(camera)
|
||||
matrix_world = tool.Drawing.get_camera_matrix(camera)
|
||||
@@ -147,11 +150,17 @@ class Annotator:
|
||||
collection.objects.link(obj)
|
||||
return obj
|
||||
|
||||
if object_type != "ANGLE":
|
||||
for obj in collection.objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element and element.ObjectType == object_type and obj.type == object_type.upper():
|
||||
return obj
|
||||
# TODO: remove as outdated?
|
||||
# Is reusing the same objects preventing the creation of new annotations.
|
||||
# if object_type != "ANGLE":
|
||||
# for obj in collection.objects:
|
||||
# element = tool.Ifc.get_entity(obj)
|
||||
# if (
|
||||
# element
|
||||
# and ifcopenshell.util.element.get_predefined_type(element) == object_type
|
||||
# and obj.type == data_type.upper()
|
||||
# ):
|
||||
# return obj
|
||||
|
||||
if data_type == "mesh":
|
||||
data = bpy.data.meshes.new(object_type)
|
||||
|
||||
@@ -88,7 +88,8 @@ class SheetsData:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
titleblocks_dir = ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "TitleblocksDir")
|
||||
if not titleblocks_dir:
|
||||
titleblocks_dir = bpy.context.scene.DocProperties.titleblocks_dir
|
||||
props = tool.Drawing.get_document_props()
|
||||
titleblocks_dir = props.titleblocks_dir
|
||||
titleblocks_dir = tool.Ifc.resolve_uri(titleblocks_dir)
|
||||
if os.path.exists(titleblocks_dir):
|
||||
files.extend([str(f.stem) for f in Path(titleblocks_dir).glob("*.svg")])
|
||||
@@ -120,23 +121,25 @@ class DrawingsData:
|
||||
|
||||
@classmethod
|
||||
def location_hint(cls):
|
||||
if bpy.context.scene.DocProperties.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
results = [("0", "Origin", "")]
|
||||
results.extend(
|
||||
[(str(s.id()), s.Name or "Unnamed", "") for s in tool.Ifc.get().by_type("IfcBuildingStorey")]
|
||||
)
|
||||
return results
|
||||
elif bpy.context.scene.DocProperties.target_view in ["MODEL_VIEW"]:
|
||||
elif props.target_view in ["MODEL_VIEW"]:
|
||||
return [(h.upper(), h, "") for h in ["Orthographic", "Perspective"]]
|
||||
return [(h.upper(), h, "") for h in ["North", "South", "East", "West"]]
|
||||
|
||||
@classmethod
|
||||
def active_drawing_pset_data(cls):
|
||||
ifc_file = tool.Ifc.get()
|
||||
drawing_id = bpy.context.scene.DocProperties.active_drawing_id
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_id = props.active_drawing_id
|
||||
if drawing_id == 0:
|
||||
return {}
|
||||
drawing = ifc_file.by_id(bpy.context.scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(drawing_id)
|
||||
return ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing")
|
||||
|
||||
|
||||
@@ -361,7 +364,11 @@ class DecoratorData:
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
supported_object_types = ("DIMENSION", "DIAMETER", "SECTION_LEVEL", "PLAN_LEVEL", "RADIUS")
|
||||
if not element or not element.is_a("IfcAnnotation") or element.ObjectType not in supported_object_types:
|
||||
if (
|
||||
not element
|
||||
or not element.is_a("IfcAnnotation")
|
||||
or ifcopenshell.util.element.get_predefined_type(element) not in supported_object_types
|
||||
):
|
||||
return None
|
||||
|
||||
dimension_style = "arrow"
|
||||
|
||||
@@ -423,7 +423,8 @@ class BaseDecorator:
|
||||
# font_size = 16 <-- this is a good default
|
||||
# TODO: need to synchronize it better with svg
|
||||
|
||||
magic_font_scale = bpy.context.scene.DocProperties.magic_font_scale
|
||||
props = tool.Drawing.get_document_props()
|
||||
magic_font_scale = props.magic_font_scale
|
||||
font_size_px = int(magic_font_scale * mm_to_px) * font_size_mm / 2.5
|
||||
pos = pos - line_no * font_size_px * rotation_matrix[1]
|
||||
|
||||
@@ -976,6 +977,7 @@ class FallDecorator(BaseDecorator):
|
||||
# same function as in svgwriter.py
|
||||
def get_label_text():
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
B, A = [v.co.xyz for v in spline_points[:2]]
|
||||
rise = abs(A.z - B.z)
|
||||
O = A.copy()
|
||||
@@ -988,13 +990,14 @@ class FallDecorator(BaseDecorator):
|
||||
angle = 90
|
||||
|
||||
# ues SLOPE_ANGLE as default
|
||||
if element.ObjectType in ("FALL", "SLOPE_ANGLE"):
|
||||
object_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if object_type in ("FALL", "SLOPE_ANGLE"):
|
||||
return f"{angle}°"
|
||||
elif element.ObjectType == "SLOPE_FRACTION":
|
||||
elif object_type == "SLOPE_FRACTION":
|
||||
if angle == 90:
|
||||
return "-"
|
||||
return f"{self.format_value(context, rise)} / {self.format_value(context, run)}"
|
||||
elif element.ObjectType == "SLOPE_PERCENT":
|
||||
elif object_type == "SLOPE_PERCENT":
|
||||
if angle == 90:
|
||||
return "-"
|
||||
return f"{round(angle_tg * 100)} %"
|
||||
@@ -2022,7 +2025,8 @@ class DecorationsHandler:
|
||||
for object_type in ("SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
self.decorators[object_type] = self.decorators["FALL"]
|
||||
self.decorators["MULTI_SYMBOL"] = self.decorators["SYMBOL"]
|
||||
if drawing_font := bpy.context.scene.DocProperties.drawing_font:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if drawing_font := props.drawing_font:
|
||||
drawing_font_path = tool.Blender.get_data_dir_path(Path("fonts") / drawing_font)
|
||||
if drawing_font_path.is_file():
|
||||
font_id = blf.load(drawing_font_path.__str__())
|
||||
@@ -2045,7 +2049,7 @@ class DecorationsHandler:
|
||||
if not element.is_a("IfcAnnotation"):
|
||||
continue
|
||||
|
||||
object_type: Union[str, None] = element.ObjectType
|
||||
object_type: Union[str, None] = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if object_type == "DRAWING":
|
||||
continue
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import blf
|
||||
import gpu
|
||||
import bonsai.tool as tool
|
||||
from bpy import types
|
||||
from mathutils import Vector
|
||||
from mathutils import geometry
|
||||
@@ -478,21 +479,25 @@ class ExtrusionWidget(types.GizmoGroup):
|
||||
bl_options = {"3D", "PERSISTENT", "SHOW_MODAL_ALL"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, ctx):
|
||||
obj = ctx.object
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
return (
|
||||
obj
|
||||
and obj.type == "MESH"
|
||||
and obj.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth") is not None
|
||||
and (data := obj.data)
|
||||
and isinstance(data, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(data).ifc_parameters.get("IfcExtrudedAreaSolid/Depth") is not None
|
||||
)
|
||||
|
||||
def setup(self, ctx):
|
||||
target = ctx.object
|
||||
prop = target.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
def setup(self, context: bpy.types.Context) -> None:
|
||||
target = context.object
|
||||
assert target
|
||||
mesh = target.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
prop = tool.Geometry.get_mesh_props(mesh).ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
|
||||
basis = target.matrix_world.normalized()
|
||||
theme = ctx.preferences.themes[0].user_interface
|
||||
scale_value = self.get_scale_value(ctx.scene.unit_settings.system, ctx.scene.unit_settings.length_unit)
|
||||
theme = context.preferences.themes[0].user_interface
|
||||
scale_value = self.get_scale_value(context.scene.unit_settings.system, context.scene.unit_settings.length_unit)
|
||||
|
||||
# setup handle
|
||||
gz = self.handle = self.gizmos.new("BIM_GT_uglydot_3d")
|
||||
@@ -521,23 +526,26 @@ class ExtrusionWidget(types.GizmoGroup):
|
||||
# gz.use_draw_modal = True
|
||||
# gz.target_set_prop('value', target.demo, 'depth')
|
||||
|
||||
def refresh(self, ctx):
|
||||
def refresh(self, context: bpy.types.Context) -> None:
|
||||
"""updating gizmos"""
|
||||
target = ctx.object
|
||||
target = context.active_object
|
||||
basis = target.matrix_world.normalized()
|
||||
self.handle.matrix_basis = basis
|
||||
self.guides.matrix_basis = basis
|
||||
|
||||
def update(self, ctx):
|
||||
def update(self, context: bpy.types.Context) -> None:
|
||||
"""updating object"""
|
||||
bpy.ops.bim.update_parametric_representation()
|
||||
target = ctx.object
|
||||
prop = target.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
target = context.active_object
|
||||
assert target
|
||||
mesh = target.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
prop = tool.Geometry.get_mesh_props(mesh).ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
self.handle.target_set_prop("offset", prop, "value")
|
||||
self.guides.target_set_prop("depth", prop, "value")
|
||||
|
||||
@staticmethod
|
||||
def get_scale_value(system, length_unit):
|
||||
def get_scale_value(system: str, length_unit: str) -> float:
|
||||
scale_value = 1
|
||||
if system == "METRIC":
|
||||
if length_unit == "KILOMETERS":
|
||||
|
||||
@@ -24,7 +24,8 @@ from bpy.app.handlers import persistent
|
||||
|
||||
@persistent
|
||||
def load_post(*args):
|
||||
if bpy.context.scene.DocProperties.should_draw_decorations:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.should_draw_decorations:
|
||||
decoration.DecorationsHandler.install(bpy.context)
|
||||
else:
|
||||
decoration.DecorationsHandler.uninstall()
|
||||
@@ -35,9 +36,11 @@ def depsgraph_update_pre_handler(scene):
|
||||
set_active_camera_resolution(scene)
|
||||
|
||||
|
||||
def set_active_camera_resolution(scene):
|
||||
if not scene.camera or "/" not in scene.camera.name or not scene.DocProperties.drawings:
|
||||
def set_active_camera_resolution(scene: bpy.types.Scene) -> None:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not scene.camera or "/" not in scene.camera.name or not props.drawings:
|
||||
return
|
||||
assert isinstance(scene.camera.data, bpy.types.Camera)
|
||||
props = scene.camera.data.BIMCameraProperties
|
||||
ortho_scale = max((props.width, props.height))
|
||||
aspect_ratio = props.width / props.height
|
||||
@@ -60,5 +63,3 @@ def set_active_camera_resolution(scene):
|
||||
|
||||
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x = int(raster_x)
|
||||
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y = int(raster_y)
|
||||
|
||||
current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index]
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
import bpy
|
||||
import math
|
||||
import mathutils.geometry
|
||||
import ifcopenshell
|
||||
import bonsai.tool as tool
|
||||
from mathutils import Vector
|
||||
from typing import Union
|
||||
|
||||
# Code taken and updated from https://blenderartists.org/t/detecting-intersection-of-bounding-boxes/457520/2
|
||||
|
||||
@@ -135,6 +137,9 @@ def format_distance(
|
||||
scaleFactor = bpy.context.scene.unit_settings.scale_length
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
unit_length = bpy.context.scene.unit_settings.length_unit
|
||||
area_unit_symbol = " " + ifcopenshell.util.unit.get_unit_symbol(
|
||||
ifcopenshell.util.unit.get_project_unit(tool.Ifc.get(), "AREAUNIT")
|
||||
)
|
||||
|
||||
value *= scaleFactor
|
||||
|
||||
@@ -225,7 +230,24 @@ def format_distance(
|
||||
if add_inches or frac:
|
||||
tx_dist += '"'
|
||||
else:
|
||||
tx_dist = str("%1.3f" % (value * toInches / inPerFoot)) + " sq. ft."
|
||||
fmt = "%1.3f"
|
||||
sq_feet = round(value * toInches / inPerFoot, 4)
|
||||
tx_dist = ""
|
||||
if area_unit_symbol == " ft2":
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % sq_feet
|
||||
if area_unit_symbol == " in2":
|
||||
sq_inch = sq_feet * 144
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % sq_inch
|
||||
if area_unit_symbol == " yd2":
|
||||
sq_yard = sq_feet / 9
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % sq_yard
|
||||
if area_unit_symbol == " mi2":
|
||||
sq_mile = sq_feet / 27878400
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % sq_mile
|
||||
|
||||
# METRIC FORMATTING
|
||||
elif unit_system == "METRIC":
|
||||
@@ -287,16 +309,38 @@ def format_distance(
|
||||
d_mm = value * (1000)
|
||||
tx_dist = fmt % d_mm
|
||||
if isArea:
|
||||
tx_dist += s_code
|
||||
if area_unit_symbol == " m2":
|
||||
if decimal_places is None:
|
||||
fmt = "%1.3f"
|
||||
if hide_units is False:
|
||||
fmt += area_unit_symbol
|
||||
tx_dist = fmt % value
|
||||
if area_unit_symbol == " cm2":
|
||||
if decimal_places is None:
|
||||
fmt = "%1.1f"
|
||||
if hide_units is False:
|
||||
fmt += area_unit_symbol
|
||||
d_cm = value * (10000)
|
||||
tx_dist = fmt % d_cm
|
||||
if area_unit_symbol == " mm2":
|
||||
if decimal_places is None:
|
||||
fmt = "%1.0f"
|
||||
if hide_units is False:
|
||||
fmt += area_unit_symbol
|
||||
d_cm = value * (1000000)
|
||||
tx_dist = fmt % d_cm
|
||||
|
||||
else:
|
||||
tx_dist = fmt % value
|
||||
|
||||
return tx_dist
|
||||
|
||||
|
||||
def get_active_drawing(scene):
|
||||
def get_active_drawing(
|
||||
scene: bpy.types.Scene,
|
||||
) -> Union[tuple[bpy.types.Collection, bpy.types.Camera], tuple[None, None]]:
|
||||
"""Get active drawing collection and camera"""
|
||||
props = scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
try:
|
||||
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id))
|
||||
return camera.BIMObjectProperties.collection, camera
|
||||
|
||||
@@ -134,19 +134,19 @@ class AddDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Add a drawing view to the IFC project"
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
hint = self.props.location_hint
|
||||
if self.props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
props = tool.Drawing.get_document_props()
|
||||
hint = props.location_hint
|
||||
if props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
hint = int(hint)
|
||||
core.add_drawing(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Drawing,
|
||||
target_view=self.props.target_view,
|
||||
target_view=props.target_view,
|
||||
location_hint=hint,
|
||||
)
|
||||
try:
|
||||
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||
drawing = tool.Ifc.get().by_id(props.active_drawing_id)
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||
except:
|
||||
pass
|
||||
@@ -162,7 +162,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -176,7 +176,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
row.prop(self, "should_duplicate_annotations")
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
core.duplicate_drawing(
|
||||
tool.Ifc,
|
||||
tool.Drawing,
|
||||
@@ -184,7 +184,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
should_duplicate_annotations=self.should_duplicate_annotations,
|
||||
)
|
||||
try:
|
||||
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||
drawing = tool.Ifc.get().by_id(props.active_drawing_id)
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||
except:
|
||||
pass
|
||||
@@ -244,7 +244,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
active_drawing_id = context.scene.camera.BIMObjectProperties.ifc_definition_id
|
||||
if self.print_all:
|
||||
@@ -261,7 +261,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
self.camera = context.scene.camera
|
||||
self.camera_element = tool.Ifc.get_entity(self.camera)
|
||||
self.camera_document = tool.Drawing.get_drawing_document(self.camera_element)
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
|
||||
with profile("Drawing generation process"):
|
||||
with profile("Initialize drawing generation process"):
|
||||
@@ -385,7 +385,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
obj.hide_render = obj.name not in visible_object_names
|
||||
|
||||
context.scene.render.filepath = str(Path(svg_path).with_suffix(".png"))
|
||||
drawing_style = context.scene.DocProperties.drawing_styles[self.cprops.active_drawing_style_index]
|
||||
drawing_style = self.props.drawing_styles[self.cprops.active_drawing_style_index]
|
||||
|
||||
if drawing_style.render_type == "DEFAULT":
|
||||
bpy.ops.render.render(write_still=True)
|
||||
@@ -712,9 +712,11 @@ class CreateDrawing(bpy.types.Operator):
|
||||
}
|
||||
cached_linework -= edited_guids
|
||||
|
||||
files = {context.scene.BIMProperties.ifc_file: tool.Ifc.get()}
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
files = {bim_props.ifc_file: tool.Ifc.get()}
|
||||
|
||||
for link in context.scene.BIMProjectProperties.links:
|
||||
props = tool.Project.get_project_props()
|
||||
for link in props.links:
|
||||
if link.name not in IfcStore.session_files:
|
||||
IfcStore.session_files[link.name] = ifcopenshell.open(link.name)
|
||||
files[link.name] = IfcStore.session_files[link.name]
|
||||
@@ -729,7 +731,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
# Don't use draw.main() just whilst we're prototyping and experimenting
|
||||
# TODO: hash paths are never used
|
||||
ifc_hash = hashlib.md5(ifc_path.encode("utf-8")).hexdigest()
|
||||
ifc_cache_path = os.path.join(context.scene.BIMProperties.cache_dir, f"{ifc_hash}.h5")
|
||||
ifc_cache_path = os.path.join(bim_props.cache_dir, f"{ifc_hash}.h5")
|
||||
|
||||
self.serialiser.setFile(ifc)
|
||||
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element, ifc_file=ifc)
|
||||
@@ -1141,7 +1143,8 @@ class CreateDrawing(bpy.types.Operator):
|
||||
try:
|
||||
return tool.Ifc.get().by_guid(guid)
|
||||
except:
|
||||
for link in bpy.context.scene.BIMProjectProperties.links:
|
||||
props = tool.Project.get_project_props()
|
||||
for link in props.links:
|
||||
if link.name not in IfcStore.session_files:
|
||||
IfcStore.session_files[link.name] = ifcopenshell.open(link.name)
|
||||
try:
|
||||
@@ -1367,7 +1370,11 @@ class CreateDrawing(bpy.types.Operator):
|
||||
elements = list(elements | filtered_drawing_annotations)
|
||||
|
||||
annotations = sorted(
|
||||
elements, key=lambda a: (tool.Drawing.get_annotation_z_index(a), 1 if a.ObjectType == "TEXT" else 0)
|
||||
elements,
|
||||
key=lambda a: (
|
||||
tool.Drawing.get_annotation_z_index(a),
|
||||
1 if ifcopenshell.util.element.get_predefined_type(a) == "TEXT" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
precision = ifcopenshell.util.element.get_pset(self.camera_element, "EPset_Drawing", "MetricPrecision")
|
||||
@@ -1458,7 +1465,8 @@ class AddSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Add a sheet to the project"
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_sheet(tool.Ifc, tool.Drawing, titleblock=context.scene.DocProperties.titleblock)
|
||||
props = tool.Drawing.get_document_props()
|
||||
core.add_sheet(tool.Ifc, tool.Drawing, titleblock=props.titleblock)
|
||||
|
||||
|
||||
class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -1474,7 +1482,7 @@ class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
cls.poll_message_set("Not implemented yet.")
|
||||
return False
|
||||
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -1483,7 +1491,7 @@ class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
pass
|
||||
"""
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
core.duplicate_sheet(
|
||||
tool.Ifc,
|
||||
tool.Drawing,
|
||||
@@ -1508,7 +1516,7 @@ class OpenLayout(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.update_sheet_drawing_sizes(sheet)
|
||||
@@ -1530,7 +1538,8 @@ class SelectAllSheets(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
for sheet in context.scene.DocProperties.sheets:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in props.sheets:
|
||||
if sheet.is_selected != self.select_all:
|
||||
sheet.is_selected = self.select_all
|
||||
return {"FINISHED"}
|
||||
@@ -1550,7 +1559,7 @@ class OpenSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(is_sheet=True):
|
||||
cls.poll_message_set("No sheet selected.")
|
||||
return False
|
||||
@@ -1564,7 +1573,7 @@ class OpenSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
svg2pdf_command = tool.Blender.get_addon_preferences().svg2pdf_command
|
||||
|
||||
if self.open_all:
|
||||
@@ -1612,9 +1621,10 @@ class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
# Won't be visible in UI anyway.
|
||||
if not props.sheets or not context.scene.BIMProperties.data_dir:
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if not props.sheets or not bim_props.data_dir:
|
||||
return False
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
@@ -1622,8 +1632,8 @@ class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
active_drawing = tool.Drawing.get_active_drawing_item()
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_drawing = props.drawings[props.active_drawing_index]
|
||||
assert active_drawing
|
||||
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
@@ -1680,7 +1690,7 @@ class RemoveDrawingFromSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_item = tool.Drawing.get_active_sheet_item()
|
||||
if active_item is None:
|
||||
return False
|
||||
@@ -1714,11 +1724,12 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(is_sheet=True):
|
||||
cls.poll_message_set("No sheet selected.")
|
||||
return False
|
||||
return props.sheets and context.scene.BIMProperties.data_dir
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
return props.sheets and bim_props.data_dir
|
||||
|
||||
def invoke(self, context, event):
|
||||
# opening all sheets on shift+click
|
||||
@@ -1731,7 +1742,7 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
scene = context.scene
|
||||
props = scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
svg2pdf_command = tool.Blender.get_addon_preferences().svg2pdf_command
|
||||
svg2dxf_command = tool.Blender.get_addon_preferences().svg2dxf_command
|
||||
|
||||
@@ -1838,7 +1849,8 @@ class SelectAllDrawings(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
for drawing in context.scene.DocProperties.drawings:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in props.drawings:
|
||||
if drawing.is_selected != self.select_all:
|
||||
drawing.is_selected = self.select_all
|
||||
return {"FINISHED"}
|
||||
@@ -1857,7 +1869,7 @@ class OpenDrawing(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -1871,7 +1883,7 @@ class OpenDrawing(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
if self.open_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in self.props.drawings if d.is_drawing and d.is_selected
|
||||
@@ -1907,7 +1919,7 @@ class ActivateModel(bpy.types.Operator):
|
||||
bl_description = "Activate the model view, hide all annotations"
|
||||
|
||||
def execute(self, context):
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.active_drawing_id = 0
|
||||
|
||||
CutDecorator.uninstall()
|
||||
@@ -1971,11 +1983,12 @@ class ActivateDrawingBase:
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
if bpy.context.scene.DocProperties.is_editing_drawings == False:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.is_editing_drawings == False:
|
||||
bpy.ops.bim.load_drawings()
|
||||
|
||||
drawing = tool.Ifc.get().by_id(self.drawing)
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
|
||||
if self.use_quick_preview:
|
||||
tool.Blender.activate_camera(tool.Drawing.import_temporary_drawing_camera(drawing))
|
||||
@@ -2027,7 +2040,7 @@ class ActivateDrawing(bpy.types.Operator, ActivateDrawingBase):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2050,7 +2063,7 @@ class ActivateDrawingFromSheet(bpy.types.Operator, ActivateDrawingBase):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(reference_type="DRAWING"):
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2066,7 +2079,8 @@ class SelectDocIfcFile(bpy.types.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DocProperties.ifc_files[self.index].name = self.filepath
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.ifc_files[self.index].name = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -2098,7 +2112,7 @@ class RemoveDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2112,12 +2126,10 @@ class RemoveDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.remove_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id)
|
||||
for d in context.scene.DocProperties.drawings
|
||||
if d.is_drawing and d.is_selected
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in props.drawings if d.is_drawing and d.is_selected
|
||||
]
|
||||
else:
|
||||
if not self.drawing:
|
||||
@@ -2187,7 +2199,8 @@ class ReloadDrawingStyles(bpy.types.Operator):
|
||||
with open(json_path, "r") as fi:
|
||||
shading_styles_json = json.load(fi)
|
||||
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
drawing_styles.clear()
|
||||
styles = [style for style in shading_styles_json]
|
||||
for style_name in styles:
|
||||
@@ -2212,7 +2225,8 @@ class AddDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
new = drawing_styles.add()
|
||||
# drawing style is saved to ifc on rename
|
||||
new.name = tool.Blender.ensure_unique_name("New Drawing Style", drawing_styles)
|
||||
@@ -2227,7 +2241,8 @@ class RemoveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DocProperties.drawing_styles.remove(self.index)
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.drawing_styles.remove(self.index)
|
||||
context.scene.camera.data.BIMCameraProperties.active_drawing_style_index = max(self.index - 1, 0)
|
||||
bpy.ops.bim.save_drawing_styles_data()
|
||||
return {"FINISHED"}
|
||||
@@ -2283,7 +2298,8 @@ class SaveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
index = int(self.index)
|
||||
else:
|
||||
index = context.scene.camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style)
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.drawing_styles[index].raster_style = json.dumps(style)
|
||||
|
||||
bpy.ops.bim.save_drawing_styles_data()
|
||||
return {"FINISHED"}
|
||||
@@ -2309,7 +2325,8 @@ class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not DrawingsData.is_loaded:
|
||||
DrawingsData.load()
|
||||
drawing_pset_data = DrawingsData.data["active_drawing_pset_data"]
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
|
||||
rel_path = drawing_pset_data["ShadingStyles"]
|
||||
current_style = drawing_pset_data.get("CurrentShadingStyle", None)
|
||||
@@ -2338,7 +2355,7 @@ class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
new_style_name = None
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
drawing = ifc_file.by_id(context.scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(props.active_drawing_id)
|
||||
pset = tool.Pset.get_element_pset(drawing, "EPset_Drawing")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", ifc_file, pset=pset, properties={"CurrentShadingStyle": new_style_name}
|
||||
@@ -2357,17 +2374,18 @@ class ActivateDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
scene = context.scene
|
||||
ifc_file = tool.Ifc.get()
|
||||
active_drawing_style_index = scene.camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
props = tool.Drawing.get_document_props()
|
||||
|
||||
if active_drawing_style_index >= len(scene.DocProperties.drawing_styles):
|
||||
if active_drawing_style_index >= len(props.drawing_styles):
|
||||
self.report({"ERROR"}, "Could not find active drawing style")
|
||||
return {"CANCELLED"}
|
||||
|
||||
self.drawing_style = scene.DocProperties.drawing_styles[active_drawing_style_index]
|
||||
self.drawing_style = props.drawing_styles[active_drawing_style_index]
|
||||
|
||||
self.set_raster_style(context)
|
||||
self.set_query(context)
|
||||
|
||||
drawing = ifc_file.by_id(scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(props.active_drawing_id)
|
||||
pset = tool.Pset.get_element_pset(drawing, "EPset_Drawing")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", ifc_file, pset=pset, properties={"CurrentShadingStyle": self.drawing_style.name}
|
||||
@@ -2392,7 +2410,8 @@ class ActivateDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def set_query(self, context: bpy.types.Context) -> None:
|
||||
self.include_global_ids = []
|
||||
self.exclude_global_ids = []
|
||||
for ifc_file in context.scene.DocProperties.ifc_files:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for ifc_file in props.ifc_files:
|
||||
try:
|
||||
ifc = ifcopenshell.open(ifc_file.name)
|
||||
except:
|
||||
@@ -2507,14 +2526,15 @@ class AddScheduleToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not props.schedules:
|
||||
cls.poll_message_set("No schedule selected.")
|
||||
return False
|
||||
return props.schedules and props.sheets and context.scene.BIMProperties.data_dir
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
return props.schedules and props.sheets and bim_props.data_dir
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_schedule = props.schedules[props.active_schedule_index]
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
schedule = tool.Ifc.get().by_id(active_schedule.ifc_definition_id)
|
||||
@@ -2573,14 +2593,15 @@ class AddReferenceToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not props.references:
|
||||
cls.poll_message_set("No reference selected.")
|
||||
return False
|
||||
return props.references and props.sheets and context.scene.BIMProperties.data_dir
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
return props.references and props.sheets and bim_props.data_dir
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_reference = props.references[props.active_reference_index]
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
extref = tool.Ifc.get().by_id(active_reference.ifc_definition_id)
|
||||
@@ -2682,7 +2703,8 @@ class AddDrawingStyleAttribute(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add()
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.drawing_styles[props.active_drawing_style_index].attributes.add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2695,7 +2717,8 @@ class RemoveDrawingStyleAttribute(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2979,7 +3002,7 @@ class LoadSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
core.load_sheets(tool.Drawing)
|
||||
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
sheets_not_found = []
|
||||
for sheet_prop in props.sheets:
|
||||
if not sheet_prop.is_sheet:
|
||||
@@ -3009,7 +3032,7 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
document_type: Literal["SHEET", "TITLEBLOCK", "EMBEDDED"]
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
if sheet.is_a("IfcDocumentInformation"):
|
||||
self.document_type = "SHEET"
|
||||
@@ -3023,6 +3046,7 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.document_type == "SHEET":
|
||||
row = self.layout.row()
|
||||
row.prop(self, "identification", text="Identification")
|
||||
@@ -3030,13 +3054,13 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
row.prop(self, "name", text="Name")
|
||||
elif self.document_type == "TITLEBLOCK":
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "titleblock", text="Titleblock")
|
||||
row.prop(props, "titleblock", text="Titleblock")
|
||||
elif self.document_type == "EMBEDDED":
|
||||
row = self.layout.row()
|
||||
row.prop(self, "identification", text="Identification")
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
if self.document_type == "SHEET":
|
||||
core.rename_sheet(tool.Ifc, tool.Drawing, sheet=sheet, identification=self.identification, name=self.name)
|
||||
@@ -3134,7 +3158,7 @@ class ExpandTargetView(bpy.types.Operator):
|
||||
target_view: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in [d for d in props.drawings if d.target_view == self.target_view]:
|
||||
drawing.is_expanded = True
|
||||
core.load_drawings(tool.Drawing)
|
||||
@@ -3150,7 +3174,7 @@ class ContractTargetView(bpy.types.Operator):
|
||||
target_view: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in [d for d in props.drawings if d.target_view == self.target_view]:
|
||||
drawing.is_expanded = False
|
||||
core.load_drawings(tool.Drawing)
|
||||
@@ -3166,7 +3190,7 @@ class ExpandSheet(bpy.types.Operator):
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
|
||||
sheet.is_expanded = True
|
||||
core.load_sheets(tool.Drawing)
|
||||
@@ -3182,7 +3206,7 @@ class ContractSheet(bpy.types.Operator):
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
|
||||
sheet.is_expanded = False
|
||||
core.load_sheets(tool.Drawing)
|
||||
@@ -3373,7 +3397,7 @@ class ConvertSVGToDXF(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -3387,14 +3411,13 @@ class ConvertSVGToDXF(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.convert_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id)
|
||||
for d in context.scene.DocProperties.drawings
|
||||
if d.is_drawing and d.is_selected
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in props.drawings if d.is_drawing and d.is_selected
|
||||
]
|
||||
else:
|
||||
drawings = [tool.Ifc.get().by_id(context.scene.DocProperties.drawings.get(self.view).ifc_definition_id)]
|
||||
drawings = [tool.Ifc.get().by_id(props.drawings.get(self.view).ifc_definition_id)]
|
||||
|
||||
drawing_uris: list[Path] = []
|
||||
drawings_not_found: list[str] = []
|
||||
|
||||
@@ -76,7 +76,7 @@ def update_diagram_scale(self, context):
|
||||
try:
|
||||
element = (
|
||||
tool.Ifc.get()
|
||||
.by_id(self.id_data.BIMMeshProperties.ifc_definition_id)
|
||||
.by_id(tool.Geometry.get_mesh_props(self.id_data).ifc_definition_id)
|
||||
.OfProductRepresentation[0]
|
||||
.ShapeOfProduct[0]
|
||||
)
|
||||
@@ -93,7 +93,7 @@ def update_diagram_scale(self, context):
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=diagram_scale)
|
||||
|
||||
|
||||
def update_is_nts(self, context):
|
||||
def update_is_nts(self: "BIMCameraProperties", context: bpy.types.Context) -> None:
|
||||
if not self.update_props:
|
||||
return
|
||||
if not context.scene.camera or context.scene.camera.data != self.id_data:
|
||||
@@ -104,7 +104,7 @@ def update_is_nts(self, context):
|
||||
try:
|
||||
element = (
|
||||
tool.Ifc.get()
|
||||
.by_id(self.id_data.BIMMeshProperties.ifc_definition_id)
|
||||
.by_id(tool.Geometry.get_mesh_props(self.id_data).ifc_definition_id)
|
||||
.OfProductRepresentation[0]
|
||||
.ShapeOfProduct[0]
|
||||
)
|
||||
@@ -192,8 +192,8 @@ def get_drawing_style_name(self: "DrawingStyle"):
|
||||
|
||||
def set_drawing_style_name(self: "DrawingStyle", new_value: str) -> None:
|
||||
"""ensure the name is unique"""
|
||||
scene = bpy.context.scene
|
||||
drawing_styles = [s.name for s in scene.DocProperties.drawing_styles if s.name != self.name]
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = [s.name for s in props.drawing_styles if s.name != self.name]
|
||||
new_value = tool.Blender.ensure_unique_name(new_value, drawing_styles)
|
||||
old_value = self.name
|
||||
self["name"] = new_value
|
||||
|
||||
@@ -56,7 +56,7 @@ def a1_to_rc(cell):
|
||||
|
||||
|
||||
class Scheduler:
|
||||
def schedule(self, infile, outfile):
|
||||
def schedule(self, infile: str, outfile: str) -> None:
|
||||
self.svg = svgwrite.Drawing(
|
||||
outfile,
|
||||
debug=False,
|
||||
@@ -71,11 +71,12 @@ class Scheduler:
|
||||
elif infile.endswith("xlsx"):
|
||||
self.schedule_xlsx(infile, outfile)
|
||||
|
||||
def parse_css(self, infile):
|
||||
def parse_css(self, infile: str) -> None:
|
||||
props = tool.Drawing.get_document_props()
|
||||
stylesheet_path = os.path.splitext(infile)[0] + ".css"
|
||||
if not os.path.exists(stylesheet_path):
|
||||
stylesheet_rel_path = getattr(bpy.context.scene.DocProperties, "schedules_stylesheet_path")
|
||||
ifc_file_path = os.path.dirname(IfcStore.path)
|
||||
stylesheet_rel_path = props.schedules_stylesheet_path
|
||||
ifc_file_path = os.path.dirname(tool.Ifc.get_path())
|
||||
stylesheet_path = ifc_file_path + "\\" + stylesheet_rel_path
|
||||
if not os.path.exists(stylesheet_path):
|
||||
stylesheet_path = tool.Blender.get_data_dir_path(Path("assets") / "schedule.css")
|
||||
@@ -91,7 +92,7 @@ class Scheduler:
|
||||
|
||||
self.svg.defs.add(self.svg.style(css))
|
||||
|
||||
def schedule_xlsx(self, infile, outfile):
|
||||
def schedule_xlsx(self, infile: str, outfile: str) -> None:
|
||||
workbook = openpyxl.open(infile, data_only=True)
|
||||
sheet = workbook.active
|
||||
|
||||
@@ -236,7 +237,7 @@ class Scheduler:
|
||||
self.svg["viewBox"] = "0 0 {} {}".format(total_width, total_height)
|
||||
self.svg.save(pretty=True)
|
||||
|
||||
def schedule_ods(self, infile, outfile):
|
||||
def schedule_ods(self, infile: str, outfile: str) -> None:
|
||||
doc = load_ods(infile)
|
||||
|
||||
# useful for debugging ods
|
||||
@@ -495,11 +496,11 @@ class Scheduler:
|
||||
self.svg["viewBox"] = "0 0 {} {}".format(total_width, total_height)
|
||||
self.svg.save(pretty=True)
|
||||
|
||||
def get_style(self, style_name, styles):
|
||||
def get_style(self, style_name: str, styles: dict) -> dict:
|
||||
style = styles[style_name] if style_name else {}
|
||||
return style
|
||||
|
||||
def get_box_alignment(self, style):
|
||||
def get_box_alignment(self, style: dict) -> str:
|
||||
if style and "vertical-align" in style and style["vertical-align"] != "automatic":
|
||||
vertical_align = style["vertical-align"]
|
||||
else:
|
||||
|
||||
@@ -181,35 +181,35 @@ class SvgWriter:
|
||||
self.decimal_places = decimal_places
|
||||
for element in annotations:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not obj or element.ObjectType == "DRAWING":
|
||||
if not obj or (object_type := ifcopenshell.util.element.get_predefined_type(element)) == "DRAWING":
|
||||
continue
|
||||
elif element.ObjectType == "GRID":
|
||||
elif object_type == "GRID":
|
||||
self.draw_grid_annotation(obj)
|
||||
elif element.ObjectType == "TEXT_LEADER":
|
||||
elif object_type == "TEXT_LEADER":
|
||||
self.draw_leader_annotation(obj)
|
||||
elif element.ObjectType == "STAIR_ARROW":
|
||||
elif object_type == "STAIR_ARROW":
|
||||
self.draw_stair_annotation(obj)
|
||||
elif element.ObjectType == "DIMENSION":
|
||||
elif object_type == "DIMENSION":
|
||||
self.draw_dimension_annotations(obj)
|
||||
elif element.ObjectType == "ANGLE":
|
||||
elif object_type == "ANGLE":
|
||||
self.draw_angle_annotations(obj)
|
||||
elif element.ObjectType == "RADIUS":
|
||||
elif object_type == "RADIUS":
|
||||
self.draw_radius_annotations(obj)
|
||||
elif element.ObjectType == "DIAMETER":
|
||||
elif object_type == "DIAMETER":
|
||||
self.draw_diameter_annotations(obj)
|
||||
elif element.ObjectType == "ELEVATION":
|
||||
elif object_type == "ELEVATION":
|
||||
self.draw_elevation_annotation(obj)
|
||||
elif element.ObjectType == "SECTION":
|
||||
elif object_type == "SECTION":
|
||||
self.draw_section_annotation(obj)
|
||||
elif element.ObjectType == "BREAKLINE":
|
||||
elif object_type == "BREAKLINE":
|
||||
self.draw_break_annotations(obj)
|
||||
elif element.ObjectType == "PLAN_LEVEL":
|
||||
elif object_type == "PLAN_LEVEL":
|
||||
self.draw_plan_level_annotation(obj)
|
||||
elif element.ObjectType == "SECTION_LEVEL":
|
||||
elif object_type == "SECTION_LEVEL":
|
||||
self.draw_section_level_annotation(obj)
|
||||
elif element.ObjectType == "TEXT":
|
||||
elif object_type == "TEXT":
|
||||
self.draw_text_annotation(obj, obj.location)
|
||||
elif element.ObjectType in ("FALL", "SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
elif object_type in ("FALL", "SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
self.draw_fall_annotations(obj)
|
||||
else:
|
||||
self.draw_misc_annotation(obj)
|
||||
@@ -1207,13 +1207,14 @@ class SvgWriter:
|
||||
angle = 90
|
||||
|
||||
# ues SLOPE_ANGLE as default
|
||||
if element.ObjectType in ("FALL", "SLOPE_ANGLE"):
|
||||
object_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if object_type in ("FALL", "SLOPE_ANGLE"):
|
||||
return f"{angle}°"
|
||||
elif element.ObjectType == "SLOPE_FRACTION":
|
||||
elif object_type == "SLOPE_FRACTION":
|
||||
if angle == 90:
|
||||
return "-"
|
||||
return f"{helper.format_distance(rise, precision=self.precision, decimal_places=self.decimal_places)} / {helper.format_distance(run, precision=self.precision, decimal_places=self.decimal_places)}"
|
||||
elif element.ObjectType == "SLOPE_PERCENT":
|
||||
elif object_type == "SLOPE_PERCENT":
|
||||
if angle == 90:
|
||||
return "-"
|
||||
return f"{round(angle_tg * 100)} %"
|
||||
|
||||
@@ -49,7 +49,7 @@ class BIM_PT_camera(Panel):
|
||||
return
|
||||
|
||||
self.layout.use_property_split = True
|
||||
dprops = context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
|
||||
col = self.layout.column(align=True)
|
||||
@@ -161,7 +161,7 @@ class BIM_PT_drawing_underlay(Panel):
|
||||
layout.use_property_split = True
|
||||
camera = context.scene.camera
|
||||
assert camera
|
||||
dprops = context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
props = camera.data.BIMCameraProperties
|
||||
drawing_index_is_valid = props.active_drawing_style_index < len(dprops.drawing_styles)
|
||||
|
||||
@@ -229,7 +229,7 @@ class BIM_PT_drawings(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_drawings:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -302,7 +302,7 @@ class BIM_PT_schedules(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_schedules:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -352,7 +352,7 @@ class BIM_PT_references(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_references:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -394,7 +394,7 @@ class BIM_PT_sheets(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_sheets:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -601,7 +601,7 @@ class BIM_UL_drawinglist(bpy.types.UIList):
|
||||
selected_icon = "CHECKBOX_HLT" if item.is_selected else "CHECKBOX_DEHLT"
|
||||
row.prop(item, "is_selected", text="", icon=selected_icon, emboss=False)
|
||||
row.prop(item, "name", text="", emboss=False)
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
if (
|
||||
self.props.drawings
|
||||
and self.props.active_drawing_id
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import os
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
import bonsai.core.type
|
||||
import bonsai.core.drawing as core
|
||||
import bonsai.tool as tool
|
||||
@@ -197,6 +198,8 @@ def create_annotation_occurrence(context):
|
||||
|
||||
|
||||
class AnnotationToolUI:
|
||||
layout: bpy.types.UILayout
|
||||
|
||||
@classmethod
|
||||
def draw(cls, context, layout):
|
||||
cls.layout = layout
|
||||
@@ -224,7 +227,8 @@ class AnnotationToolUI:
|
||||
@classmethod
|
||||
def draw_create_object_interface(cls):
|
||||
row = cls.layout.row(align=True)
|
||||
row.prop(bpy.context.scene.DocProperties, "should_draw_decorations", text="Viewport Annotations")
|
||||
props = tool.Drawing.get_document_props()
|
||||
row.prop(props, "should_draw_decorations", text="Viewport Annotations")
|
||||
|
||||
@classmethod
|
||||
def draw_edit_object_interface(cls, context):
|
||||
@@ -330,7 +334,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not element or not element.is_a("IfcAnnotation"):
|
||||
continue
|
||||
|
||||
annotation_type = element.ObjectType
|
||||
annotation_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if annotation_type not in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP:
|
||||
self.report({"ERROR"}, f"Annotation type {annotation_type} is not supported for readjustment.")
|
||||
continue
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.fm.data import FMData
|
||||
|
||||
|
||||
@@ -40,11 +39,11 @@ class BIM_PT_fm(Panel):
|
||||
scene = context.scene
|
||||
props = scene.BIMFMProperties
|
||||
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
row = layout.row()
|
||||
row.prop(props, "should_load_from_memory")
|
||||
|
||||
if not IfcStore.get_file() or not props.should_load_from_memory:
|
||||
if not tool.Ifc.get() or not props.should_load_from_memory:
|
||||
row = layout.row()
|
||||
props.ifc_files.layout_file_select(row, "*.ifc;*.ifczip;*.ifcxml", "IFC File(s)")
|
||||
|
||||
|
||||
@@ -98,12 +98,14 @@ addon_keymaps = []
|
||||
|
||||
|
||||
@persistent
|
||||
def block_scale(scene):
|
||||
def block_scale(scene: bpy.types.Scene) -> None:
|
||||
import bonsai.tool as tool
|
||||
|
||||
if obj := (getattr(bpy.context, "active_object", None) or bpy.context.view_layer.objects.active):
|
||||
if isinstance(obj, bpy.types.Object) and obj.BIMObjectProperties.ifc_definition_id:
|
||||
if obj.scale != (1, 1, 1):
|
||||
obj.scale = (1, 1, 1)
|
||||
elif isinstance(obj, bpy.types.Mesh) and obj.BIMMeshProperties.ifc_definition_id:
|
||||
elif isinstance(obj, bpy.types.Mesh) and tool.Geometry.get_mesh_props(obj).ifc_definition_id:
|
||||
if obj.scale != (1, 1, 1):
|
||||
obj.scale = (1, 1, 1)
|
||||
|
||||
|
||||
@@ -53,15 +53,16 @@ class ViewportData:
|
||||
obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
modes = [obj_mode]
|
||||
|
||||
if bpy.context.scene.BIMGeometryProperties.representation_obj:
|
||||
modes: list[tuple[str, str, str, str, int]] = [obj_mode]
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
if gprops.representation_obj:
|
||||
modes.append(item_mode)
|
||||
|
||||
if not obj:
|
||||
return modes
|
||||
|
||||
if obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
|
||||
pprops = tool.Project.get_project_props()
|
||||
if obj in pprops.clipping_planes_objs:
|
||||
pass
|
||||
elif element:
|
||||
if tool.Geometry.is_locked(element):
|
||||
@@ -107,8 +108,9 @@ class RepresentationsData:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
active_representation_id = None
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties"):
|
||||
active_representation_id = obj.data.BIMMeshProperties.ifc_definition_id
|
||||
active_representation = tool.Geometry.get_active_representation(obj)
|
||||
if active_representation:
|
||||
active_representation_id = active_representation.id()
|
||||
|
||||
for representation in tool.Geometry.get_representations_iter(element):
|
||||
representation_type = representation.RepresentationType
|
||||
@@ -158,9 +160,9 @@ class RepresentationsData:
|
||||
if not obj.data:
|
||||
return []
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not (active_representation_id := obj.data.BIMMeshProperties.ifc_definition_id):
|
||||
base_representation = tool.Geometry.get_active_representation(obj)
|
||||
if not base_representation:
|
||||
return [] # Maybe in profile editing mode
|
||||
base_representation = tool.Ifc.get().by_id(active_representation_id)
|
||||
|
||||
# shape aspects matching context of the active representation
|
||||
matching_shape_aspects = []
|
||||
@@ -390,7 +392,7 @@ class PlacementData:
|
||||
def load(cls):
|
||||
cls.data = {"has_placement": cls.has_placement()}
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
obj = bpy.context.active_object
|
||||
if obj and props.has_blender_offset:
|
||||
xyz = cls.original_xyz(obj)
|
||||
@@ -413,7 +415,7 @@ class PlacementData:
|
||||
@classmethod
|
||||
def original_xyz(cls, obj):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
xyz = ifcopenshell.util.geolocation.xyz2enh(
|
||||
obj.matrix_world[0][3],
|
||||
obj.matrix_world[1][3],
|
||||
|
||||
@@ -46,12 +46,14 @@ class ItemDecorator:
|
||||
obj_is_boolean: dict[str, list[ifcopenshell.entity_instance]] = {}
|
||||
objs: dict[str, dict[str, list]] = {}
|
||||
obj_matrix: dict[str, Matrix] = {}
|
||||
for item_obj in context.scene.BIMGeometryProperties.item_objs:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item_obj in props.item_objs:
|
||||
if obj := item_obj.obj:
|
||||
obj: bpy.types.Object
|
||||
objs[obj.name] = cls.get_obj_data(obj)
|
||||
obj_is_selected[obj.name] = obj.select_get()
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
obj_is_boolean[obj.name] = [i for i in tool.Ifc.get().get_inverse(item) if i.is_a("IfcBooleanResult")]
|
||||
obj_matrix[obj.name] = obj.matrix_world.copy()
|
||||
|
||||
@@ -143,7 +145,8 @@ class ItemDecorator:
|
||||
color = selected_elements_color
|
||||
blf.color(font_id, *color)
|
||||
|
||||
for item in context.scene.BIMGeometryProperties.item_objs:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item in props.item_objs:
|
||||
if (obj := item.obj) and obj.hide_get() == False:
|
||||
if obj.select_get():
|
||||
centroid = obj.matrix_world @ Vector(obj.bound_box[0]).lerp(Vector(obj.bound_box[6]), 0.5)
|
||||
|
||||
@@ -23,12 +23,14 @@ import numpy as np
|
||||
import numpy.typing as npt
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.layer
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.shape_builder
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.boundary
|
||||
import ifcopenshell.api.grid
|
||||
import bonsai.core.geometry
|
||||
import bonsai.core.geometry as core
|
||||
@@ -77,7 +79,8 @@ class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.separate_element(element)
|
||||
|
||||
def separate_item(self, context, obj):
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
previous_selected_objects = context.selected_objects
|
||||
bpy.ops.mesh.separate(type=self.type)
|
||||
@@ -85,7 +88,7 @@ class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if obj in previous_selected_objects:
|
||||
continue
|
||||
self.add_meshlike_item(obj)
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(tool.Geometry.get_geometry_props().representation_obj)
|
||||
else:
|
||||
self.report({"INFO"}, f"Separating an {item.is_a()} is not supported")
|
||||
|
||||
@@ -120,7 +123,7 @@ class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, obj)
|
||||
props.add_item_object(obj, item)
|
||||
|
||||
def separate_element(self, element):
|
||||
@@ -301,11 +304,12 @@ class AddRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
row = self.layout.row()
|
||||
row.prop(self, "representation_conversion_method", text="")
|
||||
if self.representation_conversion_method == "OBJECT":
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.BIMGeometryProperties, "representation_from_object", text="")
|
||||
row.prop(props, "representation_from_object", text="")
|
||||
|
||||
|
||||
class SelectConnection(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -456,13 +460,17 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def update_obj_mesh_representation(self, context: bpy.types.Context, obj: bpy.types.Object) -> None:
|
||||
data = obj.data
|
||||
assert tool.Geometry.is_data_supported_for_adding_representation(data)
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
|
||||
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
material = ifcopenshell.util.element.get_material(product, should_skip_usage=True)
|
||||
|
||||
# NOTE: Currently iterator doesn't detect whether opening is actually affected the representation
|
||||
# or it's just present on the element. In theory, we can also allow editing representations
|
||||
# if we know that representation wasn't affected by existing openings.
|
||||
has_openings = tool.Geometry.has_openings(product) and obj.data.BIMMeshProperties.has_openings_applied
|
||||
has_openings = tool.Geometry.has_openings(product) and tool.Geometry.get_mesh_props(data).has_openings_applied
|
||||
if has_openings and not self.apply_openings:
|
||||
# Meshlike things with openings can only be updated without openings applied.
|
||||
if self.from_ui:
|
||||
@@ -479,13 +487,14 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
elif product.is_a("IfcRelSpaceBoundary"):
|
||||
# TODO refactor
|
||||
settings = tool.Boundary.get_assign_connection_geometry_settings(obj)
|
||||
ifcopenshell.api.run("boundary.assign_connection_geometry", tool.Ifc.get(), **settings)
|
||||
ifcopenshell.api.boundary.assign_connection_geometry(tool.Ifc.get(), **settings)
|
||||
return
|
||||
|
||||
if tool.Ifc.is_moved(obj) or tool.Geometry.is_scaled(obj):
|
||||
core.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
old_representation = tool.Geometry.get_active_representation(obj)
|
||||
assert old_representation
|
||||
if material and material.is_a() in ["IfcMaterialProfileSet", "IfcMaterialLayerSet"]:
|
||||
if self.ifc_representation_class == "IfcTessellatedFaceSet":
|
||||
# We are explicitly casting to a tessellation, so remove all parametric materials.
|
||||
@@ -544,12 +553,12 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Geometry.run_style_add_style(obj=mat)
|
||||
for mat in tool.Geometry.get_object_materials_without_styles(obj)
|
||||
]
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_representation_styles",
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
ifcopenshell.api.style.assign_representation_styles(
|
||||
self.file,
|
||||
shape_representation=new_representation,
|
||||
styles=tool.Geometry.get_styles(obj, only_assigned_to_faces=True),
|
||||
should_use_presentation_style_assignment=context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
should_use_presentation_style_assignment=props.should_use_presentation_style_assignment,
|
||||
)
|
||||
tool.Geometry.record_object_materials(obj)
|
||||
|
||||
@@ -568,8 +577,8 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
continue
|
||||
representation.RepresentationIdentifier = "Reference"
|
||||
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
|
||||
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
|
||||
tool.Ifc.link(new_representation, data)
|
||||
data.name = tool.Loader.get_mesh_name(new_representation)
|
||||
|
||||
# TODO: In simple scenarios, a type has a ShapeRepresentation of ID
|
||||
# 123. This is then mapped through mapped representations by
|
||||
@@ -585,7 +594,7 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# transformations.
|
||||
|
||||
core.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_representation)
|
||||
if obj.data.BIMMeshProperties.ifc_parameters:
|
||||
if mprops.ifc_parameters:
|
||||
core.get_representation_ifc_parameters(tool.Geometry, obj=obj)
|
||||
|
||||
|
||||
@@ -597,12 +606,13 @@ class UpdateParametricRepresentation(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object and context.active_object.mode == "OBJECT"
|
||||
return (obj := context.active_object) and obj.mode == "OBJECT" and tool.Geometry.has_mesh_properties(obj.data)
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
obj = context.active_object
|
||||
props = obj.data.BIMMeshProperties
|
||||
assert obj and tool.Geometry.has_mesh_properties(obj.data)
|
||||
props = tool.Geometry.get_mesh_props(obj.data)
|
||||
parameter = props.ifc_parameters[self.index]
|
||||
self.file.by_id(parameter.step_id)[parameter.index] = parameter.value
|
||||
show_representation_parameters = bool(props.ifc_parameters)
|
||||
@@ -626,8 +636,10 @@ class GetRepresentationIfcParameters(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.get_representation_ifc_parameters(tool.Geometry, obj=context.active_object)
|
||||
parameters = context.active_object.data.BIMMeshProperties.ifc_parameters
|
||||
obj = context.active_object
|
||||
assert obj and tool.Geometry.has_mesh_properties((data := obj.data))
|
||||
core.get_representation_ifc_parameters(tool.Geometry, obj=obj)
|
||||
parameters = tool.Geometry.get_mesh_props(data).ifc_parameters
|
||||
self.report({"INFO"}, f"{len(parameters)} parameters found.")
|
||||
|
||||
|
||||
@@ -671,7 +683,7 @@ class CopyRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
|
||||
|
||||
def lock_error_message(name):
|
||||
def lock_error_message(name: str) -> str:
|
||||
return f"'{name}' is locked. Unlock it via the Spatial panel in the Project Overview tab."
|
||||
|
||||
|
||||
@@ -719,8 +731,10 @@ class OverrideDelete(bpy.types.Operator):
|
||||
if self.is_batch:
|
||||
row = self.layout.row()
|
||||
row.label(text="Warning: Faster deletion will use more memory.", icon="ERROR")
|
||||
row = self.layout.row()
|
||||
row.label(text="See system console for deletion progress.")
|
||||
|
||||
def _execute(self, context):
|
||||
def _execute(self, context: bpy.types.Context):
|
||||
start_time = time()
|
||||
|
||||
if self.is_batch:
|
||||
@@ -728,10 +742,19 @@ class OverrideDelete(bpy.types.Operator):
|
||||
|
||||
self.process_arrays(context)
|
||||
clear_active_object = True
|
||||
for obj in context.selected_objects:
|
||||
try:
|
||||
obj.name
|
||||
except:
|
||||
objects_to_remove = context.selected_objects
|
||||
for i, obj in enumerate(objects_to_remove, 1):
|
||||
# Log time.
|
||||
time_since_start = time() - start_time
|
||||
is_valid_data_block = tool.Blender.is_valid_data_block(obj)
|
||||
if time_since_start > 10:
|
||||
obj_name = f" ({obj.name})" if is_valid_data_block else ""
|
||||
print(
|
||||
f"Removing object {i}/{len(objects_to_remove)}{obj_name}. "
|
||||
f"Time since start: {time_since_start:.2f} seconds."
|
||||
)
|
||||
|
||||
if not is_valid_data_block:
|
||||
continue
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
@@ -781,7 +804,7 @@ class OverrideDelete(bpy.types.Operator):
|
||||
data["old_file"].redo()
|
||||
tool.Ifc.set(data["new_file"])
|
||||
|
||||
def process_arrays(self, context):
|
||||
def process_arrays(self, context: bpy.types.Context) -> None:
|
||||
selected_objects = set(context.selected_objects)
|
||||
array_parents = set()
|
||||
for obj in context.selected_objects:
|
||||
@@ -824,7 +847,7 @@ class OverrideOutlinerDelete(bpy.types.Operator):
|
||||
# unintended IFC spatial modifications. To make life less confusing for
|
||||
# the user, Delete means Delete. End of story.
|
||||
# Deep magick from the dawn of time
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
# https://blender.stackexchange.com/questions/203729/python-get-selected-objects-in-outliner
|
||||
objects_to_delete = set()
|
||||
@@ -948,7 +971,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
@staticmethod
|
||||
def execute_duplicate_operator(self, context, linked=False):
|
||||
# Deep magick from the dawn of time
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
if self.new_active_obj:
|
||||
context.view_layer.objects.active = self.new_active_obj
|
||||
@@ -1029,7 +1052,8 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
# Unlink from previous boolean element
|
||||
# and keep object tracked for decorations.
|
||||
if is_tracked_opening:
|
||||
new_obj.data.BIMMeshProperties.ifc_boolean_id = 0
|
||||
mprops = tool.Geometry.get_mesh_props(new_obj.data)
|
||||
mprops.ifc_boolean_id = 0
|
||||
tool.Root.add_tracked_opening(new_obj, tracked_opening_type)
|
||||
|
||||
if obj == context.active_object:
|
||||
@@ -1053,7 +1077,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
if new.is_a("IfcRelSpaceBoundary"):
|
||||
surface = new.ConnectionGeometry.SurfaceOnRelatingElement
|
||||
temp_data.name = f"0/{surface.id()}"
|
||||
temp_data.BIMMeshProperties.ifc_definition_id = surface.id()
|
||||
tool.Ifc.link(surface, temp_data)
|
||||
else:
|
||||
tool.Blender.remove_data_block(temp_data)
|
||||
|
||||
@@ -1089,12 +1113,14 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
@staticmethod
|
||||
def duplicate_item(obj: bpy.types.Object) -> None:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
new_item = ifcopenshell.util.element.copy_deep(tool.Ifc.get(), item)
|
||||
new_obj = obj.copy()
|
||||
assert tool.Geometry.has_mesh_properties(obj.data)
|
||||
temp_data = obj.data.copy()
|
||||
new_obj.data = temp_data
|
||||
new_obj.data.BIMMeshProperties.ifc_definition_id = new_item.id()
|
||||
tool.Ifc.link(new_item, temp_data)
|
||||
new_obj.name = obj.data.name = f"Item/{new_item.is_a()}/{new_item.id()}"
|
||||
props.add_item_object(new_obj, new_item)
|
||||
|
||||
@@ -1670,7 +1696,8 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def join_item(self) -> None:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
ifc_file = tool.Ifc.get()
|
||||
item = tool.Ifc.get().by_id(self.target.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(self.target)
|
||||
assert item
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
tool.Geometry.dissolve_triangulated_edges(self.target)
|
||||
item_objs = [i.obj for i in props.item_objs if i.obj]
|
||||
@@ -1693,7 +1720,7 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
for item_data in items_data:
|
||||
props.add_item_object(item_data["obj"], ifc_file.by_id(item_data["ifc_definition_id"]))
|
||||
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
bpy.context.view_layer.update()
|
||||
tool.Root.reload_item_decorator()
|
||||
|
||||
@@ -1701,7 +1728,8 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_file = tool.Ifc.get()
|
||||
builder = ShapeBuilder(ifc_file)
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
representation = ifc_file.by_id(self.target.data.BIMMeshProperties.ifc_definition_id)
|
||||
representation = tool.Geometry.get_active_representation(self.target)
|
||||
assert representation
|
||||
representation_type = representation.RepresentationType
|
||||
if representation_type in ("Tessellation", "Brep"):
|
||||
for obj in bpy.context.selected_objects:
|
||||
@@ -1740,7 +1768,8 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
continue
|
||||
|
||||
# Only objects of the same representation type can be joined
|
||||
obj_rep = ifc_file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
obj_rep = tool.Geometry.get_active_representation(obj)
|
||||
assert obj_rep
|
||||
if obj_rep.RepresentationType != representation_type:
|
||||
obj.select_set(False)
|
||||
self.report(
|
||||
@@ -1891,9 +1920,10 @@ class OverrideEscape(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
if context.scene.BIMGeometryProperties.mode == "ITEM":
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
if props.mode == "ITEM":
|
||||
tool.Geometry.disable_item_mode()
|
||||
elif context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
elif props.mode == "EDIT":
|
||||
bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT", should_save=False)
|
||||
tool.Geometry.disable_item_mode()
|
||||
elif tool.Model.get_model_props().openings:
|
||||
@@ -1949,6 +1979,7 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def handle_single_object(self, context: bpy.types.Context, obj: bpy.types.Object) -> None:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
pprops = tool.Project.get_project_props()
|
||||
if obj == props.representation_obj:
|
||||
self.report({"ERROR"}, f"Element '{obj.name}' is in item mode and cannot be edited directly")
|
||||
elif obj in [o.obj for o in context.scene.BIMAggregateProperties.not_editing_objects]:
|
||||
@@ -1956,7 +1987,7 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report(
|
||||
{"ERROR"}, f"Element '{obj.name}' does not belong to this aggregate and cannot be edited directly"
|
||||
)
|
||||
elif obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
|
||||
elif obj in pprops.clipping_planes_objs:
|
||||
self.report({"ERROR"}, "Clipping planes cannot be edited")
|
||||
elif element:
|
||||
if not obj.data:
|
||||
@@ -2004,12 +2035,15 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.handle_single_object(context, obj)
|
||||
|
||||
def enable_editing_representation_item(self, context: bpy.types.Context, obj: bpy.types.Object) -> None:
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
element = tool.Ifc.get_entity(context.scene.BIMGeometryProperties.representation_obj)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
element = tool.Ifc.get_entity(tool.Geometry.get_geometry_props().representation_obj)
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
tool.Geometry.dissolve_triangulated_edges(obj)
|
||||
tool.Blender.select_and_activate_single_object(context, obj)
|
||||
obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
|
||||
assert isinstance(mesh := obj.data, bpy.types.Mesh)
|
||||
props = tool.Geometry.get_mesh_props(mesh)
|
||||
props.mesh_checksum = tool.Geometry.get_mesh_checksum(mesh)
|
||||
self.enable_edit_mode(context)
|
||||
elif (
|
||||
item.is_a("IfcSweptAreaSolid")
|
||||
@@ -2026,21 +2060,21 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
f"Couldn't import profile, editing it directly is not yet supported. Failing profile: {profile}.",
|
||||
)
|
||||
return
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, obj.data)
|
||||
self.enable_edit_mode(context)
|
||||
ProfileDecorator.install(context)
|
||||
if not bpy.app.background:
|
||||
tool.Blender.set_viewport_tool("bim.cad_tool")
|
||||
elif item.is_a("IfcAnnotationFillArea"):
|
||||
tool.Model.import_annotation_fill_area(item, obj=obj)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, obj.data)
|
||||
self.enable_edit_mode(context)
|
||||
ProfileDecorator.install(context)
|
||||
if not bpy.app.background:
|
||||
tool.Blender.set_viewport_tool("bim.cad_tool")
|
||||
elif tool.Geometry.is_curvelike_item(item):
|
||||
tool.Model.import_curve(item, obj=obj)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, obj.data)
|
||||
self.enable_edit_mode(context)
|
||||
ProfileDecorator.install(context)
|
||||
if not bpy.app.background:
|
||||
@@ -2051,10 +2085,11 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def enable_edit_mode(self, context: bpy.types.Context) -> Union[None, set[str]]:
|
||||
if tool.Blender.toggle_edit_mode(context) == {"CANCELLED"}:
|
||||
return {"CANCELLED"}
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "EDIT":
|
||||
context.scene.BIMGeometryProperties.mode = "EDIT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
props.is_changing_mode = True
|
||||
if props.mode != "EDIT":
|
||||
props.mode = "EDIT"
|
||||
props.is_changing_mode = False
|
||||
|
||||
def has_aggregates(self, objs):
|
||||
for obj in objs:
|
||||
@@ -2101,14 +2136,15 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
tool.Blender.toggle_edit_mode(context)
|
||||
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.representation_obj:
|
||||
if context.scene.BIMGeometryProperties.mode != "ITEM":
|
||||
context.scene.BIMGeometryProperties.mode = "ITEM"
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
props.is_changing_mode = True
|
||||
if props.representation_obj:
|
||||
if props.mode != "ITEM":
|
||||
props.mode = "ITEM"
|
||||
else:
|
||||
if context.scene.BIMGeometryProperties.mode != "OBJECT":
|
||||
context.scene.BIMGeometryProperties.mode = "OBJECT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
if props.mode != "OBJECT":
|
||||
props.mode = "OBJECT"
|
||||
props.is_changing_mode = False
|
||||
|
||||
if context.active_object and self.should_save:
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
@@ -2154,24 +2190,27 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
else:
|
||||
bpy.ops.bim.edit_extrusion_profile()
|
||||
return self.execute(context)
|
||||
elif obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
if not tool.Geometry.has_geometric_data(obj):
|
||||
elif representation := tool.Geometry.get_active_representation(obj):
|
||||
if not tool.Geometry.is_geometric_data(obj.data):
|
||||
self.is_valid = False
|
||||
self.should_save = False
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
assert tool.Geometry.has_mesh_properties(obj.data)
|
||||
mesh_props = tool.Geometry.get_mesh_props(obj.data)
|
||||
if tool.Geometry.is_meshlike(
|
||||
representation
|
||||
) and obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
|
||||
) and mesh_props.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
|
||||
self.edited_objs.append(obj)
|
||||
elif getattr(element, "HasOpenings", None):
|
||||
self.unchanged_objs_with_openings.append(obj)
|
||||
else:
|
||||
tool.Ifc.finish_edit(obj)
|
||||
elif element.is_a("IfcGridAxis"):
|
||||
if not tool.Geometry.has_geometric_data(obj):
|
||||
if not tool.Geometry.is_geometric_data(obj.data):
|
||||
self.is_valid = False
|
||||
self.should_save = False
|
||||
if obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
|
||||
assert tool.Geometry.has_mesh_properties(obj.data)
|
||||
mesh_props = tool.Geometry.get_mesh_props(obj.data)
|
||||
if mesh_props.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
|
||||
self.edited_objs.append(obj)
|
||||
else:
|
||||
tool.Ifc.finish_edit(obj)
|
||||
@@ -2196,9 +2235,11 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def edit_representation_item(self, obj: bpy.types.Object) -> None:
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
if tool.Geometry.has_geometric_data(obj) and obj.data.polygons:
|
||||
if tool.Geometry.is_geometric_data(obj.data) and obj.data.polygons:
|
||||
tool.Geometry.edit_meshlike_item(obj)
|
||||
else:
|
||||
tool.Geometry.import_item(obj)
|
||||
@@ -2219,11 +2260,11 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_profile, profile)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_profile)
|
||||
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.import_item_attributes(obj)
|
||||
|
||||
element = tool.Ifc.get_entity(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
element = tool.Ifc.get_entity(props.representation_obj)
|
||||
# Only certain classes should have a footprint
|
||||
if element.is_a() in ("IfcSlab", "IfcRamp"):
|
||||
footprint_context = ifcopenshell.util.representation.get_context(
|
||||
@@ -2276,9 +2317,9 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
for inverse in tool.Ifc.get().get_inverse(item):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, item, profile)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = profile.id()
|
||||
tool.Ifc.link(profile, obj.data)
|
||||
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.import_item_attributes(obj)
|
||||
elif tool.Geometry.is_curvelike_item(item):
|
||||
@@ -2304,10 +2345,9 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, item, new)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item)
|
||||
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = new.id()
|
||||
tool.Ifc.link(new, obj.data)
|
||||
tool.Geometry.import_item(obj)
|
||||
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item in additional_curves:
|
||||
representation = tool.Geometry.get_active_representation(props.representation_obj)
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
@@ -2316,7 +2356,7 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
name = f"Item/{item.is_a()}/{item.id()}"
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
new_obj = bpy.data.objects.new(name, mesh)
|
||||
new_obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, new_obj.data)
|
||||
bpy.context.collection.objects.link(new_obj)
|
||||
props.add_item_object(new_obj, item)
|
||||
new_obj.matrix_world = obj.matrix_world
|
||||
@@ -2329,10 +2369,11 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def enable_edit_mode(self, context):
|
||||
if tool.Blender.toggle_edit_mode(context) == {"CANCELLED"}:
|
||||
return {"CANCELLED"}
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "EDIT":
|
||||
context.scene.BIMGeometryProperties.mode = "EDIT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
props.is_changing_mode = True
|
||||
if props.mode != "EDIT":
|
||||
props.mode = "EDIT"
|
||||
props.is_changing_mode = False
|
||||
|
||||
|
||||
class FlipObject(bpy.types.Operator):
|
||||
@@ -2369,12 +2410,13 @@ class EnableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
item.tags += ","
|
||||
item.tags += tag
|
||||
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties"):
|
||||
active_representation_id = obj.data.BIMMeshProperties.ifc_definition_id
|
||||
representation = tool.Ifc.get().by_id(active_representation_id)
|
||||
if tool.Geometry.has_mesh_properties((data := obj.data)):
|
||||
representation = tool.Geometry.get_data_representation(data)
|
||||
assert representation
|
||||
|
||||
# Shape aspects must be considered from the PartOfProductDefinitionShape level
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
product_reps = []
|
||||
if element.is_a("IfcProduct"):
|
||||
product_reps = [element.Representation]
|
||||
@@ -2439,7 +2481,8 @@ class DisableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
obj.BIMGeometryProperties.is_editing = False
|
||||
assert obj
|
||||
tool.Geometry.get_object_geometry_props(obj).is_editing = False
|
||||
|
||||
|
||||
class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -2450,9 +2493,12 @@ class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.scene.BIMGeometryProperties.representation_obj:
|
||||
if tool.Geometry.get_geometry_props().representation_obj:
|
||||
return False # Artificial restriction for now to prevent removing when in item mode
|
||||
if not (obj := tool.Geometry.get_active_or_representation_obj()) or len(obj.BIMGeometryProperties.items) <= 1:
|
||||
if (
|
||||
not (obj := tool.Geometry.get_active_or_representation_obj())
|
||||
or len(tool.Geometry.get_object_geometry_props(obj).items) <= 1
|
||||
):
|
||||
cls.poll_message_set(
|
||||
"Active object need to have more than 1 representation items to keep representation valid"
|
||||
)
|
||||
@@ -2487,13 +2533,17 @@ class SelectRepresentationItem(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
item = tool.Ifc.get().by_id(obj.BIMGeometryProperties.active_item.ifc_definition_id)
|
||||
obj_props = tool.Geometry.get_object_geometry_props(obj)
|
||||
assert obj_props.active_item
|
||||
item = tool.Ifc.get().by_id(obj_props.active_item.ifc_definition_id)
|
||||
item_ids = self.get_nested_item_ids(item)
|
||||
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item_obj in props.item_objs:
|
||||
if item_obj.obj.data.BIMMeshProperties.ifc_definition_id in item_ids:
|
||||
tool.Blender.select_object(item_obj.obj)
|
||||
obj_ = item_obj.obj
|
||||
props = tool.Geometry.get_mesh_props(obj_.data)
|
||||
if props.ifc_definition_id in item_ids:
|
||||
tool.Blender.select_object(obj_)
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_nested_item_ids(self, item):
|
||||
@@ -2511,7 +2561,7 @@ class SelectRepresentationItem(bpy.types.Operator):
|
||||
def poll_editing_representation_item_style(cls, context):
|
||||
if not (obj := tool.Geometry.get_active_or_representation_obj()):
|
||||
return False
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
if not props.is_editing:
|
||||
return False
|
||||
if not (item := props.active_item):
|
||||
@@ -2546,7 +2596,8 @@ class EnableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_style = True
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
@@ -2565,7 +2616,8 @@ class EditRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_style = False
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
@@ -2587,7 +2639,7 @@ class DisableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operato
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_style = False
|
||||
|
||||
|
||||
@@ -2603,7 +2655,8 @@ class UnassignRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
active_obj = tool.Geometry.get_active_or_representation_obj()
|
||||
active_props = active_obj.BIMGeometryProperties
|
||||
assert active_obj
|
||||
active_props = tool.Geometry.get_object_geometry_props(active_obj)
|
||||
active_props.is_editing_item_style = False
|
||||
|
||||
# Get active representation item
|
||||
@@ -2670,7 +2723,8 @@ class EnableEditingRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Op
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_shape_aspect = True
|
||||
|
||||
# set dropdown to currently active shape aspect
|
||||
@@ -2686,11 +2740,13 @@ class EditRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
assert obj
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_shape_aspect = False
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
assert props.active_item
|
||||
representation_item_id = props.active_item.ifc_definition_id
|
||||
representation_item = ifc_file.by_id(representation_item_id)
|
||||
|
||||
@@ -2743,7 +2799,8 @@ class DisableEditingRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.O
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_shape_aspect = False
|
||||
|
||||
|
||||
@@ -2754,10 +2811,12 @@ class RemoveRepresentationItemFromShapeAspect(bpy.types.Operator, tool.Ifc.Opera
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
assert obj
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
assert props.active_item
|
||||
representation_item_id = props.active_item.ifc_definition_id
|
||||
representation_item = ifc_file.by_id(representation_item_id)
|
||||
shape_aspect = ifc_file.by_id(props.active_item.shape_aspect_id)
|
||||
@@ -2840,7 +2899,7 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
boolean_ids.add(item.SecondOperand.id())
|
||||
continue
|
||||
item_mesh = bpy.data.meshes.new(f"Item/{item.is_a()}/{item_id}")
|
||||
item_mesh.BIMMeshProperties.ifc_definition_id = item_id
|
||||
tool.Ifc.link(item, item_mesh)
|
||||
|
||||
item_obj = bpy.data.objects.new(f"Item/{item.is_a()}/{item_id}", item_mesh)
|
||||
item_obj.matrix_world = obj.matrix_world
|
||||
@@ -2870,7 +2929,7 @@ class UpdateItemAttributes(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj = context.active_object
|
||||
tool.Geometry.sync_item_positions()
|
||||
tool.Geometry.update_item_attributes(obj)
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(tool.Geometry.get_geometry_props().representation_obj)
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Root.reload_item_decorator()
|
||||
|
||||
@@ -2887,6 +2946,10 @@ class NameProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
extrusion_item_obj: str
|
||||
profile_name: str
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
@@ -2901,7 +2964,7 @@ class NameProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
extrusion_item_obj = bpy.data.objects[self.extrusion_item_obj]
|
||||
mesh_props = extrusion_item_obj.data.BIMMeshProperties
|
||||
mesh_props = tool.Geometry.get_mesh_props(extrusion_item_obj.data)
|
||||
extrusion = ifc_file.by_id(mesh_props.ifc_definition_id)
|
||||
assert extrusion.is_a("IfcSweptAreaSolid")
|
||||
profile = extrusion.SweptArea
|
||||
@@ -2971,9 +3034,9 @@ class AddMeshlikeItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
props.add_item_object(obj, item)
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Geometry.get_mesh_props(obj.data).ifc_definition_id = item.id()
|
||||
tool.Root.reload_item_decorator()
|
||||
|
||||
|
||||
@@ -3019,10 +3082,10 @@ class AddSweptAreaSolidItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
props.add_item_object(obj, item)
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Geometry.get_mesh_props(obj.data).ifc_definition_id = item.id()
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.import_item_attributes(obj)
|
||||
tool.Root.reload_item_decorator()
|
||||
@@ -3090,10 +3153,10 @@ class AddCurvelikeItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
props.add_item_object(obj, item)
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Geometry.get_mesh_props(obj.data).ifc_definition_id = item.id()
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.import_item_attributes(obj)
|
||||
|
||||
@@ -3139,10 +3202,10 @@ class AddHalfSpaceSolidItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Geometry.get_mesh_props(obj.data).ifc_definition_id = item.id()
|
||||
tool.Geometry.import_item(obj)
|
||||
|
||||
# TODO refactor to core and not rely on selection
|
||||
@@ -3168,7 +3231,7 @@ class OverrideMove(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
# Deep magick from the dawn of time
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
if self.new_active_obj:
|
||||
context.view_layer.objects.active = self.new_active_obj
|
||||
|
||||
@@ -32,7 +32,7 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import Optional, TYPE_CHECKING, Union
|
||||
from typing import Optional, TYPE_CHECKING, Union, Literal
|
||||
|
||||
|
||||
def get_contexts(self, context):
|
||||
@@ -270,6 +270,9 @@ class BIMObjectGeometryProperties(PropertyGroup):
|
||||
representation_item_layer: str
|
||||
|
||||
|
||||
GeometryMode = Literal["OBJECT", "ITEM", "EDIT"]
|
||||
|
||||
|
||||
class BIMGeometryProperties(PropertyGroup):
|
||||
# Revit workaround
|
||||
should_use_presentation_style_assignment: BoolProperty(name="Force Presentation Style Assignment", default=False)
|
||||
@@ -308,7 +311,7 @@ class BIMGeometryProperties(PropertyGroup):
|
||||
should_force_faceted_brep: bool
|
||||
should_force_triangulation: bool
|
||||
is_changing_mode: bool
|
||||
mode: str
|
||||
mode: GeometryMode
|
||||
representation_obj: Union[bpy.types.Object, None]
|
||||
item_objs: bpy.types.bpy_prop_collection_idprop[RepresentationItemObject]
|
||||
representation_from_object: Union[bpy.types.Object, None]
|
||||
|
||||
@@ -20,7 +20,6 @@ import bpy
|
||||
import bonsai.bim
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, Menu, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import prop_with_search
|
||||
from bonsai.bim.module.geometry.data import (
|
||||
RepresentationsData,
|
||||
@@ -53,9 +52,10 @@ def mode_menu(self, context):
|
||||
UIData.load()
|
||||
ifc_icon = f"{UIData.data['menu_icon_color_mode']}_ifc"
|
||||
row = self.layout.row(align=True)
|
||||
if context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
if props.mode == "EDIT":
|
||||
row.operator("bim.override_mode_set_object", icon="CANCEL", text="Discard Changes").should_save = False
|
||||
row.prop(context.scene.BIMGeometryProperties, "mode", text="", icon_value=bonsai.bim.icons[ifc_icon].icon_id)
|
||||
row.prop(props, "mode", text="", icon_value=bonsai.bim.icons[ifc_icon].icon_id)
|
||||
|
||||
|
||||
def object_menu(self, context):
|
||||
@@ -336,9 +336,9 @@ class BIM_PT_connections(Panel):
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
if not IfcStore.get_element(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(context.active_object.BIMObjectProperties.ifc_definition_id):
|
||||
return False
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not ConnectionsData.is_loaded:
|
||||
@@ -411,15 +411,18 @@ class BIM_PT_mesh(Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
(obj := context.active_object) is not None
|
||||
and (mesh := obj.data)
|
||||
and isinstance(mesh, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(mesh).ifc_definition_id
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
if not context.active_object.data:
|
||||
return
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="Advanced Users Only", icon="ERROR")
|
||||
|
||||
@@ -427,7 +430,7 @@ class BIM_PT_mesh(Panel):
|
||||
|
||||
row = layout.row()
|
||||
text = "Manually Save Representation"
|
||||
if tool.Ifc.is_edited(context.active_object):
|
||||
if tool.Ifc.is_edited(obj):
|
||||
text += "*"
|
||||
row.operator("bim.update_representation", text=text)
|
||||
|
||||
@@ -454,8 +457,8 @@ class BIM_PT_mesh(Panel):
|
||||
op = row.operator("bim.update_representation", text="Convert To Arbitrary Extrusion With Voids")
|
||||
op.ifc_representation_class = "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
|
||||
|
||||
if context.active_object and context.active_object.data:
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if True:
|
||||
mprops = tool.Geometry.get_mesh_props(mesh)
|
||||
row = layout.row()
|
||||
row.operator("bim.get_representation_ifc_parameters")
|
||||
for index, ifc_parameter in enumerate(mprops.ifc_parameters):
|
||||
@@ -477,7 +480,7 @@ class BIM_PT_placement(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object and context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
return (obj := context.active_object) and obj.BIMObjectProperties.ifc_definition_id
|
||||
|
||||
def draw(self, context):
|
||||
if not PlacementData.is_loaded:
|
||||
@@ -571,10 +574,10 @@ class BIM_PT_workarounds(Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
(obj := context.active_object) is not None
|
||||
and (mesh := obj.data)
|
||||
and isinstance(mesh, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(mesh).ifc_definition_id
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
@@ -588,7 +591,7 @@ class BIM_PT_workarounds(Panel):
|
||||
|
||||
|
||||
class BIM_UL_representation_items(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(self, context, layout: bpy.types.UILayout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
icon = "MATERIAL" if item.surface_style else "MESH_UVSPHERE"
|
||||
row = layout.row(align=True)
|
||||
|
||||
@@ -167,7 +167,7 @@ class GeoreferenceData:
|
||||
result["rotation"] = str(round(ifcopenshell.util.geolocation.yaxis2angle(*wcs[:, 1][:2]), 3))
|
||||
result["x"], result["y"], result["z"] = wcs[:, 3][:3]
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
blender_xyz = ifcopenshell.util.geolocation.enh2xyz(
|
||||
result["x"],
|
||||
@@ -193,7 +193,7 @@ class GeoreferenceData:
|
||||
|
||||
@classmethod
|
||||
def local_origin(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.has_blender_offset:
|
||||
return
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
@@ -21,6 +21,7 @@ import blf
|
||||
import gpu
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.geolocation
|
||||
import bonsai.tool as tool
|
||||
from math import radians
|
||||
from bpy.types import SpaceView3D
|
||||
@@ -53,7 +54,8 @@ class GeoreferenceDecorator:
|
||||
cls.is_installed = False
|
||||
|
||||
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||
self.scale = bpy.context.scene.BIMGeoreferenceProperties.visualization_scale
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
self.scale = props.visualization_scale
|
||||
content_pos = [v * self.scale for v in content_pos]
|
||||
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||
@@ -64,7 +66,7 @@ class GeoreferenceDecorator:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.model_origin: # If this is empty, no georeferencing data has been loaded.
|
||||
return
|
||||
|
||||
@@ -177,7 +179,7 @@ class GeoreferenceDecorator:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.model_origin: # If this is empty, no georeferencing data has been loaded.
|
||||
return
|
||||
|
||||
@@ -350,7 +352,7 @@ class GeoreferenceDecorator:
|
||||
self.gn_angle = float(GeoreferenceData.data["map_derived_angle"] or 0)
|
||||
self.tn_angle = float(GeoreferenceData.data["true_derived_angle"] or 0)
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
blender_angle = ifcopenshell.util.geolocation.xaxis2angle(
|
||||
float(props.blender_x_axis_abscissa), float(props.blender_x_axis_ordinate)
|
||||
|
||||
@@ -33,15 +33,18 @@ from bpy.props import (
|
||||
)
|
||||
from bonsai.bim.module.georeference.data import GeoreferenceData
|
||||
from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
def get_coordinate_operation_class(self, context):
|
||||
def get_coordinate_operation_class(
|
||||
self: "BIMGeoreferenceProperties", context: bpy.types.Context
|
||||
) -> list[tuple[str, str, str]]:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
return GeoreferenceData.data["coordinate_operation_class"]
|
||||
|
||||
|
||||
def update_true_north_angle(self, context):
|
||||
def update_true_north_angle(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -54,7 +57,7 @@ def update_true_north_angle(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_true_north_vector(self, context):
|
||||
def update_true_north_vector(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -67,7 +70,7 @@ def update_true_north_vector(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_grid_north_angle(self, context):
|
||||
def update_grid_north_angle(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -81,7 +84,7 @@ def update_grid_north_angle(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_grid_north_vector(self, context):
|
||||
def update_grid_north_vector(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -95,15 +98,15 @@ def update_grid_north_vector(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_should_visualise(self, context):
|
||||
def update_should_visualise(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.should_visualise:
|
||||
GeoreferenceDecorator.install(bpy.context)
|
||||
else:
|
||||
GeoreferenceDecorator.uninstall()
|
||||
|
||||
|
||||
def update_blender_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_blender_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -123,8 +126,8 @@ def update_blender_coordinates(self, context):
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
def update_local_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_local_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -147,8 +150,8 @@ def update_local_coordinates(self, context):
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
def update_map_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_map_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -248,3 +251,45 @@ class BIMGeoreferenceProperties(PropertyGroup):
|
||||
wcs_y: StringProperty(name="WCS Y", default="0")
|
||||
wcs_z: StringProperty(name="WCS Z", default="0")
|
||||
wcs_rotation: StringProperty(name="WCS Rotation", default="0")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
coordinate_operation_class: str
|
||||
is_changing_angle: bool
|
||||
is_editing: bool
|
||||
is_editing_wcs: bool
|
||||
is_editing_true_north: bool
|
||||
coordinate_operation: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
projected_crs: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
is_updating_coordinates: bool
|
||||
blender_coordinates: str
|
||||
local_coordinates: str
|
||||
map_coordinates: str
|
||||
should_visualise: bool
|
||||
visualization_scale: float
|
||||
grid_north_angle: str
|
||||
x_axis_abscissa: str
|
||||
x_axis_ordinate: str
|
||||
x_axis_is_null: bool
|
||||
|
||||
host_model_origin: str
|
||||
host_model_origin_si: str
|
||||
host_model_project_north: str
|
||||
|
||||
model_origin: str
|
||||
model_origin_si: str
|
||||
model_project_north: str
|
||||
|
||||
has_blender_offset: bool
|
||||
blender_offset_x: str
|
||||
blender_offset_y: str
|
||||
blender_offset_z: str
|
||||
blender_x_axis_abscissa: str
|
||||
blender_x_axis_ordinate: str
|
||||
|
||||
true_north_angle: str
|
||||
true_north_abscissa: str
|
||||
true_north_ordinate: str
|
||||
wcs_x: str
|
||||
wcs_y: str
|
||||
wcs_z: str
|
||||
wcs_rotation: str
|
||||
|
||||
@@ -32,7 +32,7 @@ class BIM_PT_gis(Panel):
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
def draw_header(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
row = self.layout.row(align=True)
|
||||
icon = "HIDE_OFF" if props.should_visualise else "HIDE_ON"
|
||||
row.label(text="") # empty text occupies the left of the row
|
||||
@@ -43,7 +43,7 @@ class BIM_PT_gis(Panel):
|
||||
def draw(self, context):
|
||||
self.layout.use_property_split = True
|
||||
self.layout.use_property_decorate = False
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
@@ -54,7 +54,7 @@ class BIM_PT_gis(Panel):
|
||||
self.draw_ui(context)
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Projected CRS", icon="WORLD")
|
||||
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
|
||||
@@ -87,7 +87,7 @@ class BIM_PT_gis(Panel):
|
||||
draw_attribute(attribute, self.layout.row())
|
||||
|
||||
def draw_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
row = self.layout.row()
|
||||
@@ -149,7 +149,7 @@ class BIM_PT_gis_true_north(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
self.props = context.scene.BIMGeoreferenceProperties
|
||||
self.props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if self.props.is_editing_true_north:
|
||||
self.draw_editable_ui(context)
|
||||
@@ -200,7 +200,7 @@ class BIM_PT_gis_blender(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row()
|
||||
@@ -233,7 +233,7 @@ class BIM_PT_gis_wcs(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.is_editing_wcs:
|
||||
self.draw_editable_ui(context)
|
||||
@@ -263,7 +263,7 @@ class BIM_PT_gis_wcs(Panel):
|
||||
row.operator("bim.enable_editing_wcs", icon="GREASEPENCIL", text="")
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="World Coordinate System", icon="EMPTY_ARROWS")
|
||||
@@ -293,7 +293,7 @@ class BIM_PT_gis_calculator(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -22,7 +22,6 @@ import ifcopenshell.api.group
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
import json
|
||||
|
||||
|
||||
@@ -129,8 +128,7 @@ class RemoveGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
group: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMGroupProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run("group.remove_group", self.file, **{"group": self.file.by_id(self.group)})
|
||||
bpy.ops.bim.load_groups()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.group.data import GroupsData, ObjectGroupsData
|
||||
|
||||
@@ -34,7 +34,7 @@ class BIM_PT_groups(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not GroupsData.is_loaded:
|
||||
@@ -93,7 +93,7 @@ class BIM_PT_object_groups(Panel):
|
||||
def poll(cls, context):
|
||||
if not context.active_object:
|
||||
return False
|
||||
return IfcStore.get_file() and context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
return tool.Ifc.get() and context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
|
||||
def draw(self, context):
|
||||
if not ObjectGroupsData.is_loaded:
|
||||
|
||||
@@ -24,7 +24,6 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class LoadLayers(bpy.types.Operator):
|
||||
@@ -33,7 +32,7 @@ class LoadLayers(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
props = context.scene.BIMLayerProperties
|
||||
props.layers.clear()
|
||||
for layer in tool.Ifc.get().by_type("IfcPresentationLayerAssignment"):
|
||||
@@ -126,7 +125,7 @@ class RemovePresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMLayerProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run("layer.remove_layer", self.file, **{"layer": self.file.by_id(self.layer)})
|
||||
bpy.ops.bim.load_layers()
|
||||
return {"FINISHED"}
|
||||
@@ -142,12 +141,12 @@ class AssignPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"layer.assign_layer",
|
||||
self.file,
|
||||
**{
|
||||
"items": [self.file.by_id(item.BIMMeshProperties.ifc_definition_id)],
|
||||
"items": [self.file.by_id(tool.Geometry.get_mesh_props(item).ifc_definition_id)],
|
||||
"layer": self.file.by_id(self.layer),
|
||||
},
|
||||
)
|
||||
@@ -164,15 +163,10 @@ class UnassignPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"layer.unassign_layer",
|
||||
self.file,
|
||||
**{
|
||||
"items": [self.file.by_id(item.BIMMeshProperties.ifc_definition_id)],
|
||||
"layer": self.file.by_id(self.layer),
|
||||
},
|
||||
)
|
||||
ifc_file = tool.Ifc.get()
|
||||
representation = tool.Geometry.get_data_representation(item)
|
||||
assert representation
|
||||
ifcopenshell.api.layer.unassign_layer(ifc_file, items=[representation], layer=ifc_file.by_id(self.layer))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel, UIList, Mesh
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.module.layer.data import LayersData
|
||||
|
||||
@@ -34,7 +34,7 @@ class BIM_PT_layers(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not LayersData.is_loaded:
|
||||
@@ -77,7 +77,6 @@ class BIM_UL_layers(UIList):
|
||||
row.label(text=item.name)
|
||||
|
||||
if context.active_object and isinstance(context.active_object.data, Mesh):
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if item.ifc_definition_id in LayersData.data["active_layers"]:
|
||||
op = row.operator("bim.unassign_presentation_layer", text="", icon="KEYFRAME_HLT", emboss=False)
|
||||
op.layer = item.ifc_definition_id
|
||||
|
||||
@@ -282,8 +282,9 @@ class ObjectMaterialData:
|
||||
if item.is_a("IfcMaterialLayer"):
|
||||
total_thickness = item.LayerThickness
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
props = tool.Drawing.get_document_props()
|
||||
if unit_system == "IMPERIAL":
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
precision = props.imperial_precision
|
||||
else:
|
||||
precision = None
|
||||
formatted_thickness = format_distance(
|
||||
|
||||
@@ -29,7 +29,6 @@ import bonsai.tool as tool
|
||||
import bonsai.core.style
|
||||
import bonsai.core.material as core
|
||||
import bonsai.bim.module.model.profile as model_profile
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from typing import Any, Union, TYPE_CHECKING
|
||||
from bonsai.bim.module.model import wall, slab
|
||||
|
||||
@@ -118,7 +117,7 @@ class AssignParameterizedProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
profile = ifcopenshell.api.run(
|
||||
"profile.add_parameterized_profile",
|
||||
self.file,
|
||||
@@ -292,7 +291,7 @@ class AddConstituent(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"material.add_constituent",
|
||||
self.file,
|
||||
@@ -328,7 +327,7 @@ class AddProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
props = tool.Material.get_material_props()
|
||||
ifcopenshell.api.run(
|
||||
"material.add_profile",
|
||||
@@ -387,7 +386,7 @@ class ReorderMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
material_set = self.file.by_id(self.material_set)
|
||||
ifcopenshell.api.run(
|
||||
"material.reorder_set_item",
|
||||
@@ -443,7 +442,7 @@ class AddListItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"material.add_list_item",
|
||||
self.file,
|
||||
@@ -463,7 +462,7 @@ class RemoveListItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"material.remove_list_item",
|
||||
self.file,
|
||||
@@ -557,7 +556,7 @@ class EditAssignedMaterial(bpy.types.Operator, tool.Ifc.Operator):
|
||||
material_set_usage: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
active_obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
props = active_obj.BIMObjectMaterialProperties
|
||||
element = tool.Ifc.get_entity(active_obj)
|
||||
@@ -663,7 +662,7 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
|
||||
material_set_item: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.mprops = tool.Material.get_material_props()
|
||||
self.props = obj.BIMObjectMaterialProperties
|
||||
@@ -707,7 +706,7 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
props = obj.BIMObjectMaterialProperties
|
||||
mprops = tool.Material.get_material_props()
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
@@ -22,7 +22,6 @@ from ifcopenshell.util.doc import get_entity_doc
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.material.data import MaterialsData, ObjectMaterialData
|
||||
from bonsai.bim.module.profile.data import ProfileData
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
|
||||
@@ -21,7 +21,6 @@ import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import draw_attributes
|
||||
from bonsai.bim.helper import prop_with_search
|
||||
from bonsai.bim.module.material.data import MaterialsData, ObjectMaterialData
|
||||
@@ -43,7 +42,7 @@ class BIM_PT_materials(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
if not MaterialsData.is_loaded:
|
||||
@@ -143,9 +142,9 @@ class BIM_PT_object_material(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
if not hasattr(IfcStore.get_file().by_id(props.ifc_definition_id), "HasAssociations"):
|
||||
if not hasattr(tool.Ifc.get().by_id(props.ifc_definition_id), "HasAssociations"):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -153,7 +152,7 @@ class BIM_PT_object_material(Panel):
|
||||
if not ObjectMaterialData.is_loaded:
|
||||
ObjectMaterialData.load()
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
self.oprops = context.active_object.BIMObjectProperties
|
||||
self.props = context.active_object.BIMObjectMaterialProperties
|
||||
self.mprops = tool.Material.get_material_props()
|
||||
@@ -351,9 +350,10 @@ class BIM_PT_object_material(Panel):
|
||||
if ObjectMaterialData.data["total_thickness"]:
|
||||
total_thickness = ObjectMaterialData.data["total_thickness"]
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
props = tool.Drawing.get_document_props()
|
||||
|
||||
if unit_system == "IMPERIAL":
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
precision = props.imperial_precision
|
||||
else:
|
||||
precision = None
|
||||
formatted_thickness = format_distance(
|
||||
|
||||
@@ -299,7 +299,7 @@ class DrawSystemArrows(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Blender.select_and_activate_single_object(context, curve)
|
||||
|
||||
def get_absolute_matrix(self, matrix):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
matrix = np.array(
|
||||
ifcopenshell.util.geolocation.global2local(
|
||||
|
||||
@@ -26,7 +26,7 @@ from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
|
||||
import bonsai.tool as tool
|
||||
from math import degrees
|
||||
from natsort import natsorted
|
||||
from typing import Union
|
||||
from typing import Union, Optional
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -45,17 +45,14 @@ class AuthoringData:
|
||||
data = {}
|
||||
type_thumbnails = {}
|
||||
types_per_page = 9
|
||||
ifc_element_type = None
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls, ifc_element_type=None):
|
||||
def load(cls, ifc_element_type: Optional[str] = None):
|
||||
cls.is_loaded = True
|
||||
cls.props = tool.Model.get_model_props()
|
||||
if ifc_element_type:
|
||||
cls.ifc_element_type = None if ifc_element_type == "all" else ifc_element_type
|
||||
cls.data["default_container"] = cls.default_container()
|
||||
cls.data["ifc_element_type"] = cls.ifc_element_type
|
||||
cls.data["ifc_element_type"] = ifc_element_type
|
||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||
cls.data["ifc_class_current"] = cls.ifc_class_current()
|
||||
# Make sure .ifc_classes() was run before next lines
|
||||
@@ -97,7 +94,7 @@ class AuthoringData:
|
||||
|
||||
@classmethod
|
||||
def default_container(cls) -> str | None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
props = tool.Spatial.get_spatial_props()
|
||||
if props.default_container:
|
||||
try:
|
||||
return tool.Ifc.get().by_id(props.default_container).Name
|
||||
|
||||
@@ -20,7 +20,6 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
from bonsai.bim.module.model import product, wall, slab, profile, opening, task
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ import bonsai.tool as tool
|
||||
import bonsai.core.geometry
|
||||
import bonsai.bim.import_ifc as import_ifc
|
||||
from collections import defaultdict
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from math import pi, radians
|
||||
from mathutils import Vector, Matrix
|
||||
from bpy.types import Operator
|
||||
@@ -236,9 +235,9 @@ class FilledOpeningGenerator:
|
||||
voided_elements = ifcopenshell.util.element.get_parts(voided_element) or [voided_element]
|
||||
for voided_element in voided_elements:
|
||||
voided_obj = tool.Ifc.get_object(voided_element)
|
||||
if not voided_obj.data:
|
||||
representation = tool.Geometry.get_active_representation(voided_obj)
|
||||
if not representation:
|
||||
continue
|
||||
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
bonsai.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
@@ -465,10 +464,13 @@ class AddBoolean(Operator, tool.Ifc.Operator):
|
||||
self.report({"INFO"}, "At least two representation items must be selected to add a boolean.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
|
||||
first_item = tool.Ifc.get().by_id(first_obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
second_items = [tool.Ifc.get().by_id(o.data.BIMMeshProperties.ifc_definition_id) for o in second_objs]
|
||||
first_item = tool.Geometry.get_active_representation(first_obj)
|
||||
assert first_item
|
||||
second_items = [
|
||||
representation for o in second_objs if (representation := tool.Geometry.get_active_representation(o))
|
||||
]
|
||||
booleans = ifcopenshell.api.geometry.add_boolean(tool.Ifc.get(), first_item, second_items, props.operator)
|
||||
|
||||
rep_obj = tool.Geometry.get_geometry_props().representation_obj
|
||||
@@ -664,6 +666,7 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
||||
def edit_openings(
|
||||
self, building_objs: set[bpy.types.Object], opening_elements: set[ifcopenshell.entity_instance]
|
||||
) -> None:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
objects_to_remove: set[bpy.types.Object] = set()
|
||||
for opening_element in opening_elements:
|
||||
opening_obj = tool.Ifc.get_object(opening_element)
|
||||
@@ -690,8 +693,8 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
||||
self.get_all_building_objects_of_similar_openings(opening_element)
|
||||
) # NB this has nothing to do with clone similar_opening
|
||||
tool.Ifc.unlink(element=opening_element)
|
||||
if bpy.context.scene.BIMGeometryProperties.representation_obj == opening_obj:
|
||||
bpy.context.scene.BIMGeometryProperties.representation_obj = None
|
||||
if props.representation_obj == opening_obj:
|
||||
props.representation_obj = None
|
||||
objects_to_remove.add(opening_obj)
|
||||
tool.Blender.remove_data_blocks(objects_to_remove, remove_unused_data=True)
|
||||
|
||||
@@ -817,11 +820,11 @@ class RemoveBoolean(Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
return props.active_boolean
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
ifcopenshell.api.geometry.remove_boolean(
|
||||
tool.Ifc.get(), tool.Ifc.get().by_id(props.active_boolean.ifc_definition_id)
|
||||
)
|
||||
@@ -841,7 +844,7 @@ class SelectBoolean(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
return props.active_boolean
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -850,9 +853,9 @@ class SelectBoolean(Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
queue = [tool.Ifc.get().by_id(props.active_boolean.ifc_definition_id)]
|
||||
items = {i.ifc_definition_id: i.obj for i in context.scene.BIMGeometryProperties.item_objs}
|
||||
items = {i.ifc_definition_id: i.obj for i in tool.Geometry.get_geometry_props().item_objs}
|
||||
while queue:
|
||||
item = queue.pop()
|
||||
if item.is_a("IfcBooleanResult"):
|
||||
@@ -911,11 +914,12 @@ class DecorationsHandler:
|
||||
gpu.state.point_size_set(6)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
for opening in props.openings:
|
||||
obj = opening.obj
|
||||
if context.scene.BIMGeometryProperties.representation_obj == obj:
|
||||
if gprops.representation_obj == obj:
|
||||
# We are editing the representation of the opening :
|
||||
for item in context.scene.BIMGeometryProperties.item_objs:
|
||||
for item in gprops.item_objs:
|
||||
if item.obj.mode == "EDIT":
|
||||
obj = item.obj
|
||||
break
|
||||
|
||||
@@ -35,7 +35,6 @@ import bonsai.core.root
|
||||
import bonsai.core.geometry
|
||||
import bonsai.core.model as core
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from math import pi, sin, cos, degrees, tan, radians
|
||||
from mathutils import Vector, Matrix, Quaternion
|
||||
from bonsai.bim.module.model.opening import FilledOpeningGenerator
|
||||
@@ -430,71 +429,90 @@ def get_horizontal_profile_preview_data(context, relating_type):
|
||||
case "9":
|
||||
grouped_verts = [(v[0] + x_offset, v[1] - y_offset, v[2]) for v in grouped_verts]
|
||||
|
||||
# Create profile curve
|
||||
scale_mat = Matrix.Scale(-1, 4, (1.0, 0.0, 0.0))
|
||||
grouped_verts = [scale_mat @ Vector(v) for v in grouped_verts]
|
||||
profile_curve = bpy.data.curves.new("Profile", type="CURVE")
|
||||
profile_curve.dimensions = "2D"
|
||||
profile_curve.splines.new("POLY")
|
||||
profile_curve.splines[0].points.add(len(grouped_verts))
|
||||
|
||||
for i, point in enumerate(profile_curve.splines[0].points):
|
||||
if i == len(grouped_verts): # Close curve
|
||||
point.co = Vector((*grouped_verts[0], 0))
|
||||
continue
|
||||
point.co = Vector((*grouped_verts[i], 0))
|
||||
profile_obj = bpy.data.objects.new("Profile", profile_curve)
|
||||
|
||||
# Create path curve with profile object as bevel
|
||||
path_curve = bpy.data.curves.new("Polyline", type="CURVE")
|
||||
path_curve.dimensions = "2D"
|
||||
path_curve.splines.new("POLY")
|
||||
path_curve.splines[0].points.add(len(polyline_verts) - 1)
|
||||
for i, point in enumerate(path_curve.splines[0].points):
|
||||
point.co = Vector((*polyline_verts[i], 0))
|
||||
path_curve.splines[0].use_smooth = False
|
||||
path_curve.bevel_mode = "OBJECT"
|
||||
path_curve.bevel_object = profile_obj
|
||||
|
||||
# Convert path curve to mesh
|
||||
# This operation throws a warning when done during gpu drawing, so it was removed from the decorator file to be handled here
|
||||
path_obj = bpy.data.objects.new("Preview", path_curve)
|
||||
context.scene.collection.objects.link(path_obj)
|
||||
bpy.context.view_layer.objects.active = path_obj
|
||||
dg = context.evaluated_depsgraph_get()
|
||||
path_obj = path_obj.evaluated_get(dg)
|
||||
me = path_obj.to_mesh()
|
||||
|
||||
# Create bmesh from path mesh
|
||||
bm = bmesh.new()
|
||||
new_verts = [bm.verts.new(v.co) for v in me.vertices]
|
||||
index = [[v for v in edge.vertices] for edge in me.edges]
|
||||
new_edges = [bm.edges.new((new_verts[i[0]], new_verts[i[1]])) for i in index]
|
||||
for face in me.polygons:
|
||||
verts = [new_verts[i] for i in face.vertices]
|
||||
bm.faces.new(verts)
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()]
|
||||
|
||||
bpy.data.objects.remove(bpy.data.objects[path_obj.name], do_unlink=True)
|
||||
bpy.data.objects.remove(bpy.data.objects[profile_obj.name], do_unlink=True)
|
||||
try:
|
||||
bpy.data.curves.remove(profile_obj.data, do_unlink=True)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
bpy.data.curves.remove(path_obj.data, do_unlink=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
data = {}
|
||||
data["verts"] = [tuple(v.co) for v in bm.verts]
|
||||
data["edges"] = [(edge.verts[0].index, edge.verts[1].index) for edge in bm.edges]
|
||||
data["verts"] = []
|
||||
data["edges"] = []
|
||||
data["tris"] = []
|
||||
|
||||
grouped_verts = [(v) for v in grouped_verts]
|
||||
|
||||
all_bm = bmesh.new()
|
||||
for i in range(len(polyline_verts) - 1):
|
||||
mesh = bpy.data.meshes.new("TempMesh")
|
||||
# Create the initial mesh from the profile verts
|
||||
bm = create_bmesh_from_vertices(grouped_verts, is_closed=True)
|
||||
bm.verts.ensure_lookup_table()
|
||||
# Creates the clipping plane formed by two segments.
|
||||
# The first one is for the profile start, based on the current and previous segment of the polyline.
|
||||
# The second is for the profile end, based on the current and the next segment.
|
||||
if i == 0:
|
||||
d = (polyline_verts[i + 1] - polyline_verts[i]).normalized()
|
||||
clip_start = d
|
||||
else:
|
||||
d1 = (polyline_verts[i] - polyline_verts[i - 1]).normalized()
|
||||
d2 = (polyline_verts[i] - polyline_verts[i + 1]).normalized()
|
||||
clip_start = (d1 - d2).normalized()
|
||||
|
||||
if i == len(polyline_verts) - 2:
|
||||
d = (polyline_verts[i + 1] - polyline_verts[i]).normalized()
|
||||
clip_end = d
|
||||
else:
|
||||
d1 = (polyline_verts[i + 1] - polyline_verts[i]).normalized()
|
||||
d2 = (polyline_verts[i + 1] - polyline_verts[i + 2]).normalized()
|
||||
clip_end = (d1 - d2).normalized()
|
||||
|
||||
# Rotates the profile face to the right direction
|
||||
direction = polyline_verts[i + 1] - polyline_verts[i]
|
||||
position = polyline_verts[i]
|
||||
rotation_matrix = direction.to_track_quat("Z", "Y").to_matrix().to_4x4()
|
||||
bmesh.ops.transform(bm, verts=bm.verts, matrix=rotation_matrix)
|
||||
bmesh.ops.translate(bm, verts=bm.verts, vec=position)
|
||||
bmesh.ops.translate(bm, verts=bm.verts, vec=-direction)
|
||||
|
||||
# Extrude and move the new face
|
||||
last_face = bmesh.ops.extrude_face_region(bm, geom=bm.edges[:] + bm.faces[:])
|
||||
new_verts = [e for e in last_face["geom"] if isinstance(e, bmesh.types.BMVert)]
|
||||
bmesh.ops.translate(bm, verts=new_verts, vec=direction * 3)
|
||||
# Apply the cutting planes
|
||||
cut = bmesh.ops.bisect_plane(
|
||||
bm,
|
||||
geom=bm.verts[:] + bm.edges[:] + bm.faces[:],
|
||||
plane_co=polyline_verts[i],
|
||||
plane_no=clip_start,
|
||||
clear_inner=True,
|
||||
)
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
cut = bmesh.ops.bisect_plane(
|
||||
bm,
|
||||
geom=bm.verts[:] + bm.edges[:] + bm.faces[:],
|
||||
plane_co=polyline_verts[i + 1],
|
||||
plane_no=clip_end,
|
||||
clear_outer=True,
|
||||
)
|
||||
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
mesh.update()
|
||||
all_bm.from_mesh(mesh)
|
||||
bpy.data.meshes.remove(bpy.data.meshes["TempMesh"])
|
||||
|
||||
# It's necessary to add the mesh to an object to get the expected result.
|
||||
mesh = bpy.data.meshes.new("TempMesh2")
|
||||
all_bm.to_mesh(mesh)
|
||||
all_bm.free()
|
||||
obj = bpy.data.objects.new("TempObj", mesh)
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
bpy.data.meshes.remove(bpy.data.meshes["TempMesh2"])
|
||||
|
||||
verts = [tuple(v.co) for v in bm.verts]
|
||||
edges = [[v.index for v in e.verts] for e in bm.edges]
|
||||
tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()]
|
||||
data["verts"] = verts
|
||||
data["edges"] = edges
|
||||
data["tris"] = tris
|
||||
|
||||
bm.free()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -598,7 +616,7 @@ class PolylineOperator:
|
||||
self.report({"WARNING"}, "The number typed is not valid.")
|
||||
return is_valid
|
||||
else:
|
||||
if self.input_type in {"X", "Y"}:
|
||||
if self.input_type in {"X", "Y", "Z"}:
|
||||
tool.Polyline.calculate_distance_and_angle(context, self.input_ui, self.tool_state)
|
||||
elif self.input_type in {"D", "A"}:
|
||||
tool.Polyline.calculate_x_y_and_z(context, self.input_ui, self.tool_state)
|
||||
@@ -633,21 +651,21 @@ class PolylineOperator:
|
||||
if x:
|
||||
if event.shift and event.value == "PRESS" and event.type == "X":
|
||||
self.tool_state.use_default_container = False
|
||||
self.tool_state.plane_method = "YZ"
|
||||
self.tool_state.plane_method = "YZ" if self.tool_state.plane_method != "YZ" else None
|
||||
self.tool_state.axis_method = None
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if y:
|
||||
if event.shift and event.value == "PRESS" and event.type == "Y":
|
||||
self.tool_state.use_default_container = False
|
||||
self.tool_state.plane_method = "XZ"
|
||||
self.tool_state.plane_method = "XZ" if self.tool_state.plane_method != "XZ" else None
|
||||
self.tool_state.axis_method = None
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
if z:
|
||||
if event.shift and event.value == "PRESS" and event.type == "Z":
|
||||
self.tool_state.use_default_container = False
|
||||
self.tool_state.plane_method = "XY"
|
||||
self.tool_state.plane_method = "XY" if self.tool_state.plane_method != "XY" else None
|
||||
self.tool_state.axis_method = None
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ class AddEmptyType(bpy.types.Operator, AddObjectHelper):
|
||||
def execute(self, context):
|
||||
obj = bpy.data.objects.new("TYPEX", None)
|
||||
context.scene.collection.objects.link(obj)
|
||||
context.scene.BIMRootProperties.ifc_product = "IfcElementType"
|
||||
rprops = tool.Root.get_root_props()
|
||||
rprops.ifc_product = "IfcElementType"
|
||||
tool.Blender.select_and_activate_single_object(context, obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -71,7 +72,7 @@ class AddDefaultType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_element_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMRootProperties
|
||||
props = tool.Root.get_root_props()
|
||||
props.ifc_product = "IfcElementType"
|
||||
props.ifc_class = self.ifc_element_type
|
||||
if self.ifc_element_type == "IfcWallType":
|
||||
@@ -363,7 +364,7 @@ class AddConstrTypeInstance(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
bonsai.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type)
|
||||
|
||||
rprops = context.scene.BIMRootProperties
|
||||
rprops = tool.Root.get_root_props()
|
||||
ifc_context = None
|
||||
if get_enum_items(rprops, "contexts", context):
|
||||
ifc_context = int(rprops.contexts or "0") or None
|
||||
@@ -757,7 +758,7 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings):
|
||||
elements.append(element)
|
||||
|
||||
for element in elements:
|
||||
obj = IfcStore.get_element(element.id())
|
||||
obj = tool.Ifc.get_object_by_identifier(element.id())
|
||||
if not obj:
|
||||
continue
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@@ -22,6 +22,7 @@ import bmesh
|
||||
import mathutils.geometry
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.util.type
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
@@ -47,6 +48,7 @@ class DumbProfileGenerator:
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
def generate(self, insertion_type="CURSOR"):
|
||||
self.insertion_type = insertion_type
|
||||
self.file = tool.Ifc.get()
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
material = ifcopenshell.util.element.get_material(self.relating_type)
|
||||
@@ -69,9 +71,9 @@ class DumbProfileGenerator:
|
||||
self.rotation = 0
|
||||
self.location = Vector((0, 0, 0))
|
||||
self.cardinal_point = int(props.cardinal_point)
|
||||
if insertion_type == "POLYLINE":
|
||||
if self.insertion_type == "POLYLINE":
|
||||
return self.derive_from_polyline()
|
||||
elif insertion_type == "CURSOR":
|
||||
elif self.insertion_type == "CURSOR":
|
||||
return self.derive_from_cursor()
|
||||
|
||||
def derive_from_polyline(self) -> tuple[list[Union[dict[str, Any], None]], bool]:
|
||||
@@ -106,13 +108,16 @@ class DumbProfileGenerator:
|
||||
|
||||
matrix_world = Matrix()
|
||||
if self.relating_type.is_a() not in ("IfcColumnType", "IfcPileType"):
|
||||
matrix_world = Matrix.Rotation(pi / 2, 4, "Z") @ Matrix.Rotation(pi / 2, 4, "X") @ matrix_world
|
||||
matrix_world = Matrix.Rotation(self.rotation, 4, "Z") @ matrix_world
|
||||
if self.insertion_type not in {"POLYLINE"}:
|
||||
matrix_world = Matrix.Rotation(pi / 2, 4, "Z") @ Matrix.Rotation(pi / 2, 4, "X") @ matrix_world
|
||||
matrix_world = Matrix.Rotation(self.rotation, 4, "Z") @ matrix_world
|
||||
else:
|
||||
rotation_matrix = self.direction.to_track_quat("Z", "Y")
|
||||
matrix_world = rotation_matrix.to_matrix().to_4x4() @ matrix_world
|
||||
|
||||
matrix_world.translation = self.location
|
||||
if self.container_obj:
|
||||
if self.insertion_type not in {"POLYLINE"} and self.container_obj:
|
||||
matrix_world.translation.z = self.container_obj.location.z
|
||||
|
||||
element = bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
@@ -170,19 +175,19 @@ class DumbProfileGenerator:
|
||||
return obj
|
||||
|
||||
def create_profile_from_2_points(self, coords, should_round=False) -> Union[dict[str, Any], None]:
|
||||
direction = coords[1] - coords[0]
|
||||
length = direction.length
|
||||
self.direction = coords[1] - coords[0]
|
||||
length = self.direction.length
|
||||
if round(length, 4) < 0.1:
|
||||
return
|
||||
data = {"coords": coords}
|
||||
|
||||
self.depth = length
|
||||
self.rotation = atan2(direction[1], direction[0])
|
||||
self.rotation = atan2(self.direction[1], self.direction[0])
|
||||
if should_round:
|
||||
# Round to nearest 50mm (yes, metric for now)
|
||||
self.length = 0.05 * round(length / 0.05)
|
||||
# Round to nearest 5 degrees
|
||||
nearest_degree = (math.pi / 180) * 5
|
||||
nearest_degree = (pi / 180) * 5
|
||||
self.rotation = nearest_degree * round(self.rotation / nearest_degree)
|
||||
self.location = coords[0]
|
||||
data["obj"] = self.create_profile()
|
||||
@@ -265,7 +270,7 @@ class DumbProfileRegenerator:
|
||||
|
||||
def _regenerate_from_type(self, related_object: ifcopenshell.entity_instance) -> None:
|
||||
obj = tool.Ifc.get_object(related_object)
|
||||
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
if not obj or not tool.Geometry.get_active_representation(obj):
|
||||
return
|
||||
DumbProfileRecalculator().recalculate([obj])
|
||||
|
||||
@@ -501,7 +506,9 @@ class DumbProfileJoiner:
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=new_axis
|
||||
)
|
||||
|
||||
def get_placement_axes(body_representation):
|
||||
def get_placement_axes(
|
||||
body_representation: Union[ifcopenshell.entity_instance, None],
|
||||
) -> Union[tuple[tuple[float, float, float], tuple[float, float, float]], tuple[None, None]]:
|
||||
if not body_representation:
|
||||
return None, None
|
||||
extrusion = tool.Model.get_extrusion(body_representation)
|
||||
@@ -513,8 +520,7 @@ class DumbProfileJoiner:
|
||||
return ((0.0, 0.0, 1.0), (1.0, 0.0, 0.0))
|
||||
|
||||
old_body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
new_body = ifcopenshell.api.run(
|
||||
"geometry.add_profile_representation",
|
||||
new_body = ifcopenshell.api.geometry.add_profile_representation(
|
||||
tool.Ifc.get(),
|
||||
context=self.body_context,
|
||||
profile=self.profile,
|
||||
@@ -527,8 +533,9 @@ class DumbProfileJoiner:
|
||||
if old_body:
|
||||
for inverse in tool.Ifc.get().get_inverse(old_body):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_body, new_body)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_body.id())
|
||||
obj.data.name = f"{self.body_context.id()}/{new_body.id()}"
|
||||
assert isinstance(mesh := obj.data, bpy.types.Mesh)
|
||||
tool.Ifc.link(new_body, mesh)
|
||||
mesh.name = tool.Loader.get_mesh_name(new_body)
|
||||
bonsai.core.geometry.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_body)
|
||||
else:
|
||||
ifcopenshell.api.run(
|
||||
@@ -1119,6 +1126,8 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator, tool.Ifc.Operato
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.input_ui = tool.Polyline.create_input_ui(init_z=True)
|
||||
self.input_options = ["D", "A", "X", "Y", "Z"]
|
||||
self.relating_type = None
|
||||
props = tool.Model.get_model_props()
|
||||
relating_type_id = props.relating_type_id
|
||||
@@ -1163,7 +1172,9 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator, tool.Ifc.Operato
|
||||
|
||||
self.handle_mouse_move(context, event, should_round=True)
|
||||
|
||||
self.choose_axis(event)
|
||||
self.choose_axis(event, z=True)
|
||||
|
||||
self.choose_plane(event)
|
||||
|
||||
self.handle_snap_selection(context, event)
|
||||
|
||||
@@ -1199,6 +1210,6 @@ class DrawPolylineProfile(bpy.types.Operator, PolylineOperator, tool.Ifc.Operato
|
||||
def _invoke(self, context, event):
|
||||
super().invoke(context, event)
|
||||
ProductDecorator.install(context)
|
||||
self.tool_state.use_default_container = True
|
||||
self.tool_state.plane_method = "XY"
|
||||
self.tool_state.use_default_container = False
|
||||
self.tool_state.plane_method = None
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
@@ -60,6 +60,8 @@ def get_materials(self, context):
|
||||
|
||||
def update_ifc_class(self, context):
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class)
|
||||
AuthoringData.data["ifc_class_current"] = self.ifc_class
|
||||
AuthoringData.data["type_elements"] = AuthoringData.type_elements()
|
||||
AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id()
|
||||
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
|
||||
if tool.Blender.get_enum_safe(self, "relating_type_id") is None:
|
||||
|
||||
@@ -259,7 +259,7 @@ class DumbSlabPlaner:
|
||||
self, related_object: ifcopenshell.entity_instance, layer_set_direction: Optional[str], new_thickness: float
|
||||
) -> None:
|
||||
obj = tool.Ifc.get_object(related_object)
|
||||
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
if not obj or not tool.Geometry.get_active_representation(obj):
|
||||
return
|
||||
|
||||
material = ifcopenshell.util.element.get_material(related_object)
|
||||
@@ -885,11 +885,13 @@ class DrawPolylineSlab(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
slab = DumbSlabGenerator(self.relating_type).generate("POLYLINE")
|
||||
if not slab:
|
||||
return
|
||||
|
||||
model_props = tool.Model.get_model_props()
|
||||
direction_sense = model_props.direction_sense
|
||||
offset = model_props.offset
|
||||
model = IfcStore.get_file()
|
||||
model = tool.Ifc.get()
|
||||
element = tool.Ifc.get_entity(slab)
|
||||
material = ifcopenshell.util.element.get_material(element)
|
||||
material_set_usage = model.by_id(material.id())
|
||||
|
||||
@@ -117,9 +117,8 @@ class LaunchTypeManager(bpy.types.Operator):
|
||||
op.ifc_product = "IfcElementType"
|
||||
op.ifc_class = AuthoringData.data["ifc_element_type"] or props.ifc_class or ""
|
||||
|
||||
if AuthoringData.data["total_types"]:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "search_name", icon="FILTER", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "search_name", icon="FILTER", text="")
|
||||
|
||||
columns = self.layout.column_flow(columns=3)
|
||||
if AuthoringData.data["total_pages"] > 0:
|
||||
|
||||
@@ -377,7 +377,7 @@ class DrawPolylineWall(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
|
||||
|
||||
walls, is_polyline_closed = DumbWallGenerator(self.relating_type).generate("POLYLINE")
|
||||
for wall in walls:
|
||||
model = IfcStore.get_file()
|
||||
model = tool.Ifc.get()
|
||||
element = tool.Ifc.get_entity(wall["obj"])
|
||||
material = ifcopenshell.util.element.get_material(element)
|
||||
material_set_usage = model.by_id(material.id())
|
||||
@@ -598,7 +598,7 @@ class DumbWallGenerator:
|
||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
def generate(self, insertion_type="CURSOR"):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
self.layers = tool.Model.get_material_layer_parameters(self.relating_type)
|
||||
if not self.layers["thickness"]:
|
||||
return
|
||||
@@ -908,7 +908,7 @@ class DumbWallPlaner:
|
||||
self, related_object: ifcopenshell.entity_instance, layer_set_direction: Optional[str]
|
||||
) -> None:
|
||||
obj = tool.Ifc.get_object(related_object)
|
||||
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
if not obj or not tool.Geometry.get_active_representation(obj):
|
||||
return
|
||||
|
||||
material = ifcopenshell.util.element.get_material(related_object)
|
||||
@@ -1331,7 +1331,9 @@ class DumbWallJoiner:
|
||||
axis = body = tool.Model.get_wall_axis(obj)["reference"]
|
||||
self.axis = copy.deepcopy(axis)
|
||||
self.body = copy.deepcopy(body)
|
||||
extrusion_data = self.get_extrusion_data(tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id))
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
assert representation
|
||||
extrusion_data = self.get_extrusion_data(representation)
|
||||
height = extrusion_data["height"]
|
||||
x_angle = extrusion_data["x_angle"]
|
||||
self.clippings = []
|
||||
@@ -1410,8 +1412,9 @@ class DumbWallJoiner:
|
||||
if old_body:
|
||||
for inverse in tool.Ifc.get().get_inverse(old_body):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_body, new_body)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_body.id())
|
||||
obj.data.name = f"{self.body_context.id()}/{new_body.id()}"
|
||||
assert isinstance(mesh := obj.data, bpy.types.Mesh)
|
||||
tool.Ifc.link(new_body, mesh)
|
||||
mesh.name = tool.Loader.get_mesh_name(new_body)
|
||||
bonsai.core.geometry.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_body)
|
||||
else:
|
||||
ifcopenshell.api.run(
|
||||
@@ -1708,10 +1711,11 @@ class DumbWallJoiner:
|
||||
|
||||
return True
|
||||
|
||||
def clip(self, wall1, slab2):
|
||||
def clip(self, wall1: bpy.types.Object, slab2: bpy.types.Object) -> float:
|
||||
"""returns height of the clipped wall, adds clipping plane to `clippings`"""
|
||||
element1 = tool.Ifc.get_entity(wall1)
|
||||
element2 = tool.Ifc.get_entity(slab2)
|
||||
assert element1 and element2
|
||||
|
||||
layers1 = tool.Model.get_material_layer_parameters(element1)
|
||||
axis1 = tool.Model.get_wall_axis(wall1, layers1)
|
||||
@@ -1719,7 +1723,9 @@ class DumbWallJoiner:
|
||||
bases = [axis1["base"][0].to_3d(), axis1["base"][1].to_3d(), axis1["side"][0].to_3d(), axis1["side"][1].to_3d()]
|
||||
bases = [Vector((v[0], v[1], wall1.matrix_world.translation.z)) for v in bases] # add wall Z location
|
||||
|
||||
extrusion = self.get_extrusion_data(tool.Ifc.get().by_id(wall1.data.BIMMeshProperties.ifc_definition_id))
|
||||
representation = tool.Geometry.get_active_representation(wall1)
|
||||
assert representation
|
||||
extrusion = self.get_extrusion_data(representation)
|
||||
wall_dir = wall1.matrix_world.to_quaternion() @ extrusion["direction"]
|
||||
|
||||
slab_pt = slab2.matrix_world @ Vector((0, 0, 0))
|
||||
|
||||
@@ -96,14 +96,16 @@ class BimTool(WorkSpaceTool):
|
||||
def draw_settings(
|
||||
cls, context: bpy.types.Context, layout: bpy.types.UILayout, ws_tool: bpy.types.WorkSpaceTool
|
||||
) -> None:
|
||||
if context.scene.BIMGeometryProperties.mode == "ITEM":
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
ifc_element_type = None if cls.ifc_element_type == "all" else cls.ifc_element_type
|
||||
if props.mode == "ITEM":
|
||||
EditItemUI.draw(context, layout)
|
||||
elif (
|
||||
active_ifc_object := (context.active_object and tool.Ifc.get_entity(context.active_object))
|
||||
) and context.selected_objects:
|
||||
EditObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
EditObjectUI.draw(context, layout, ifc_element_type=ifc_element_type)
|
||||
else:
|
||||
CreateObjectUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||
CreateObjectUI.draw(context, layout, ifc_element_type=ifc_element_type)
|
||||
|
||||
|
||||
class WallTool(BimTool):
|
||||
@@ -430,7 +432,7 @@ class EditItemUI:
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
|
||||
mesh_props = obj.data.BIMMeshProperties
|
||||
mesh_props = tool.Geometry.get_mesh_props(obj.data)
|
||||
if AuthoringData.data["is_representation_item_swept_solid"]:
|
||||
# TODO: support EndSweptArea for IfcRevolvedAreaSolidTapered,
|
||||
# will need to add second attribute for this.
|
||||
@@ -440,10 +442,10 @@ class EditItemUI:
|
||||
op = row.operator("bim.name_profile", text="", icon="TAG")
|
||||
op.extrusion_item_obj = obj.name
|
||||
|
||||
for item_attribute in obj.data.BIMMeshProperties.item_attributes:
|
||||
for item_attribute in mesh_props.item_attributes:
|
||||
row = cls.layout.row()
|
||||
draw_attribute(item_attribute, cls.layout)
|
||||
if len(obj.data.BIMMeshProperties.item_attributes) or AuthoringData.data["is_representation_item_swept_solid"]:
|
||||
if len(mesh_props.item_attributes) or AuthoringData.data["is_representation_item_swept_solid"]:
|
||||
row = cls.layout.row()
|
||||
row.operator("bim.update_item_attributes", icon="FILE_REFRESH", text="")
|
||||
|
||||
@@ -499,9 +501,7 @@ class CreateObjectUI:
|
||||
layout: bpy.types.UILayout
|
||||
|
||||
@classmethod
|
||||
def draw(
|
||||
cls, context: bpy.types.Context, layout: bpy.types.UILayout, ifc_element_type: Optional[str] = None
|
||||
) -> None:
|
||||
def draw(cls, context: bpy.types.Context, layout: bpy.types.UILayout, ifc_element_type: Union[str, None]) -> None:
|
||||
cls.layout = layout
|
||||
cls.props = tool.Model.get_model_props()
|
||||
|
||||
@@ -515,15 +515,13 @@ class CreateObjectUI:
|
||||
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load(ifc_element_type)
|
||||
elif ifc_element_type == "all" and AuthoringData.data["ifc_element_type"] is not None:
|
||||
AuthoringData.load("all")
|
||||
elif AuthoringData.data["ifc_element_type"] != ifc_element_type:
|
||||
AuthoringData.load(ifc_element_type)
|
||||
|
||||
if ifc_element_type and context.region.type == "TOOL_HEADER":
|
||||
if context.region.type == "TOOL_HEADER":
|
||||
tool_name = (
|
||||
"Multi Object Tool"
|
||||
if ifc_element_type == "all"
|
||||
if ifc_element_type is None
|
||||
else format_ifc_camel_case(ifc_element_type.removesuffix("Type")) + " Tool"
|
||||
)
|
||||
cls.layout.label(text=tool_name, icon="TOOL_SETTINGS")
|
||||
@@ -746,8 +744,6 @@ class EditObjectUI:
|
||||
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load(ifc_element_type)
|
||||
elif ifc_element_type == "all" and AuthoringData.data["ifc_element_type"] is not None:
|
||||
AuthoringData.load("all")
|
||||
elif AuthoringData.data["ifc_element_type"] != ifc_element_type:
|
||||
AuthoringData.load(ifc_element_type)
|
||||
|
||||
@@ -1136,7 +1132,8 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
row.prop(self, "z")
|
||||
|
||||
def hotkey_S_A(self):
|
||||
if bpy.context.scene.BIMGeometryProperties.mode == "ITEM":
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
if gprops.mode == "ITEM":
|
||||
bpy.ops.wm.call_menu(name="BIM_MT_add_representation_item")
|
||||
return
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.nest as core
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_OT_nest_assign_object(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -113,7 +112,7 @@ class BIM_OT_select_components(bpy.types.Operator):
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
obj = bpy.data.objects.get(self.obj) or context.active_object
|
||||
components = ifcopenshell.util.element.get_components(tool.Ifc.get_entity(obj))
|
||||
component_objs = set(tool.Ifc.get_object(c) for c in components)
|
||||
@@ -132,7 +131,7 @@ class BIM_OT_select_nest(bpy.types.Operator):
|
||||
obj: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
obj = bpy.data.objects.get(self.obj) or context.active_object
|
||||
nest = ifcopenshell.util.element.get_nest(tool.Ifc.get_entity(obj))
|
||||
nest_obj = tool.Ifc.get_object(nest)
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.module.nest.data import NestData
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class BIM_PT_nest(Panel):
|
||||
@@ -37,9 +37,9 @@ class BIM_PT_nest(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
if not tool.Ifc.get().by_id(props.ifc_definition_id).is_a("IfcObjectDefinition"):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class ProfileData:
|
||||
obj = bpy.context.active_object
|
||||
return (
|
||||
obj
|
||||
and obj.data
|
||||
and hasattr(obj.data, "BIMMeshProperties")
|
||||
and obj.data.BIMMeshProperties.subshape_type == "PROFILE"
|
||||
and (data := obj.data)
|
||||
and isinstance(data, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(data).subshape_type == "PROFILE"
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.profile
|
||||
import ifcopenshell.util.element
|
||||
import bonsai.bim.helper
|
||||
import bonsai.tool as tool
|
||||
@@ -174,12 +175,12 @@ class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props.object_to_profile = None
|
||||
if not indices:
|
||||
points = [(0, 0), (0.1, 0), (0.1, 0.1), (0, 0.1), (0, 0)]
|
||||
profile = ifcopenshell.api.run("profile.add_arbitrary_profile", tool.Ifc.get(), profile=points)
|
||||
profile = ifcopenshell.api.profile.add_arbitrary_profile(tool.Ifc.get(), profile=points)
|
||||
else:
|
||||
if "inner_curves" not in indices:
|
||||
points = [(obj.data.vertices[i].co.x, obj.data.vertices[i].co.y) for i in indices["profile"]]
|
||||
points.append(points[0])
|
||||
profile = ifcopenshell.api.run("profile.add_arbitrary_profile", tool.Ifc.get(), profile=points)
|
||||
profile = ifcopenshell.api.profile.add_arbitrary_profile(tool.Ifc.get(), profile=points)
|
||||
else:
|
||||
outer_points = [(obj.data.vertices[i].co.x, obj.data.vertices[i].co.y) for i in indices["profile"]]
|
||||
outer_points.append(outer_points[0])
|
||||
@@ -189,8 +190,7 @@ class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator):
|
||||
]
|
||||
for curve in inner_points:
|
||||
curve.append(curve[0])
|
||||
profile = ifcopenshell.api.run(
|
||||
"profile.add_arbitrary_profile_with_voids",
|
||||
profile = ifcopenshell.api.profile.add_arbitrary_profile_with_voids(
|
||||
tool.Ifc.get(),
|
||||
outer_profile=outer_points,
|
||||
inner_profiles=inner_points,
|
||||
@@ -248,7 +248,12 @@ class EnableEditingArbitraryProfile(bpy.types.Operator):
|
||||
|
||||
def disable_editing_arbitrary_profile(context):
|
||||
obj = context.active_object
|
||||
if obj and obj.type == "MESH" and obj.data and obj.data.BIMMeshProperties.subshape_type == "PROFILE":
|
||||
if (
|
||||
obj
|
||||
and (mesh := obj.data)
|
||||
and isinstance(mesh, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(mesh).subshape_type == "PROFILE"
|
||||
):
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
profile_mesh = obj.data
|
||||
|
||||
@@ -21,7 +21,6 @@ import ifcopenshell
|
||||
import ifcopenshell.util.schema
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bonsai.bim.module.profile.data import ProfileData
|
||||
from bpy.types import PropertyGroup
|
||||
|
||||
@@ -31,7 +31,8 @@ from typing import Union
|
||||
|
||||
@persistent
|
||||
def toggle_decorations_on_load(*args):
|
||||
if bpy.context.scene.BIMProjectProperties.clipping_planes:
|
||||
props = tool.Project.get_project_props()
|
||||
if props.clipping_planes:
|
||||
ClippingPlaneDecorator.install(bpy.context)
|
||||
else:
|
||||
ClippingPlaneDecorator.uninstall()
|
||||
@@ -99,7 +100,7 @@ class ProjectDecorator:
|
||||
selected_edges = []
|
||||
selected_tris = []
|
||||
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
try:
|
||||
obj = props.queried_obj
|
||||
selected_vertices = obj["selected_vertices"]
|
||||
@@ -171,7 +172,8 @@ class ClippingPlaneDecorator:
|
||||
unselected_edges = []
|
||||
unselected_tris = []
|
||||
|
||||
for clipping_plane in context.scene.BIMProjectProperties.clipping_planes:
|
||||
props = tool.Project.get_project_props()
|
||||
for clipping_plane in props.clipping_planes:
|
||||
obj = clipping_plane.obj
|
||||
if not obj or not obj.data:
|
||||
continue
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import GizmoGroup
|
||||
from mathutils import Matrix
|
||||
|
||||
@@ -32,11 +33,12 @@ class ClippingPlane(GizmoGroup):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.object
|
||||
props = tool.Project.get_project_props()
|
||||
return (
|
||||
context.selected_objects
|
||||
and obj
|
||||
and obj.name.startswith("ClippingPlane")
|
||||
and obj in [sp.obj for sp in context.scene.BIMProjectProperties.clipping_planes]
|
||||
and obj in [sp.obj for sp in props.clipping_planes]
|
||||
)
|
||||
|
||||
def setup(self, context):
|
||||
|
||||
@@ -75,35 +75,37 @@ class NewProject(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.wm.read_homefile()
|
||||
pprops = tool.Project.get_project_props()
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
|
||||
if self.preset == "metric_m":
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||
pprops.export_schema = "IFC4"
|
||||
bpy.context.scene.unit_settings.system = "METRIC"
|
||||
bpy.context.scene.unit_settings.length_unit = "METERS"
|
||||
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
bim_props.area_unit = "SQUARE_METRE"
|
||||
bim_props.volume_unit = "CUBIC_METRE"
|
||||
pprops.template_file = "0"
|
||||
elif self.preset == "metric_mm":
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||
pprops.export_schema = "IFC4"
|
||||
bpy.context.scene.unit_settings.system = "METRIC"
|
||||
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
|
||||
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
bim_props.area_unit = "SQUARE_METRE"
|
||||
bim_props.volume_unit = "CUBIC_METRE"
|
||||
pprops.template_file = "0"
|
||||
elif self.preset == "imperial_ft":
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||
pprops.export_schema = "IFC4"
|
||||
bpy.context.scene.unit_settings.system = "IMPERIAL"
|
||||
bpy.context.scene.unit_settings.length_unit = "FEET"
|
||||
bpy.context.scene.BIMProperties.area_unit = "square foot"
|
||||
bpy.context.scene.BIMProperties.volume_unit = "cubic foot"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
bim_props.area_unit = "square foot"
|
||||
bim_props.volume_unit = "cubic foot"
|
||||
pprops.template_file = "0"
|
||||
elif self.preset == "demo":
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||
pprops.export_schema = "IFC4"
|
||||
bpy.context.scene.unit_settings.system = "METRIC"
|
||||
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
|
||||
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "IFC4 Demo Template.ifc"
|
||||
bim_props.area_unit = "SQUARE_METRE"
|
||||
bim_props.volume_unit = "CUBIC_METRE"
|
||||
pprops.template_file = "IFC4 Demo Template.ifc"
|
||||
|
||||
if self.preset != "wizard":
|
||||
bpy.ops.bim.create_project()
|
||||
@@ -126,7 +128,7 @@ class CreateProject(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
template = None if props.template_file == "0" else props.template_file
|
||||
if tool.Blender.is_default_scene():
|
||||
for obj in bpy.data.objects:
|
||||
@@ -236,7 +238,7 @@ class RefreshLibrary(bpy.types.Operator):
|
||||
elements.update(library_file.by_type(importable_type))
|
||||
rels = tool.Project.get_project_library_rels(library_file)
|
||||
elements = {e for e in elements if not tool.Project.is_element_assigned_to_project_library(e, rels)}
|
||||
self.props.add_library_project_library("Unassigned", len(elements), 0)
|
||||
self.props.add_library_project_library("Unassigned", len(elements), 0, False)
|
||||
|
||||
ifc_project = library_file.by_type("IfcProject")[0]
|
||||
hierarchy = tool.Project.get_project_hierarchy(library_file)
|
||||
@@ -304,13 +306,14 @@ class ChangeLibraryElement(bpy.types.Operator):
|
||||
if self.breadcrumb_type == "LIBRARY":
|
||||
hierarchy = tool.Project.get_project_hierarchy(library_file)
|
||||
assert active_project_library is not None
|
||||
if active_project_library == "NO_LIBRARY" or not hierarchy[active_project_library]:
|
||||
for appendable_type in sorted(tool.Project.get_appendable_asset_types()):
|
||||
elements = library_file.by_type(appendable_type)
|
||||
if elements := filter_elements(elements):
|
||||
self.props.add_library_asset_class(appendable_type, len(elements))
|
||||
else:
|
||||
if active_project_library != "NO_LIBRARY" and hierarchy[active_project_library]:
|
||||
tool.Project.load_project_libraries_to_ui(active_project_library, hierarchy)
|
||||
|
||||
for appendable_type in sorted(tool.Project.get_appendable_asset_types()):
|
||||
elements = library_file.by_type(appendable_type)
|
||||
if elements := filter_elements(elements):
|
||||
self.props.add_library_asset_class(appendable_type, len(elements))
|
||||
|
||||
else: # breadcrumb_type CLASS.
|
||||
elements = self.library_file.by_type(self.element_name)
|
||||
elements = list(filter_elements(elements))
|
||||
@@ -501,10 +504,10 @@ class AppendEntireLibrary(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
self.library = IfcStore.library_file
|
||||
|
||||
query = ", ".join(tool.Project.get_appendable_asset_types())
|
||||
@@ -521,10 +524,10 @@ class AppendLibraryElementByQuery(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
self.library = IfcStore.library_file
|
||||
|
||||
for element in ifcopenshell.util.selector.filter_elements(self.library, self.query):
|
||||
@@ -554,7 +557,7 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
poll = bool(IfcStore.get_file())
|
||||
poll = bool(tool.Ifc.get())
|
||||
if not poll:
|
||||
cls.poll_message_set("Please create or load a project first.")
|
||||
return poll
|
||||
@@ -591,7 +594,8 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.import_presentation_style_from_ifc(element, context)
|
||||
|
||||
try:
|
||||
context.scene.BIMProjectProperties.library_elements[self.prop_index].is_appended = True
|
||||
props = tool.Project.get_project_props()
|
||||
props.library_elements[self.prop_index].is_appended = True
|
||||
except:
|
||||
# TODO Remove this terrible code when I refactor this into the core
|
||||
pass
|
||||
@@ -599,7 +603,7 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def import_material_from_ifc(self, element: ifcopenshell.entity_instance, context: bpy.types.Context) -> None:
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
||||
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
||||
@@ -617,7 +621,7 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_importer.create_style(style)
|
||||
|
||||
def import_product_from_ifc(self, element: ifcopenshell.entity_instance, context: bpy.types.Context) -> None:
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
||||
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
||||
@@ -630,7 +634,7 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_importer.place_objects_in_collections()
|
||||
|
||||
def import_type_from_ifc(self, element: ifcopenshell.entity_instance, context: bpy.types.Context) -> None:
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
||||
|
||||
@@ -645,7 +649,7 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def import_materials(self, element: ifcopenshell.entity_instance, ifc_importer: import_ifc.IfcImporter) -> None:
|
||||
for material in ifcopenshell.util.element.get_materials(element):
|
||||
if IfcStore.get_element(material.id()):
|
||||
if tool.Ifc.get_object_by_identifier(material.id()):
|
||||
continue
|
||||
self.import_material_styles(material, ifc_importer)
|
||||
|
||||
@@ -659,7 +663,7 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not element.is_a("IfcRepresentationItem") or not element.StyledByItem:
|
||||
continue
|
||||
for element2 in self.file.traverse(element.StyledByItem[0]):
|
||||
if element2.is_a("IfcSurfaceStyle") and not IfcStore.get_element(element2.id()):
|
||||
if element2.is_a("IfcSurfaceStyle") and not tool.Ifc.get_object_by_identifier(element2.id()):
|
||||
ifc_importer.create_style(element2)
|
||||
|
||||
def import_material_styles(
|
||||
@@ -670,7 +674,7 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not material.HasRepresentation:
|
||||
return
|
||||
for element in self.file.traverse(material.HasRepresentation[0]):
|
||||
if element.is_a("IfcSurfaceStyle") and not IfcStore.get_element(element.id()):
|
||||
if element.is_a("IfcSurfaceStyle") and not tool.Ifc.get_object_by_identifier(element.id()):
|
||||
ifc_importer.create_style(element)
|
||||
|
||||
|
||||
@@ -767,14 +771,14 @@ class EnableEditingHeader(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMProjectProperties
|
||||
self.file = tool.Ifc.get()
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_editing = True
|
||||
|
||||
mvd = "".join(IfcStore.get_file().wrapped_data.header.file_description.description)
|
||||
mvd = "".join(tool.Ifc.get().wrapped_data.header.file_description.description)
|
||||
if "[" in mvd:
|
||||
props.mvd = mvd.split("[")[1][0:-1]
|
||||
else:
|
||||
@@ -804,7 +808,7 @@ class EditHeader(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
return tool.Ifc.get()
|
||||
|
||||
def execute(self, context):
|
||||
IfcStore.begin_transaction(self)
|
||||
@@ -817,8 +821,8 @@ class EditHeader(bpy.types.Operator):
|
||||
return result
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMProjectProperties
|
||||
self.file = tool.Ifc.get()
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_editing = True
|
||||
|
||||
self.file.wrapped_data.header.file_description.description = (f"ViewDefinition[{props.mvd}]",)
|
||||
@@ -829,7 +833,7 @@ class EditHeader(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def record_state(self):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
return {
|
||||
"description": self.file.wrapped_data.header.file_description.description,
|
||||
"author": self.file.wrapped_data.header.file_name.author,
|
||||
@@ -838,14 +842,14 @@ class EditHeader(bpy.types.Operator):
|
||||
}
|
||||
|
||||
def rollback(self, data):
|
||||
file = IfcStore.get_file()
|
||||
file = tool.Ifc.get()
|
||||
file.wrapped_data.header.file_description.description = data["old"]["description"]
|
||||
file.wrapped_data.header.file_name.author = data["old"]["author"]
|
||||
file.wrapped_data.header.file_name.organization = data["old"]["organisation"]
|
||||
file.wrapped_data.header.file_name.authorization = data["old"]["authorisation"]
|
||||
|
||||
def commit(self, data):
|
||||
file = IfcStore.get_file()
|
||||
file = tool.Ifc.get()
|
||||
file.wrapped_data.header.file_description.description = data["new"]["description"]
|
||||
file.wrapped_data.header.file_name.author = data["new"]["author"]
|
||||
file.wrapped_data.header.file_name.organization = data["new"]["organisation"]
|
||||
@@ -859,7 +863,8 @@ class DisableEditingHeader(bpy.types.Operator):
|
||||
bl_description = "Cancel unsaved header information"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMProjectProperties.is_editing = False
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_editing = False
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -975,16 +980,18 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
||||
if not self.is_advanced and not self.should_start_fresh_session:
|
||||
bpy.ops.bim.convert_to_blender()
|
||||
|
||||
context.scene.BIMProperties.ifc_file = filepath
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
bim_props.ifc_file = filepath
|
||||
if not tool.Ifc.get():
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
f"Error loading IFC file from filepath '{filepath}'. See logs above in the system console for the details.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
context.scene.BIMProjectProperties.is_loading = True
|
||||
context.scene.BIMProjectProperties.total_elements = len(tool.Ifc.get().by_type("IfcElement"))
|
||||
context.scene.BIMProjectProperties.use_relative_project_path = self.use_relative_path
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_loading = True
|
||||
props.total_elements = len(tool.Ifc.get().by_type("IfcElement"))
|
||||
props.use_relative_project_path = self.use_relative_path
|
||||
tool.Blender.register_toolbar()
|
||||
tool.Project.add_recent_ifc_project(self.get_filepath_abs())
|
||||
|
||||
@@ -1034,13 +1041,15 @@ class RevertProject(bpy.types.Operator, IFCFileSelector):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.scene.BIMProperties.ifc_file:
|
||||
props = tool.Blender.get_bim_props()
|
||||
if not props.ifc_file:
|
||||
cls.poll_message_set("IFC project need to be loaded and saved on the disk.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.bim.load_project(should_start_fresh_session=True, filepath=context.scene.BIMProperties.ifc_file)
|
||||
props = tool.Blender.get_bim_props()
|
||||
bpy.ops.bim.load_project(should_start_fresh_session=True, filepath=props.ifc_file)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1050,20 +1059,23 @@ class LoadProjectElements(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMProjectProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.props = tool.Project.get_project_props()
|
||||
self.file = tool.Ifc.get()
|
||||
bonsai.bim.schema.reload(self.file.schema_identifier)
|
||||
start = time.time()
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
path_log = tool.Blender.get_data_dir_path("process.log")
|
||||
if not os.access(path_log.parent, os.W_OK):
|
||||
path_log = os.path.join(tempfile.mkdtemp(), "process.log")
|
||||
path_log = os.path.join(
|
||||
tempfile.mkdtemp(dir=tool.Blender.get_addon_preferences().tmp_dir or None), "process.log"
|
||||
)
|
||||
logging.basicConfig(
|
||||
filename=path_log,
|
||||
filemode="a",
|
||||
level=logging.DEBUG,
|
||||
)
|
||||
settings = import_ifc.IfcImportSettings.factory(context, context.scene.BIMProperties.ifc_file, logger)
|
||||
props = tool.Blender.get_bim_props()
|
||||
settings = import_ifc.IfcImportSettings.factory(context, props.ifc_file, logger)
|
||||
settings.has_filter = self.props.filter_mode != "NONE"
|
||||
settings.should_filter_spatial_elements = self.props.should_filter_spatial_elements
|
||||
if self.props.filter_mode == "DECOMPOSITION":
|
||||
@@ -1081,7 +1093,8 @@ class LoadProjectElements(bpy.types.Operator):
|
||||
ifc_importer.execute()
|
||||
settings.logger.info("Import finished in {:.2f} seconds".format(time.time() - start))
|
||||
print("Import finished in {:.2f} seconds".format(time.time() - start))
|
||||
context.scene.BIMProjectProperties.is_loading = False
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_loading = False
|
||||
|
||||
tool.Project.load_pset_templates()
|
||||
tool.Project.load_default_thumbnails()
|
||||
@@ -1153,7 +1166,8 @@ class ToggleFilterCategories(bpy.types.Operator):
|
||||
should_select: bpy.props.BoolProperty(name="Should Select", default=True)
|
||||
|
||||
def execute(self, context):
|
||||
for filter_category in context.scene.BIMProjectProperties.filter_categories:
|
||||
props = tool.Project.get_project_props()
|
||||
for filter_category in props.filter_categories:
|
||||
filter_category.is_selected = self.should_select
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1180,7 +1194,7 @@ class LinkIfc(bpy.types.Operator):
|
||||
directory: str
|
||||
|
||||
def draw(self, context):
|
||||
pprops = context.scene.BIMProjectProperties
|
||||
pprops = tool.Project.get_project_props()
|
||||
row = self.layout.row()
|
||||
row.prop(self, "use_relative_path")
|
||||
row = self.layout.row()
|
||||
@@ -1201,7 +1215,8 @@ class LinkIfc(bpy.types.Operator):
|
||||
if bpy.data.filepath and filepath.samefile(bpy.data.filepath):
|
||||
self.report({"INFO"}, "Can't link the current .blend file")
|
||||
continue
|
||||
new = context.scene.BIMProjectProperties.links.add()
|
||||
props = tool.Project.get_project_props()
|
||||
new = props.links.add()
|
||||
filepath = tool.Ifc.get_uri(filepath, use_relative_path=self.use_relative_path)
|
||||
new.name = filepath
|
||||
status = bpy.ops.bim.load_link(filepath=filepath, use_cache=self.use_cache)
|
||||
@@ -1232,9 +1247,10 @@ class UnlinkIfc(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
filepath = Path(self.filepath).as_posix()
|
||||
bpy.ops.bim.unload_link(filepath=filepath)
|
||||
index = context.scene.BIMProjectProperties.links.find(filepath)
|
||||
props = tool.Project.get_project_props()
|
||||
index = props.links.find(filepath)
|
||||
if index != -1:
|
||||
context.scene.BIMProjectProperties.links.remove(index)
|
||||
props.links.remove(index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1254,8 +1270,9 @@ class UnloadLink(bpy.types.Operator):
|
||||
if tool.Blender.ensure_blender_path_is_abs(Path(library.filepath)) == filepath:
|
||||
bpy.data.libraries.remove(library)
|
||||
|
||||
links = context.scene.BIMProjectProperties.links
|
||||
link = links.get(self.filepath)
|
||||
props = tool.Project.get_project_props()
|
||||
links = props.links
|
||||
link = links[self.filepath]
|
||||
# Let's assume that user might delete it.
|
||||
if empty_handle := link.empty_handle:
|
||||
bpy.data.objects.remove(empty_handle)
|
||||
@@ -1264,7 +1281,7 @@ class UnloadLink(bpy.types.Operator):
|
||||
if not any([l.is_loaded for l in links]):
|
||||
ProjectDecorator.uninstall()
|
||||
# we make sure we don't draw queried object from the file that was just unlinked
|
||||
elif queried_obj := context.scene.BIMProjectProperties.queried_obj:
|
||||
elif queried_obj := props.queried_obj:
|
||||
queried_filepath = Path(queried_obj["ifc_filepath"])
|
||||
if queried_filepath == filepath:
|
||||
ProjectDecorator.uninstall()
|
||||
@@ -1296,7 +1313,7 @@ class LoadLink(bpy.types.Operator):
|
||||
def link_blend(self, filepath: Path) -> None:
|
||||
with bpy.data.libraries.load(str(filepath), link=True) as (data_from, data_to):
|
||||
data_to.scenes = data_from.scenes
|
||||
link = bpy.context.scene.BIMProjectProperties.links[self.filepath]
|
||||
link = tool.Project.get_project_props().links[self.filepath]
|
||||
for scene in bpy.data.scenes:
|
||||
if not scene.library or Path(scene.library.filepath) != filepath:
|
||||
continue
|
||||
@@ -1322,13 +1339,14 @@ class LoadLink(bpy.types.Operator):
|
||||
|
||||
if not blend_filepath.exists():
|
||||
pprops = tool.Project.get_project_props()
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
|
||||
code = f"""
|
||||
import bpy
|
||||
|
||||
def run():
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
import bonsai.tool as tool
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
# Our model origin becomes their host model origin
|
||||
gprops.host_model_origin = "{gprops.model_origin}"
|
||||
gprops.host_model_origin_si = "{gprops.model_origin_si}"
|
||||
@@ -1339,7 +1357,7 @@ def run():
|
||||
gprops.blender_offset_z = "{gprops.blender_offset_z}"
|
||||
gprops.blender_x_axis_abscissa = "{gprops.blender_x_axis_abscissa}"
|
||||
gprops.blender_x_axis_ordinate = "{gprops.blender_x_axis_ordinate}"
|
||||
pprops = bpy.context.scene.BIMProjectProperties
|
||||
pprops = tool.Project.get_project_props()
|
||||
pprops.distance_limit = {pprops.distance_limit}
|
||||
pprops.false_origin_mode = "{pprops.false_origin_mode}"
|
||||
pprops.false_origin = "{pprops.false_origin}"
|
||||
@@ -1394,7 +1412,7 @@ except Exception as e:
|
||||
with open(json_filepath, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
for prop in ("model_origin", "model_origin_si", "model_project_north"):
|
||||
if (value := data.get(prop, None)) is not None:
|
||||
setattr(gprops, prop, value)
|
||||
@@ -1430,8 +1448,8 @@ class ToggleLinkSelectability(bpy.types.Operator):
|
||||
link: bpy.props.StringProperty(name="Linked IFC Filepath")
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
link = props.links.get(self.link)
|
||||
props = tool.Project.get_project_props()
|
||||
link = props.links[self.link]
|
||||
self.library_filepath = tool.Blender.ensure_blender_path_is_abs(Path(self.link).with_suffix(".ifc.cache.blend"))
|
||||
link.is_selectable = (is_selectable := not link.is_selectable)
|
||||
for collection in self.get_linked_collections():
|
||||
@@ -1457,8 +1475,8 @@ class ToggleLinkVisibility(bpy.types.Operator):
|
||||
mode: bpy.props.EnumProperty(name="Visibility Mode", items=((i, i, "") for i in ("WIREFRAME", "VISIBLE")))
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
link = props.links.get(self.link)
|
||||
props = tool.Project.get_project_props()
|
||||
link = props.links[self.link]
|
||||
self.library_filepath = tool.Blender.ensure_blender_path_is_abs(Path(self.link).with_suffix(".ifc.cache.blend"))
|
||||
if self.mode == "WIREFRAME":
|
||||
self.toggle_wireframe(link)
|
||||
@@ -1504,7 +1522,7 @@ class SelectLinkHandle(bpy.types.Operator):
|
||||
index: bpy.props.IntProperty(name="Link Index")
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
link = props.links[self.index]
|
||||
handle = link.empty_handle
|
||||
if not handle:
|
||||
@@ -1546,8 +1564,9 @@ class ExportIFC(bpy.types.Operator):
|
||||
bpy.ops.wm.save_mainfile("INVOKE_DEFAULT")
|
||||
return {"FINISHED"}
|
||||
|
||||
self.use_relative_path = context.scene.BIMProjectProperties.use_relative_project_path
|
||||
if (filepath := context.scene.BIMProperties.ifc_file) and not self.should_save_as:
|
||||
self.use_relative_path = tool.Project.get_project_props().use_relative_project_path
|
||||
props = tool.Blender.get_bim_props()
|
||||
if (filepath := props.ifc_file) and not self.should_save_as:
|
||||
self.filepath = str(tool.Blender.ensure_blender_path_is_abs(Path(filepath)))
|
||||
return self.execute(context)
|
||||
if not self.filepath:
|
||||
@@ -1561,7 +1580,7 @@ class ExportIFC(bpy.types.Operator):
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def execute(self, context):
|
||||
project_props = context.scene.BIMProjectProperties
|
||||
project_props = tool.Project.get_project_props()
|
||||
project_props.use_relative_project_path = self.use_relative_path
|
||||
if project_props.should_disable_undo_on_save:
|
||||
old_history_size = tool.Ifc.get().history_size
|
||||
@@ -1579,7 +1598,9 @@ class ExportIFC(bpy.types.Operator):
|
||||
logger = logging.getLogger("ExportIFC")
|
||||
path_log = tool.Blender.get_data_dir_path("process.log")
|
||||
if not os.access(path_log.parent, os.W_OK):
|
||||
path_log = os.path.join(tempfile.mkdtemp(), "process.log")
|
||||
path_log = os.path.join(
|
||||
tempfile.mkdtemp(dir=tool.Blender.get_addon_preferences().tmp_dir or None), "process.log"
|
||||
)
|
||||
logging.basicConfig(
|
||||
filename=path_log,
|
||||
filemode="a",
|
||||
@@ -1606,18 +1627,20 @@ class ExportIFC(bpy.types.Operator):
|
||||
print("Export finished in {:.2f} seconds".format(time.time() - start))
|
||||
# New project created in Bonsai should be in recent projects too.
|
||||
tool.Project.add_recent_ifc_project(Path(output_file))
|
||||
scene = context.scene
|
||||
if not scene.DocProperties.ifc_files:
|
||||
new = scene.DocProperties.ifc_files.add()
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not props.ifc_files:
|
||||
new = props.ifc_files.add()
|
||||
new.name = output_file
|
||||
if context.scene.BIMProjectProperties.use_relative_project_path and bpy.data.is_saved:
|
||||
props = tool.Project.get_project_props()
|
||||
if props.use_relative_project_path and bpy.data.is_saved:
|
||||
output_file = os.path.relpath(output_file, bpy.path.abspath("//"))
|
||||
if scene.BIMProperties.ifc_file != output_file and extension not in ("ifczip", "ifcjson"):
|
||||
scene.BIMProperties.ifc_file = output_file
|
||||
bim_props = tool.Blender.get_bim_props()
|
||||
if bim_props.ifc_file != output_file and extension not in ("ifczip", "ifcjson"):
|
||||
bim_props.ifc_file = output_file
|
||||
save_blend_file = bool(bpy.data.is_saved and bpy.data.is_dirty and bpy.data.filepath)
|
||||
if save_blend_file:
|
||||
bpy.ops.wm.save_mainfile(filepath=bpy.data.filepath)
|
||||
bpy.context.scene.BIMProperties.is_dirty = False
|
||||
bim_props.is_dirty = False
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
self.report(
|
||||
{"INFO"},
|
||||
@@ -1655,8 +1678,8 @@ class LoadLinkedProject(bpy.types.Operator):
|
||||
|
||||
start = time.time()
|
||||
|
||||
pprops = bpy.context.scene.BIMProjectProperties
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
pprops = tool.Project.get_project_props()
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
|
||||
self.filepath = Path(self.filepath).as_posix()
|
||||
print("Processing", self.filepath)
|
||||
@@ -1870,7 +1893,7 @@ class LoadLinkedProject(bpy.types.Operator):
|
||||
mesh = bpy.data.meshes.new("Mesh")
|
||||
|
||||
geometry = shape.geometry
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
if (
|
||||
gprops.has_blender_offset
|
||||
and geometry.verts
|
||||
@@ -1976,9 +1999,8 @@ class QueryLinkedElement(bpy.types.Operator):
|
||||
from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d
|
||||
|
||||
LinksData.linked_data = {}
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
props.queried_obj = None
|
||||
props.quried_obj_root = None
|
||||
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == "PROPERTIES":
|
||||
@@ -2114,6 +2136,7 @@ class AppendInspectedLinkedElement(AppendLibraryElement):
|
||||
def _execute(self, context):
|
||||
from bonsai.bim.module.project.data import LinksData
|
||||
|
||||
props = tool.Project.get_project_props()
|
||||
if not LinksData.linked_data:
|
||||
self.report({"INFO"}, "No linked element found.")
|
||||
return {"CANCELLED"}
|
||||
@@ -2123,7 +2146,7 @@ class AppendInspectedLinkedElement(AppendLibraryElement):
|
||||
self.report({"INFO"}, "Cannot find Global Id for element.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
queried_obj = context.scene.BIMProjectProperties.queried_obj
|
||||
queried_obj = props.queried_obj
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
linked_ifc_file: ifcopenshell.file
|
||||
@@ -2270,34 +2293,36 @@ class RefreshClippingPlanes(bpy.types.Operator):
|
||||
|
||||
def modal(self, context, event):
|
||||
should_refresh = False
|
||||
props = tool.Project.get_project_props()
|
||||
|
||||
self.clean_deleted_planes(context)
|
||||
|
||||
for clipping_plane in context.scene.BIMProjectProperties.clipping_planes:
|
||||
for clipping_plane in props.clipping_planes:
|
||||
if clipping_plane.obj and self.is_moved(clipping_plane.obj):
|
||||
should_refresh = True
|
||||
break
|
||||
|
||||
total_planes = len(context.scene.BIMProjectProperties.clipping_planes)
|
||||
total_planes = len(props.clipping_planes)
|
||||
if should_refresh or total_planes != self.total_planes:
|
||||
self.refresh_clipping_planes(context)
|
||||
for clipping_plane in context.scene.BIMProjectProperties.clipping_planes:
|
||||
for clipping_plane in props.clipping_planes:
|
||||
if clipping_plane.obj:
|
||||
tool.Geometry.record_object_position(clipping_plane.obj)
|
||||
self.total_planes = total_planes
|
||||
return {"PASS_THROUGH"}
|
||||
|
||||
def clean_deleted_planes(self, context):
|
||||
def clean_deleted_planes(self, context: bpy.types.Context) -> None:
|
||||
props = tool.Project.get_project_props()
|
||||
while True:
|
||||
for i, clipping_plane in enumerate(context.scene.BIMProjectProperties.clipping_planes):
|
||||
for i, clipping_plane in enumerate(props.clipping_planes):
|
||||
if clipping_plane.obj:
|
||||
try:
|
||||
clipping_plane.obj.name
|
||||
except:
|
||||
context.scene.BIMProjectProperties.clipping_planes.remove(i)
|
||||
props.clipping_planes.remove(i)
|
||||
break
|
||||
else:
|
||||
context.scene.BIMProjectProperties.clipping_planes.remove(i)
|
||||
props.clipping_planes.remove(i)
|
||||
break
|
||||
else:
|
||||
break
|
||||
@@ -2321,14 +2346,15 @@ class RefreshClippingPlanes(bpy.types.Operator):
|
||||
region = next(r for r in area.regions if r.type == "WINDOW")
|
||||
data = region.data
|
||||
|
||||
if not len(context.scene.BIMProjectProperties.clipping_planes):
|
||||
props = tool.Project.get_project_props()
|
||||
if not len(props.clipping_planes):
|
||||
data.use_clip_planes = False
|
||||
else:
|
||||
with bpy.context.temp_override(area=area, region=region):
|
||||
bpy.ops.view3d.clip_border()
|
||||
|
||||
clip_planes = []
|
||||
for clipping_plane in bpy.context.scene.BIMProjectProperties.clipping_planes:
|
||||
for clipping_plane in tool.Project.get_project_props().clipping_planes:
|
||||
obj = clipping_plane.obj
|
||||
if not obj:
|
||||
continue
|
||||
@@ -2367,8 +2393,8 @@ class CreateClippingPlane(bpy.types.Operator):
|
||||
from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d
|
||||
|
||||
# Clean up deleted planes
|
||||
|
||||
if len(context.scene.BIMProjectProperties.clipping_planes) > 5:
|
||||
props = tool.Project.get_project_props()
|
||||
if len(props.clipping_planes) > 5:
|
||||
self.report({"INFO"}, "Maximum of six clipping planes allowed.")
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -2408,7 +2434,7 @@ class CreateClippingPlane(bpy.types.Operator):
|
||||
|
||||
context.scene.cursor.location = location
|
||||
|
||||
new = context.scene.BIMProjectProperties.clipping_planes.add()
|
||||
new = tool.Project.get_project_props().clipping_planes.add()
|
||||
new.obj = plane_obj
|
||||
|
||||
tool.Blender.set_active_object(plane_obj)
|
||||
@@ -2439,7 +2465,7 @@ class FlipClippingPlane(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
if obj in context.scene.BIMProjectProperties.clipping_planes_objs:
|
||||
if obj in tool.Project.get_project_props().clipping_planes_objs:
|
||||
obj.rotation_euler[0] += radians(180)
|
||||
context.view_layer.update()
|
||||
return {"FINISHED"}
|
||||
@@ -2457,14 +2483,15 @@ class BIM_OT_save_clipping_planes(bpy.types.Operator):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if IfcStore.path:
|
||||
return context.scene.BIMProjectProperties.clipping_planes
|
||||
return tool.Project.get_project_props().clipping_planes
|
||||
cls.poll_message_set("Please Save The IFC File")
|
||||
|
||||
def execute(self, context):
|
||||
clipping_planes_to_serialize = defaultdict(dict)
|
||||
clipping_planes = context.scene.BIMProjectProperties.clipping_planes
|
||||
clipping_planes = tool.Project.get_project_props().clipping_planes
|
||||
for clipping_plane in clipping_planes:
|
||||
obj = clipping_plane.obj
|
||||
assert obj
|
||||
name = obj.name
|
||||
clipping_planes_to_serialize[name]["location"] = obj.location[0:3]
|
||||
clipping_planes_to_serialize[name]["rotation"] = obj.rotation_euler[0:3]
|
||||
@@ -2490,13 +2517,14 @@ class BIM_OT_load_clipping_planes(bpy.types.Operator):
|
||||
cls.poll_message_set("Please Save The IFC File")
|
||||
|
||||
def execute(self, context):
|
||||
bpy.data.batch_remove(context.scene.BIMProjectProperties.clipping_planes_objs)
|
||||
context.scene.BIMProjectProperties.clipping_planes.clear()
|
||||
props = tool.Project.get_project_props()
|
||||
bpy.data.batch_remove(props.clipping_planes_objs)
|
||||
props.clipping_planes.clear()
|
||||
with open(Path(IfcStore.path).with_name(CLIPPING_PLANES_FILE_NAME), "r") as file:
|
||||
clipping_planes_dict = json.load(file)
|
||||
for name, values in clipping_planes_dict.items():
|
||||
bpy.ops.bim.create_clipping_plane()
|
||||
obj = context.scene.BIMProjectProperties.clipping_planes_objs[-1]
|
||||
obj = props.clipping_planes_objs[-1]
|
||||
obj.name = name
|
||||
obj.location = values["location"]
|
||||
obj.rotation_euler = values["rotation"]
|
||||
|
||||
@@ -121,7 +121,7 @@ def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context)
|
||||
self.filter_categories.clear()
|
||||
if self.filter_mode == "NONE":
|
||||
return
|
||||
file = IfcStore.get_file()
|
||||
file = tool.Ifc.get()
|
||||
if self.filter_mode == "DECOMPOSITION":
|
||||
if file.schema == "IFC2X3":
|
||||
elements = file.by_type("IfcSpatialStructureElement")
|
||||
@@ -180,6 +180,8 @@ class LibraryElement(PropertyGroup):
|
||||
element_type: EnumProperty(items=[(i, i, "") for i in get_args(LibraryElementType)], name="Element Type")
|
||||
# Asset group.
|
||||
asset_count: IntProperty(name="Asset Count")
|
||||
# Asset library.
|
||||
has_sublibraries: BoolProperty(name="Has Sublibraries", default=False)
|
||||
# Asset.
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
is_declared: BoolProperty(name="Is Declared", default=False)
|
||||
@@ -194,6 +196,7 @@ class LibraryElement(PropertyGroup):
|
||||
name: str
|
||||
element_type: LibraryElementType
|
||||
asset_count: int
|
||||
has_sublibraries: bool
|
||||
ifc_definition_id: int
|
||||
is_declared: bool
|
||||
is_appended: bool
|
||||
@@ -299,7 +302,14 @@ class BIMProjectProperties(PropertyGroup):
|
||||
should_merge_materials_by_colour: BoolProperty(name="Merge Materials by Colour", default=False)
|
||||
should_stream: BoolProperty(name="Stream Data From IFC-SPF (Only for advanced users)", default=False)
|
||||
should_load_geometry: BoolProperty(name="Load Geometry", default=True)
|
||||
should_clean_mesh: BoolProperty(name="Clean Meshes", default=False)
|
||||
should_clean_mesh: BoolProperty(
|
||||
name="Clean Meshes",
|
||||
description=(
|
||||
"Convert all triangles to quads for meshes. "
|
||||
"By default Bonsai is importing meshes triangulated (even if they are not stored as triangulated in IFC)."
|
||||
),
|
||||
default=False,
|
||||
)
|
||||
should_cache: BoolProperty(name="Cache", default=False)
|
||||
deflection_tolerance: FloatProperty(name="Deflection Tolerance", default=0.001)
|
||||
angular_tolerance: FloatProperty(name="Angular Tolerance", default=0.5)
|
||||
@@ -395,12 +405,15 @@ class BIMProjectProperties(PropertyGroup):
|
||||
def clipping_planes_objs(self) -> list[bpy.types.Object]:
|
||||
return list({cp.obj for cp in self.clipping_planes if cp.obj})
|
||||
|
||||
def add_library_project_library(self, name: str, asset_count: int, ifc_definition_id: int) -> LibraryElement:
|
||||
def add_library_project_library(
|
||||
self, name: str, asset_count: int, ifc_definition_id: int, has_sublibraries: bool
|
||||
) -> LibraryElement:
|
||||
new = self.library_elements.add()
|
||||
new["name"] = name
|
||||
new.asset_count = asset_count
|
||||
new.element_type = "LIBRARY"
|
||||
new.ifc_definition_id = ifc_definition_id
|
||||
new.has_sublibraries = has_sublibraries
|
||||
return new
|
||||
|
||||
def add_library_asset_class(self, name: str, asset_count: int) -> LibraryElement:
|
||||
|
||||
@@ -28,7 +28,7 @@ from bonsai.bim.module.project.data import ProjectData, LinksData
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.project.prop import LibraryElement, BIMProjectProperties
|
||||
from bonsai.bim.module.project.prop import LibraryElement, BIMProjectProperties, FilterCategory, Link
|
||||
|
||||
|
||||
def file_import_menu(self, context):
|
||||
@@ -151,9 +151,9 @@ class BIM_PT_project(Panel):
|
||||
|
||||
self.layout.use_property_decorate = False
|
||||
self.layout.use_property_split = True
|
||||
props = context.scene.BIMProperties
|
||||
props = tool.Blender.get_bim_props()
|
||||
pprops = self.props = tool.Project.get_project_props()
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
if pprops.is_loading:
|
||||
self.draw_advanced_loading_ui(context)
|
||||
elif self.file or props.ifc_file:
|
||||
@@ -247,7 +247,7 @@ class BIM_PT_project(Panel):
|
||||
|
||||
def draw_editing_buttons(self, context, row):
|
||||
pprops = self.props
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
if pprops.is_editing:
|
||||
row.operator("bim.edit_header", icon="CHECKMARK", text="")
|
||||
row.operator("bim.disable_editing_header", icon="CANCEL", text="")
|
||||
@@ -257,10 +257,10 @@ class BIM_PT_project(Panel):
|
||||
def draw_editable_file_info(self, context):
|
||||
pprops = self.props
|
||||
|
||||
if IfcStore.get_file():
|
||||
if tool.Ifc.get():
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="IFC Schema", icon="FILE_CACHE")
|
||||
row.label(text=IfcStore.get_file().schema)
|
||||
row.label(text=tool.Ifc.get().schema)
|
||||
|
||||
if pprops.is_editing:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -281,7 +281,7 @@ class BIM_PT_project(Panel):
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="IFC MVD", icon="FILE_HIDDEN")
|
||||
mvd = "".join(IfcStore.get_file().wrapped_data.header.file_description.description)
|
||||
mvd = "".join(tool.Ifc.get().wrapped_data.header.file_description.description)
|
||||
if "[" in mvd:
|
||||
mvd = mvd.split("[")[1][0:-1]
|
||||
row.label(text=mvd)
|
||||
@@ -305,7 +305,7 @@ class BIM_PT_project(Panel):
|
||||
|
||||
def draw_loaded_project_ui(self, context):
|
||||
# file name row
|
||||
props = context.scene.BIMProperties
|
||||
props = tool.Blender.get_bim_props()
|
||||
file_name_row = self.layout.row(align=True)
|
||||
file_name_row.label(text=os.path.basename(props.ifc_file), icon="FILE")
|
||||
self.draw_editing_buttons(context, file_name_row)
|
||||
@@ -315,7 +315,7 @@ class BIM_PT_project(Panel):
|
||||
|
||||
# file path row and actions section
|
||||
row = self.layout.row(align=True)
|
||||
if context.scene.BIMProperties.is_dirty:
|
||||
if props.is_dirty:
|
||||
row.label(text="Saved*", icon="EXPORT")
|
||||
else:
|
||||
row.label(text="Saved", icon="EXPORT")
|
||||
@@ -339,7 +339,7 @@ class BIM_PT_new_project_wizard(Panel):
|
||||
self.layout.use_property_decorate = False
|
||||
self.layout.use_property_split = True
|
||||
|
||||
props = context.scene.BIMProperties
|
||||
props = tool.Blender.get_bim_props()
|
||||
pprops = tool.Project.get_project_props()
|
||||
prop_with_search(self.layout, pprops, "export_schema")
|
||||
row = self.layout.row()
|
||||
@@ -512,7 +512,7 @@ class BIM_UL_library(UIList):
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
if item.element_type != "ASSET" and item.asset_count > 0:
|
||||
if item.element_type != "ASSET" and (item.asset_count > 0 or item.has_sublibraries):
|
||||
op = row.operator("bim.change_library_element", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
|
||||
op.element_name = item.name
|
||||
op.breadcrumb_type = item.element_type
|
||||
@@ -542,7 +542,16 @@ class BIM_UL_library(UIList):
|
||||
|
||||
|
||||
class BIM_UL_filter_categories(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMProjectProperties,
|
||||
item: FilterCategory,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"{item.name} ({item.total_elements})")
|
||||
@@ -556,7 +565,17 @@ class BIM_UL_filter_categories(UIList):
|
||||
|
||||
|
||||
class BIM_UL_links(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMProjectProperties,
|
||||
item: Link,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
index,
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
if item.is_loaded:
|
||||
|
||||
@@ -94,7 +94,7 @@ class EditPset(bpy.types.Operator, tool.Ifc.Operator):
|
||||
properties: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
props = tool.Pset.get_pset_props(self.obj, self.obj_type)
|
||||
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(self.obj, self.obj_type, context)
|
||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
@@ -226,7 +226,7 @@ class AddQto(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
qto_name = tool.Pset.get_pset_name(self.obj, self.obj_type, pset_type="QTO")
|
||||
bpy.ops.bim.enable_pset_editing(
|
||||
pset_id=0, pset_name=qto_name, pset_type="QTO", obj=self.obj, obj_type=self.obj_type
|
||||
@@ -314,7 +314,7 @@ class BIM_OT_rename_parameters(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props_to_map = context.scene.RenameProperties
|
||||
ifc_file = IfcStore.get_file()
|
||||
ifc_file = tool.Ifc.get()
|
||||
all_ifc_elements = ifc_file.by_type("IfcElement")
|
||||
|
||||
for ifc_element in all_ifc_elements:
|
||||
@@ -347,7 +347,7 @@ class BIM_OT_add_edit_custom_property(bpy.types.Operator, tool.Ifc.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
props = context.scene.AddEditProperties
|
||||
|
||||
for obj in tool.Blender.get_selected_objects():
|
||||
@@ -402,7 +402,7 @@ class BIM_OT_bulk_remove_psets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.file = tool.Ifc.get()
|
||||
props = context.scene.DeletePsets
|
||||
|
||||
for obj in tool.Blender.get_selected_objects():
|
||||
|
||||
@@ -26,7 +26,6 @@ import bonsai.tool as tool
|
||||
from bonsai.bim.prop import Attribute, StrProperty
|
||||
from bonsai.bim.module.pset.data import AddEditCustomPropertiesData, ObjectPsetsData, MaterialPsetsData
|
||||
from bonsai.bim.module.material.data import ObjectMaterialData
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -135,7 +134,7 @@ def get_resource_pset_names(self, context):
|
||||
global psetnames
|
||||
rprops = context.scene.BIMResourceProperties
|
||||
rtprops = context.scene.BIMResourceTreeProperties
|
||||
ifc_class = IfcStore.get_file().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
||||
ifc_class = tool.Ifc.get().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
||||
if ifc_class not in psetnames:
|
||||
psets = bonsai.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True, schema=tool.Ifc.get_schema())
|
||||
psetnames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||
@@ -146,7 +145,7 @@ def get_resource_qto_names(self, context):
|
||||
global qtonames
|
||||
rprops = context.scene.BIMResourceProperties
|
||||
rtprops = context.scene.BIMResourceTreeProperties
|
||||
ifc_class = IfcStore.get_file().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
||||
ifc_class = tool.Ifc.get().by_id(rtprops.resources[rprops.active_resource_index].ifc_definition_id).is_a()
|
||||
if ifc_class not in qtonames:
|
||||
psets = bonsai.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True, schema=tool.Ifc.get_schema())
|
||||
qtonames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||
@@ -174,7 +173,7 @@ def get_group_qto_names(self, context):
|
||||
def get_profile_pset_names(self, context):
|
||||
global psetnames
|
||||
pprops = tool.Profile.get_profile_props()
|
||||
ifc_class = IfcStore.get_file().by_id(pprops.profiles[pprops.active_profile_index].ifc_definition_id).is_a()
|
||||
ifc_class = tool.Ifc.get().by_id(pprops.profiles[pprops.active_profile_index].ifc_definition_id).is_a()
|
||||
if ifc_class not in psetnames:
|
||||
psets = bonsai.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True, schema=tool.Ifc.get_schema())
|
||||
psetnames[ifc_class] = blender_formatted_enum_from_psets(psets)
|
||||
@@ -290,7 +289,8 @@ class AddEditProperties(PropertyGroup):
|
||||
enum_values: CollectionProperty(name="Enum Values", type=Attribute)
|
||||
|
||||
def get_value_name(self) -> Union[Literal["string_value", "bool_value", "int_value", "float_value"], None]:
|
||||
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
|
||||
schema = tool.Ifc.schema()
|
||||
ifc_data_type = schema.declaration_by_name(self.primary_measure_type)
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(ifc_data_type)
|
||||
if data_type == "string":
|
||||
return "string_value"
|
||||
|
||||
@@ -20,7 +20,6 @@ from __future__ import annotations
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.helper import prop_with_search, get_display_value
|
||||
from bonsai.bim.module.pset.data import (
|
||||
ObjectPsetsData,
|
||||
@@ -247,7 +246,7 @@ class BIM_PT_object_psets(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -325,7 +324,7 @@ class BIM_PT_object_qtos(Panel):
|
||||
props = context.active_object.BIMObjectProperties
|
||||
if not props.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(props.ifc_definition_id):
|
||||
if not tool.Ifc.get_object_by_identifier(props.ifc_definition_id):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.attribute
|
||||
from ifcopenshell.util.doc import get_attribute_doc
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.pset_template.data import PsetTemplatesData
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
@@ -183,7 +184,8 @@ class PropTemplate(PropertyGroup):
|
||||
def get_value_name(self) -> str:
|
||||
if self.primary_measure_type == "-":
|
||||
return "string_value"
|
||||
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
|
||||
schema = tool.Ifc.schema()
|
||||
ifc_data_type = schema.declaration_by_name(self.primary_measure_type)
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(ifc_data_type)
|
||||
if data_type == "string":
|
||||
return "string_value"
|
||||
|
||||
@@ -451,7 +451,8 @@ def get_side_area(o: bpy.types.Object) -> float:
|
||||
|
||||
|
||||
def get_cross_section_area(obj: bpy.types.Object) -> float:
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
assert representation
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
|
||||
@@ -21,7 +21,6 @@ import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.qto as core
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.qto import helper
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
import bpy
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.resource
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.module.pset.data
|
||||
import bonsai.bim.module.resource.data
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.helper
|
||||
from bpy.types import Panel, UIList
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from bonsai.bim.module.resource.data import ResourceData
|
||||
from typing import Any
|
||||
|
||||
@@ -35,7 +35,7 @@ class BIM_PT_resources(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
file = IfcStore.get_file()
|
||||
file = tool.Ifc.get()
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
@@ -54,32 +54,14 @@ class IfcClassData:
|
||||
|
||||
@classmethod
|
||||
def ifc_products(cls):
|
||||
products = [
|
||||
"IfcElementType",
|
||||
"IfcElement",
|
||||
"IfcFeatureElement",
|
||||
"IfcSpatialElement",
|
||||
"IfcSpatialElementType",
|
||||
"IfcStructuralItem",
|
||||
"IfcAnnotation",
|
||||
"IfcRelSpaceBoundary",
|
||||
]
|
||||
products = tool.Root.get_ifc_products()
|
||||
version = tool.Ifc.get_schema()
|
||||
if version == "IFC2X3":
|
||||
products = [
|
||||
"IfcElementType",
|
||||
"IfcElement",
|
||||
"IfcFeatureElement",
|
||||
"IfcSpatialStructureElement",
|
||||
"IfcStructuralItem",
|
||||
"IfcAnnotation",
|
||||
"IfcRelSpaceBoundary",
|
||||
]
|
||||
return [(e, e, (get_entity_doc(version, e) or {}).get("description", "")) for e in products]
|
||||
|
||||
@classmethod
|
||||
def ifc_classes(cls):
|
||||
ifc_product = bpy.context.scene.BIMRootProperties.ifc_product
|
||||
rprops = tool.Root.get_root_props()
|
||||
ifc_product = rprops.ifc_product
|
||||
declaration = tool.Ifc.schema().declaration_by_name(ifc_product)
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||
names = [d.name() for d in declarations]
|
||||
@@ -99,7 +81,8 @@ class IfcClassData:
|
||||
@classmethod
|
||||
def ifc_predefined_types(cls):
|
||||
types_enum = []
|
||||
ifc_class = bpy.context.scene.BIMRootProperties.ifc_class
|
||||
rprops = tool.Root.get_root_props()
|
||||
ifc_class = rprops.ifc_class
|
||||
declaration = tool.Ifc.schema().declaration_by_name(ifc_class)
|
||||
version = tool.Ifc.get_schema()
|
||||
for attribute in declaration.attributes():
|
||||
@@ -133,7 +116,8 @@ class IfcClassData:
|
||||
|
||||
@classmethod
|
||||
def representation_template(cls):
|
||||
ifc_class = bpy.context.scene.BIMRootProperties.ifc_class
|
||||
rprops = tool.Root.get_root_props()
|
||||
ifc_class = rprops.ifc_class
|
||||
templates = [
|
||||
("EMPTY", "No Geometry", "Start with an empty object"),
|
||||
None,
|
||||
|
||||
@@ -42,32 +42,33 @@ class EnableReassignClass(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
rprops = tool.Root.get_root_props()
|
||||
obj = context.active_object
|
||||
self.file = tool.Ifc.get()
|
||||
ifc_class = obj.name.split("/")[0]
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
ifc_class = element.is_a()
|
||||
context.active_object.BIMObjectProperties.is_reassigning_class = True
|
||||
ifc_products = [
|
||||
"IfcElement",
|
||||
"IfcElementType",
|
||||
"IfcSpatialElement",
|
||||
"IfcGroup",
|
||||
"IfcStructural",
|
||||
"IfcPositioningElement",
|
||||
"IfcContext",
|
||||
"IfcAnnotation",
|
||||
"IfcRelSpaceBoundary",
|
||||
]
|
||||
ifc_products = tool.Root.get_ifc_products()
|
||||
schema = tool.Ifc.schema()
|
||||
declaration = schema.declaration_by_name(ifc_class)
|
||||
for ifc_product in ifc_products:
|
||||
if ifcopenshell.util.schema.is_a(IfcStore.get_schema().declaration_by_name(ifc_class), ifc_product):
|
||||
context.scene.BIMRootProperties.ifc_product = ifc_product
|
||||
if ifcopenshell.util.schema.is_a(declaration, ifc_product):
|
||||
rprops.ifc_product = ifc_product
|
||||
break
|
||||
else:
|
||||
self.report({"ERROR"}, f"Couldn't find matching IFC product for the selected object: '{element}'.")
|
||||
obj.BIMObjectProperties.is_reassigning_class = False
|
||||
return {"CANCELLED"}
|
||||
|
||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
context.scene.BIMRootProperties.ifc_class = element.is_a()
|
||||
context.scene.BIMRootProperties.relating_class_object = None
|
||||
rprops.ifc_class = element.is_a()
|
||||
rprops.relating_class_object = None
|
||||
if hasattr(element, "PredefinedType"):
|
||||
if element.PredefinedType:
|
||||
context.scene.BIMRootProperties.ifc_predefined_type = element.PredefinedType
|
||||
rprops.ifc_predefined_type = element.PredefinedType
|
||||
userdefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
context.scene.BIMRootProperties.ifc_userdefined_type = userdefined_type or ""
|
||||
rprops.ifc_userdefined_type = userdefined_type or ""
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -94,9 +95,9 @@ class ReassignClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
else:
|
||||
objects = set(context.selected_objects + [context.active_object])
|
||||
self.file = tool.Ifc.get()
|
||||
root_props = context.scene.BIMRootProperties
|
||||
ifc_product: str = root_props.ifc_product
|
||||
ifc_class: str = root_props.ifc_class
|
||||
root_props = tool.Root.get_root_props()
|
||||
ifc_product = root_props.ifc_product
|
||||
ifc_class = root_props.ifc_class
|
||||
type_ifc_class = next(iter(ifcopenshell.util.type.get_applicable_types(ifc_class, self.file.schema)), None)
|
||||
|
||||
predefined_type = root_props.ifc_predefined_type
|
||||
@@ -176,7 +177,7 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_representation_class: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMRootProperties
|
||||
props = tool.Root.get_root_props()
|
||||
objects: list[bpy.types.Object] = []
|
||||
if self.obj:
|
||||
objects = [bpy.data.objects[self.obj]]
|
||||
@@ -358,7 +359,7 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
|
||||
|
||||
def _invoke(self, context, event):
|
||||
props = context.scene.BIMRootProperties
|
||||
props = tool.Root.get_root_props()
|
||||
# For convenience, preselect OBJs if applicable
|
||||
if props.ifc_product == "IfcFeatureElement":
|
||||
if (obj := tool.Blender.get_active_object(is_selected=True)) and obj.type == "MESH":
|
||||
@@ -376,7 +377,7 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMRootProperties
|
||||
props = tool.Root.get_root_props()
|
||||
predefined_type = (
|
||||
props.ifc_userdefined_type if props.ifc_predefined_type == "USERDEFINED" else props.ifc_predefined_type
|
||||
)
|
||||
@@ -500,7 +501,7 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
else:
|
||||
material = ifcopenshell.api.run("material.add_material", tool.Ifc.get(), name="Unknown")
|
||||
if representation_template == "PROFILESET":
|
||||
profile_id = tool.Blender.get_enum_safe(context.scene.BIMRootProperties, "profile")
|
||||
profile_id = tool.Blender.get_enum_safe(props, "profile")
|
||||
if profile_id in ("-", None):
|
||||
profile = next((p for p in ifc_file.by_type("IfcProfileDef") if p.ProfileName), None)
|
||||
if profile is None:
|
||||
@@ -589,7 +590,7 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Blender.set_active_object(obj)
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMRootProperties
|
||||
props = tool.Root.get_root_props()
|
||||
self.layout.use_property_split = True
|
||||
self.layout.use_property_decorate = False
|
||||
row = self.layout.row()
|
||||
@@ -600,7 +601,7 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not self.ifc_product:
|
||||
prop_with_search(self.layout, props, "ifc_product", text="Definition", should_click_ok=True)
|
||||
prop_with_search(self.layout, props, "ifc_class", should_click_ok=True)
|
||||
ifc_predefined_types = root_prop.get_ifc_predefined_types(context.scene.BIMRootProperties, context)
|
||||
ifc_predefined_types = root_prop.get_ifc_predefined_types(props, context)
|
||||
if ifc_predefined_types:
|
||||
prop_with_search(self.layout, props, "ifc_predefined_type", should_click_ok=True)
|
||||
if props.ifc_predefined_type == "USERDEFINED":
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user