This commit is contained in:
admin
2021-05-10 14:06:18 +08:00
parent db5432d902
commit 58e9f1baf9
13 changed files with 593 additions and 489 deletions
+56 -145
View File
@@ -1,153 +1,64 @@
import bpy
import json
import math import math
import ifcopenshell
import ifcopenshell.util.attribute
from mathutils import geometry from mathutils import geometry
from mathutils import Vector from mathutils import Vector
import bpy from blenderbim.bim.ifc import IfcStore
# TODO: Deprecate this in favour of ifcopenshell.util.unit def import_attributes(ifc_class, props, data, callback=None):
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or (isinstance(data_type, tuple) and "entity" in ".".join(data_type)):
continue
new = props.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type if isinstance(data_type, str) else ""
is_handled_by_callback = callback(attribute.name(), new, data) if callback else False
if is_handled_by_callback:
pass # Our job is done
elif is_handled_by_callback is False:
props.remove(len(props) - 1)
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class SIUnitHelper: def export_attributes(props, callback=None):
prefixes = { attributes = {}
"EXA": 1e18, for attribute in props:
"PETA": 1e15, is_handled_by_callback = callback(attributes, attribute) if callback else False
"TERA": 1e12, if attribute.is_null:
"GIGA": 1e9, attributes[attribute.name] = None
"MEGA": 1e6, elif is_handled_by_callback:
"KILO": 1e3, pass # Our job is done
"HECTO": 1e2, elif attribute.data_type == "string":
"DECA": 1e1, attributes[attribute.name] = attribute.string_value
"DECI": 1e-1, elif attribute.data_type == "boolean":
"CENTI": 1e-2, attributes[attribute.name] = attribute.bool_value
"MILLI": 1e-3, elif attribute.data_type == "integer":
"MICRO": 1e-6, attributes[attribute.name] = attribute.int_value
"NANO": 1e-9, elif attribute.data_type == "float":
"PICO": 1e-12, attributes[attribute.name] = attribute.float_value
"FEMTO": 1e-15, elif attribute.data_type == "enum":
"ATTO": 1e-18, attributes[attribute.name] = attribute.enum_value
} return attributes
unit_names = [
"AMPERE",
"BECQUEREL",
"CANDELA",
"COULOMB",
"CUBIC_METRE",
"DEGREE CELSIUS",
"FARAD",
"GRAM",
"GRAY",
"HENRY",
"HERTZ",
"JOULE",
"KELVIN",
"LUMEN",
"LUX",
"MOLE",
"NEWTON",
"OHM",
"PASCAL",
"RADIAN",
"SECOND",
"SIEMENS",
"SIEVERT",
"SQUARE METRE",
"METRE",
"STERADIAN",
"TESLA",
"VOLT",
"WATT",
"WEBER",
]
si_conversions = {
"inch": 0.0254,
"foot": 0.3048,
"yard": 0.914,
"mile": 1609,
"square inch": 0.0006452,
"square foot": 0.09290304,
"square yard": 0.83612736,
"acre": 4046.86,
"square mile": 2588881,
"cubic inch": 0.00001639,
"cubic foot": 0.02831684671168849,
"cubic yard": 0.7636,
"litre": 0.001,
"fluid ounce UK": 0.0000284130625,
"fluid ounce US": 0.00002957353,
"pint UK": 0.000568,
"pint US": 0.000473,
"gallon UK": 0.004546,
"gallon US": 0.003785,
"degree": math.pi / 180,
"ounce": 0.02835,
"pound": 0.454,
"ton UK": 1016.0469088,
"ton US": 907.18474,
"lbf": 4.4482216153,
"kip": 4448.2216153,
"psi": 6894.7572932,
"ksi": 6894757.2932,
"minute": 60,
"hour": 3600,
"day": 86400,
"btu": 1055.056,
}
@staticmethod
def get_prefix(text):
for prefix in SIUnitHelper.prefixes.keys():
if prefix in text.upper():
return prefix
@staticmethod # TODO: migrate the below helper functions into the drawing module, since it is specific to that module
def get_prefix_multiplier(text):
if not text:
return 1
prefix = SIUnitHelper.get_prefix(text)
if prefix:
return SIUnitHelper.prefixes[prefix]
return 1
@staticmethod
def get_unit_name(text):
for name in SIUnitHelper.unit_names:
if name in text.upper().replace("METER", "METRE"):
return name
@staticmethod
def convert(value, from_prefix, from_unit, to_prefix, to_unit):
"""Converts between length, area, and volume units
:param value: The numeric value you want to convert
:type value: float
:param from_prefix: A prefix from IfcSIPrefix. Can be None.
:type from_prefix: string
:param from_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
:type from_unit: string
:param to_prefix: A prefix from IfcSIPrefix. Can be None.
:type to_prefix: string
:param to_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
:type to_unit: string
"""
if from_unit in SIUnitHelper.si_conversions:
value *= SIUnitHelper.si_conversions[from_unit]
elif from_prefix:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
if "SQUARE" in from_unit:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
elif "CUBIC" in from_unit:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
if to_unit in SIUnitHelper.si_conversions:
return value * (1 / SIUnitHelper.si_conversions[to_unit])
elif to_prefix:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
if "SQUARE" in from_unit:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
elif "CUBIC" in from_unit:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
return value
# This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py # This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py
@@ -302,7 +213,7 @@ def parse_diagram_scale(camera):
def get_project_collection(scene): def get_project_collection(scene):
"""Get main project collection""" """Get main project collection"""
colls = [c for c in scene.collection.children if c.name.startswith('IfcProject')] colls = [c for c in scene.collection.children if c.name.startswith("IfcProject")]
if len(colls) != 1: if len(colls) != 1:
raise RuntimeError("project collection missing or not unique") raise RuntimeError("project collection missing or not unique")
return colls[0] return colls[0]
@@ -315,7 +226,7 @@ def get_active_drawing(scene):
return None, None return None, None
try: try:
drawing = props.drawings[props.active_drawing_index] drawing = props.drawings[props.active_drawing_index]
return scene.collection.children['Views'].children[f"IfcGroup/{drawing.name}"], drawing.camera return scene.collection.children["Views"].children[f"IfcGroup/{drawing.name}"], drawing.camera
except (KeyError, IndexError): except (KeyError, IndexError):
raise RuntimeError("missing drawing collection") raise RuntimeError("missing drawing collection")
@@ -333,8 +244,8 @@ def ortho_view_frame(camera, margin=0.015):
""" """
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
size = camera.ortho_scale size = camera.ortho_scale
hwidth = size * .5 hwidth = size * 0.5
hheight = size * .5 * aspect hheight = size * 0.5 * aspect
scale = parse_diagram_scale(camera) scale = parse_diagram_scale(camera)
xmarg = margin * scale xmarg = margin * scale
ymarg = margin * scale * aspect ymarg = margin * scale * aspect
+1 -1
View File
@@ -718,7 +718,7 @@ class IfcImporter:
# Create structural collections # Create structural collections
self.structural_member_collection = bpy.data.collections.new("Members") self.structural_member_collection = bpy.data.collections.new("Members")
self.structural_connection_collection = bpy.data.collections.new("Connections") self.structural_connection_collection = bpy.data.collections.new("Connections")
self.structural_collection = bpy.data.collections.new("StructuralEntities") self.structural_collection = bpy.data.collections.new("StructuralItems")
self.structural_collection.children.link(self.structural_member_collection) self.structural_collection.children.link(self.structural_member_collection)
self.structural_collection.children.link(self.structural_connection_collection) self.structural_collection.children.link(self.structural_connection_collection)
self.project["blender"].children.link(self.structural_collection) self.project["blender"].children.link(self.structural_collection)
@@ -122,7 +122,7 @@ class AddRepresentation(bpy.types.Operator):
[ [
bpy.ops.bim.add_style(material=s.material.name) bpy.ops.bim.add_style(material=s.material.name)
for s in obj.material_slots for s in obj.material_slots
if not s.material.BIMMaterialProperties.ifc_style_id if s.material and not s.material.BIMMaterialProperties.ifc_style_id
] ]
ifcopenshell.api.run( ifcopenshell.api.run(
@@ -76,7 +76,7 @@ class ReassignClass(bpy.types.Operator):
class AssignClass(bpy.types.Operator): class AssignClass(bpy.types.Operator):
bl_idname = "bim.assign_class" bl_idname = "bim.assign_class"
bl_label = "Assign IFC Class" bl_label = "Assign IFC Class"
bl_options = {'REGISTER', 'UNDO'} bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty() obj: bpy.props.StringProperty()
ifc_class: bpy.props.StringProperty() ifc_class: bpy.props.StringProperty()
predefined_type: bpy.props.StringProperty() predefined_type: bpy.props.StringProperty()
@@ -92,10 +92,6 @@ class AssignClass(bpy.types.Operator):
elif self.predefined_type == "": elif self.predefined_type == "":
predefined_type = None predefined_type = None
for obj in objects: for obj in objects:
if obj.data and hasattr(obj.data, "materials"):
for material in obj.data.materials:
if not material.BIMMaterialProperties.ifc_style_id:
bpy.ops.bim.add_style(material=material.name)
self.assign_class(context, obj) self.assign_class(context, obj)
return {"FINISHED"} return {"FINISHED"}
@@ -61,6 +61,7 @@ classes = (
operator.UnassignProduct, operator.UnassignProduct,
operator.GenerateGanttChart, operator.GenerateGanttChart,
operator.ImportP6, operator.ImportP6,
operator.ImportMSP,
operator.LoadTaskProperties, operator.LoadTaskProperties,
operator.SelectTaskRelatedProducts, operator.SelectTaskRelatedProducts,
operator.VisualiseWorkScheduleDate, operator.VisualiseWorkScheduleDate,
@@ -82,6 +83,7 @@ classes = (
def menu_func_import(self, context): def menu_func_import(self, context):
self.layout.operator(operator.ImportP6.bl_idname, text="P6 (.xml)") self.layout.operator(operator.ImportP6.bl_idname, text="P6 (.xml)")
self.layout.operator(operator.ImportMSP.bl_idname, text="Microsoft Project (.xml)")
def register(): def register():
@@ -1,3 +1,4 @@
import re
import os import os
import bpy import bpy
import json import json
@@ -7,6 +8,7 @@ import pystache
import webbrowser import webbrowser
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.date import ifcopenshell.util.date
import blenderbim.bim.helper
import blenderbim.bim.module.sequence.helper as helper import blenderbim.bim.module.sequence.helper as helper
from datetime import datetime from datetime import datetime
from datetime import timedelta from datetime import timedelta
@@ -32,15 +34,7 @@ class EditWorkPlan(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkPlanProperties props = context.scene.BIMWorkPlanProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.work_plan_attributes, self.export_attributes)
for attribute in props.work_plan_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_work_plan", "sequence.edit_work_plan",
@@ -51,6 +45,14 @@ class EditWorkPlan(bpy.types.Operator):
bpy.ops.bim.disable_editing_work_plan() bpy.ops.bim.disable_editing_work_plan()
return {"FINISHED"} return {"FINISHED"}
def export_attributes(self, attributes, prop):
if "Date" in prop.name or "Time" in prop.name:
attributes[prop.name] = helper.parse_datetime(prop.string_value)
return True
elif prop.name == "Duration" or prop.name == "TotalFloat":
attributes[prop.name] = helper.parse_duration(prop.string_value)
return True
class RemoveWorkPlan(bpy.types.Operator): class RemoveWorkPlan(bpy.types.Operator):
bl_idname = "bim.remove_work_plan" bl_idname = "bim.remove_work_plan"
@@ -76,27 +78,17 @@ class EnableEditingWorkPlan(bpy.types.Operator):
data = Data.work_plans[self.work_plan] data = Data.work_plans[self.work_plan]
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkPlan").all_attributes(): blenderbim.bim.helper.import_attributes("IfcWorkPlan", props.work_plan_attributes, data, self.import_attributes)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.work_plan_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if attribute.name() in ["CreationDate", "StartTime", "FinishTime"]:
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
props.active_work_plan_id = self.work_plan props.active_work_plan_id = self.work_plan
props.editing_type = "ATTRIBUTES" props.editing_type = "ATTRIBUTES"
return {"FINISHED"} return {"FINISHED"}
def import_attributes(self, name, prop, data):
if name in ["CreationDate", "StartTime", "FinishTime"]:
prop.string_value = "" if prop.is_null else data[name].isoformat()
return True
class DisableEditingWorkPlan(bpy.types.Operator): class DisableEditingWorkPlan(bpy.types.Operator):
bl_idname = "bim.disable_editing_work_plan" bl_idname = "bim.disable_editing_work_plan"
@@ -175,15 +167,7 @@ class EditWorkSchedule(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.work_schedule_attributes, self.export_attributes)
for attribute in props.work_schedule_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_work_schedule", "sequence.edit_work_schedule",
@@ -194,6 +178,14 @@ class EditWorkSchedule(bpy.types.Operator):
bpy.ops.bim.disable_editing_work_schedule() bpy.ops.bim.disable_editing_work_schedule()
return {"FINISHED"} return {"FINISHED"}
def export_attributes(self, attributes, prop):
if "Date" in prop.name or "Time" in prop.name:
attributes[prop.name] = helper.parse_datetime(prop.string_value)
return True
elif prop.name == "Duration" or prop.name == "TotalFloat":
attributes[prop.name] = helper.parse_duration(prop.string_value)
return True
class RemoveWorkSchedule(bpy.types.Operator): class RemoveWorkSchedule(bpy.types.Operator):
bl_idname = "bim.remove_work_schedule" bl_idname = "bim.remove_work_schedule"
@@ -226,23 +218,14 @@ class EnableEditingWorkSchedule(bpy.types.Operator):
def enable_editing_work_schedule(self): def enable_editing_work_schedule(self):
data = Data.work_schedules[self.work_schedule] data = Data.work_schedules[self.work_schedule]
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkSchedule").all_attributes(): blenderbim.bim.helper.import_attributes(
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) "IfcWorkSchedule", self.props.work_schedule_attributes, data, self.import_attributes
if data_type == "entity": )
continue
new = self.props.work_schedule_attributes.add() def import_attributes(self, name, prop, data):
new.name = attribute.name() if name in ["CreationDate", "StartTime", "FinishTime"]:
new.is_null = data[attribute.name()] is None prop.string_value = "" if prop.is_null else data[name].isoformat()
new.is_optional = attribute.optional() return True
new.data_type = data_type
if attribute.name() in ["CreationDate", "StartTime", "FinishTime"]:
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class EnableEditingTasks(bpy.types.Operator): class EnableEditingTasks(bpy.types.Operator):
@@ -258,10 +241,11 @@ class EnableEditingTasks(bpy.types.Operator):
self.tprops.tasks.remove(0) self.tprops.tasks.remove(0)
self.contracted_tasks = json.loads(self.props.contracted_tasks) self.contracted_tasks = json.loads(self.props.contracted_tasks)
sort_keys = { self.sort_keys = {
i: Data.tasks[i]["Identification"] for i in Data.work_schedules[self.work_schedule]["RelatedObjects"] i: Data.tasks[i]["Identification"] or "" for i in Data.work_schedules[self.work_schedule]["RelatedObjects"]
} }
for related_object_id in sorted(sort_keys, key=sort_keys.__getitem__):
for related_object_id in sorted(self.sort_keys, key=self.natural_sort_key):
self.create_new_task_li(related_object_id, 0) self.create_new_task_li(related_object_id, 0)
bpy.ops.bim.load_task_properties() bpy.ops.bim.load_task_properties()
self.props.editing_type = "TASKS" self.props.editing_type = "TASKS"
@@ -276,10 +260,13 @@ class EnableEditingTasks(bpy.types.Operator):
if task["RelatedObjects"]: if task["RelatedObjects"]:
new.has_children = True new.has_children = True
if new.is_expanded: if new.is_expanded:
sort_keys = {i: Data.tasks[i]["Identification"] for i in task["RelatedObjects"]} self.sort_keys = {i: Data.tasks[i]["Identification"] or "" for i in task["RelatedObjects"]}
for related_object_id in sorted(sort_keys, key=sort_keys.__getitem__): for related_object_id in sorted(self.sort_keys, key=self.natural_sort_key):
self.create_new_task_li(related_object_id, level_index + 1) self.create_new_task_li(related_object_id, level_index + 1)
return {"FINISHED"}
def natural_sort_key(self, i, _nsre=re.compile("([0-9]+)")):
s = self.sort_keys[i]
return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)]
class LoadTaskProperties(bpy.types.Operator): class LoadTaskProperties(bpy.types.Operator):
@@ -322,6 +309,10 @@ class LoadTaskProperties(bpy.types.Operator):
item.start = "-" item.start = "-"
item.finish = "-" item.finish = "-"
item.duration = "-" item.duration = "-"
if task["HasAssignmentsWorkCalendar"]:
item.calendar = Data.work_calendars[task["HasAssignmentsWorkCalendar"][0]]["Name"] or "Unnamed"
else:
item.calendar = ""
self.props.is_task_update_enabled = True self.props.is_task_update_enabled = True
return {"FINISHED"} return {"FINISHED"}
@@ -434,39 +425,23 @@ class EnableEditingTaskTime(bpy.types.Operator):
data = Data.task_times[task_time_id] data = Data.task_times[task_time_id]
for attribute in IfcStore.get_schema().declaration_by_name("IfcTaskTime").all_attributes(): blenderbim.bim.helper.import_attributes("IfcTaskTime", props.task_time_attributes, data, self.import_attributes)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.task_time_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
if isinstance(data[attribute.name()], datetime):
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif isinstance(data[attribute.name()], isodate.Duration):
new.string_value = (
""
if new.is_null
else ifcopenshell.util.date.datetime2ifc(data[attribute.name()], "IfcDuration")
)
else:
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
props.active_task_time_id = task_time_id props.active_task_time_id = task_time_id
props.active_task_id = self.task props.active_task_id = self.task
props.editing_task_type = "TASKTIME" props.editing_task_type = "TASKTIME"
return {"FINISHED"} return {"FINISHED"}
def import_attributes(self, name, prop, data):
if prop.data_type == "string":
if isinstance(data[name], datetime):
prop.string_value = "" if prop.is_null else data[name].isoformat()
return True
elif isinstance(data[name], isodate.Duration):
prop.string_value = (
"" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration")
)
return True
def add_task_time(self): def add_task_time(self):
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.file.by_id(self.task)) task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.file.by_id(self.task))
Data.load(IfcStore.get_file()) Data.load(IfcStore.get_file())
@@ -479,20 +454,7 @@ class EditTaskTime(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.task_time_attributes, self.export_attributes)
for attribute in props.task_time_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "boolean":
attributes[attribute.name] = attribute.bool_value
elif attribute.data_type == "float":
attributes[attribute.name] = attribute.float_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
attributes = self.convert_strings_to_date_times(attributes) attributes = self.convert_strings_to_date_times(attributes)
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
@@ -506,21 +468,13 @@ class EditTaskTime(bpy.types.Operator):
bpy.ops.bim.load_task_properties(task=props.active_task_id) bpy.ops.bim.load_task_properties(task=props.active_task_id)
return {"FINISHED"} return {"FINISHED"}
def convert_strings_to_date_times(self, attributes): def export_attributes(self, attributes, prop):
for key, value in attributes.items(): if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
if not value: attributes[prop.name] = helper.parse_datetime(value)
continue return True
if "Start" in key or "Finish" in key or key == "StatusTime": elif prop.name == "ScheduleDuration":
try: attributes[prop.name] = helper.parse_duration(value)
attributes[key] = parser.isoparse(value) return True
except:
try:
attributes[key] = parser.parse(value, dayfirst=True, fuzzy=True)
except:
attributes[key] = None
elif key == "ScheduleDuration":
attributes[key] = isodate.parse_duration(value)
return attributes
class EnableEditingTask(bpy.types.Operator): class EnableEditingTask(bpy.types.Operator):
@@ -535,25 +489,7 @@ class EnableEditingTask(bpy.types.Operator):
data = Data.tasks[self.task] data = Data.tasks[self.task]
for attribute in IfcStore.get_schema().declaration_by_name("IfcTask").all_attributes(): blenderbim.bim.helper.import_attributes("IfcTask", props.task_attributes, data)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.task_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
props.active_task_id = self.task props.active_task_id = self.task
props.editing_task_type = "ATTRIBUTES" props.editing_task_type = "ATTRIBUTES"
return {"FINISHED"} return {"FINISHED"}
@@ -575,19 +511,7 @@ class EditTask(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.export_attributes(props.task_attributes)
for attribute in props.task_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "boolean":
attributes[attribute.name] = attribute.bool_value
elif attribute.data_type == "integer":
attributes[attribute.name] = attribute.int_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_task", self.file, **{"task": self.file.by_id(props.active_task_id), "attributes": attributes} "sequence.edit_task", self.file, **{"task": self.file.by_id(props.active_task_id), "attributes": attributes}
@@ -773,15 +697,7 @@ class EditWorkCalendar(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkCalendarProperties props = context.scene.BIMWorkCalendarProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.work_calendar_attributes)
for attribute in props.work_calendar_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_work_calendar", "sequence.edit_work_calendar",
@@ -819,21 +735,7 @@ class EnableEditingWorkCalendar(bpy.types.Operator):
data = Data.work_calendars[self.work_calendar] data = Data.work_calendars[self.work_calendar]
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkCalendar").all_attributes(): blenderbim.bim.helper.import_attributes("IfcWorkCalendar", self.props.work_calendar_attributes, data)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = self.props.work_calendar_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
self.props.active_work_calendar_id = self.work_calendar self.props.active_work_calendar_id = self.work_calendar
self.props.editing_type = "ATTRIBUTES" self.props.editing_type = "ATTRIBUTES"
return {"FINISHED"} return {"FINISHED"}
@@ -869,6 +771,27 @@ class ImportP6(bpy.types.Operator, ImportHelper):
return {"FINISHED"} return {"FINISHED"}
class ImportMSP(bpy.types.Operator, ImportHelper):
bl_idname = "import_msp.bim"
bl_label = "Import MSP"
filename_ext = ".xml"
filter_glob: bpy.props.StringProperty(default="*.xml", options={"HIDDEN"})
def execute(self, context):
from ifcp6.msp2ifc import MSP2Ifc
self.file = IfcStore.get_file()
start = time.time()
msp2ifc = MSP2Ifc()
msp2ifc.xml = self.filepath
msp2ifc.file = self.file
msp2ifc.work_plan = self.file.by_type("IfcWorkPlan")[0] if self.file.by_type("IfcWorkPlan") else None
msp2ifc.execute()
Data.load(IfcStore.get_file())
print("Import finished in {:.2f} seconds".format(time.time() - start))
return {"FINISHED"}
class EnableEditingWorkCalendarTimes(bpy.types.Operator): class EnableEditingWorkCalendarTimes(bpy.types.Operator):
bl_idname = "bim.enable_editing_work_calendar_times" bl_idname = "bim.enable_editing_work_calendar_times"
bl_label = "Enable Editing Work Calendar Times" bl_label = "Enable Editing Work Calendar Times"
@@ -910,29 +833,20 @@ class EnableEditingWorkTime(bpy.types.Operator):
data = Data.work_times[self.work_time] data = Data.work_times[self.work_time]
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkTime").all_attributes(): blenderbim.bim.helper.import_attributes(
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) "IfcWorkTime", self.props.work_time_attributes, data, self.import_attributes
if data_type == "entity": )
continue
new = self.props.work_time_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if attribute.name() in ["Start", "Finish"]:
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
self.initialise_recurrence_components() self.initialise_recurrence_components()
self.load_recurrence_pattern_data(data) self.load_recurrence_pattern_data(data)
self.props.active_work_time_id = self.work_time self.props.active_work_time_id = self.work_time
return {"FINISHED"} return {"FINISHED"}
def import_attributes(self, name, prop, data):
if name in ["Start", "Finish"]:
prop.string_value = "" if prop.is_null else data[name].isoformat()
return True
def initialise_recurrence_components(self): def initialise_recurrence_components(self):
if len(self.props.day_components) == 0: if len(self.props.day_components) == 0:
for i in range(0, 31): for i in range(0, 31):
@@ -988,15 +902,7 @@ class EditWorkTime(bpy.types.Operator):
def execute(self, context): def execute(self, context):
self.props = context.scene.BIMWorkCalendarProperties self.props = context.scene.BIMWorkCalendarProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(self.props.work_time_attributes)
for attribute in self.props.work_time_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_work_time", "sequence.edit_work_time",
@@ -1163,6 +1069,7 @@ class EditTaskCalendar(bpy.types.Operator):
}, },
) )
Data.load(IfcStore.get_file()) Data.load(IfcStore.get_file())
bpy.ops.bim.load_task_properties(task=self.task)
return {"FINISHED"} return {"FINISHED"}
@@ -1183,6 +1090,7 @@ class RemoveTaskCalendar(bpy.types.Operator):
}, },
) )
Data.load(IfcStore.get_file()) Data.load(IfcStore.get_file())
bpy.ops.bim.load_task_properties(task=self.task)
return {"FINISHED"} return {"FINISHED"}
@@ -1225,21 +1133,7 @@ class EnableEditingSequenceAttributes(bpy.types.Operator):
def enable_editing_sequence_attributes(self): def enable_editing_sequence_attributes(self):
data = Data.sequences[self.sequence] data = Data.sequences[self.sequence]
for attribute in IfcStore.get_schema().declaration_by_name("IfcRelSequence").all_attributes(): blenderbim.bim.helper.import_attributes("IfcRelSequence", self.props.sequence_attributes, data)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = self.props.sequence_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class EnableEditingSequenceTimeLag(bpy.types.Operator): class EnableEditingSequenceTimeLag(bpy.types.Operator):
@@ -1259,34 +1153,22 @@ class EnableEditingSequenceTimeLag(bpy.types.Operator):
def enable_editing_attributes(self): def enable_editing_attributes(self):
data = Data.lag_times[self.lag_time] data = Data.lag_times[self.lag_time]
for attribute in IfcStore.get_schema().declaration_by_name("IfcLagTime").all_attributes(): blenderbim.bim.helper.import_attributes(
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) "IfcLagTime", self.props.time_lag_attributes, data, self.import_attributes
if data_type == "entity": )
continue
new = self.props.time_lag_attributes.add() def import_attributes(self, name, prop, data):
new.name = attribute.name() if name == "LagValue":
new.is_null = data[attribute.name()] is None if isinstance(data[name], isodate.Duration):
new.is_optional = attribute.optional() prop.data_type = "string"
new.data_type = data_type prop.string_value = (
if attribute.name() == "LagValue": "" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration")
if isinstance(data[attribute.name()], isodate.Duration): )
new.data_type = "string" return True
new.string_value = ( else:
"" prop.data_type = "float"
if new.is_null prop.float_value = 0.0 if prop.is_null else data[name]
else ifcopenshell.util.date.datetime2ifc(data[attribute.name()], "IfcDuration") return True
)
else:
new.data_type = "float"
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class UnassignLagTime(bpy.types.Operator): class UnassignLagTime(bpy.types.Operator):
@@ -1325,15 +1207,7 @@ class EditSequenceAttributes(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.sequence_attributes)
for attribute in props.sequence_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_sequence", "sequence.edit_sequence",
@@ -1352,17 +1226,7 @@ class EditSequenceTimeLag(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.time_lag_attributes)
for attribute in props.time_lag_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "float":
attributes[attribute.name] = attribute.float_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_lag_time", "sequence.edit_lag_time",
@@ -159,6 +159,7 @@ class Task(PropertyGroup):
derived_start: StringProperty(name="Derived Start") derived_start: StringProperty(name="Derived Start")
derived_finish: StringProperty(name="Derived Finish") derived_finish: StringProperty(name="Derived Finish")
derived_duration: StringProperty(name="Derived Duration") derived_duration: StringProperty(name="Derived Duration")
calendar: StringProperty(name="Calendar")
is_predecessor: BoolProperty(name="Is Predecessor") is_predecessor: BoolProperty(name="Is Predecessor")
is_successor: BoolProperty(name="Is Successor") is_successor: BoolProperty(name="Is Successor")
@@ -189,6 +190,7 @@ class BIMWorkScheduleProperties(PropertyGroup):
task_attributes: CollectionProperty(name="Task Attributes", type=Attribute) task_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", default=False) should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", default=False)
should_show_times: BoolProperty(name="Should Show Times", default=False) should_show_times: BoolProperty(name="Should Show Times", default=False)
should_show_calendars: BoolProperty(name="Should Show Calendars", default=False)
active_task_time_id: IntProperty(name="Active Task Id") active_task_time_id: IntProperty(name="Active Task Id")
task_time_attributes: CollectionProperty(name="Task Time Attributes", type=Attribute) task_time_attributes: CollectionProperty(name="Task Time Attributes", type=Attribute)
contracted_tasks: StringProperty(name="Contracted Task Items", default="[]") contracted_tasks: StringProperty(name="Contracted Task Items", default="[]")
@@ -109,6 +109,7 @@ class BIM_PT_work_schedules(Panel):
row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK") row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK")
elif self.props.editing_type == "TASKS": elif self.props.editing_type == "TASKS":
row.prop(self.props, "should_show_times", text="", icon="TIME") row.prop(self.props, "should_show_times", text="", icon="TIME")
row.prop(self.props, "should_show_calendars", text="", icon="VIEW_ORTHO")
row.prop(self.props, "should_show_visualisation_ui", text="", icon="CAMERA_STEREO") row.prop(self.props, "should_show_visualisation_ui", text="", icon="CAMERA_STEREO")
row.operator("bim.generate_gantt_chart", text="", icon="NLA").work_schedule = work_schedule_id row.operator("bim.generate_gantt_chart", text="", icon="NLA").work_schedule = work_schedule_id
row.operator("bim.add_summary_task", text="", icon="ADD").work_schedule = work_schedule_id row.operator("bim.add_summary_task", text="", icon="ADD").work_schedule = work_schedule_id
@@ -336,6 +337,9 @@ class BIM_UL_tasks(UIList):
else: else:
row.prop(item, "duration", emboss=False, text="") row.prop(item, "duration", emboss=False, text="")
if props.should_show_calendars:
row.label(text=item.calendar)
if context.active_object: if context.active_object:
oprops = context.active_object.BIMObjectProperties oprops = context.active_object.BIMObjectProperties
row = layout.row(align=True) row = layout.row(align=True)
@@ -349,10 +353,6 @@ class BIM_UL_tasks(UIList):
if props.active_task_id == item.ifc_definition_id: if props.active_task_id == item.ifc_definition_id:
if props.editing_task_type == "TASKTIME": if props.editing_task_type == "TASKTIME":
row.operator("bim.edit_task_time", text="", icon="CHECKMARK") row.operator("bim.edit_task_time", text="", icon="CHECKMARK")
elif props.editing_task_type == "CALENDAR":
row.operator("bim.disable_editing_task", text="", icon="CHECKMARK")
elif props.editing_task_type == "SEQUENCE":
row.operator("bim.disable_editing_task", text="", icon="CHECKMARK")
elif props.editing_task_type == "ATTRIBUTES": elif props.editing_task_type == "ATTRIBUTES":
row.operator("bim.edit_task", text="", icon="CHECKMARK") row.operator("bim.edit_task", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_task", text="", icon="CANCEL") row.operator("bim.disable_editing_task", text="", icon="CANCEL")
@@ -20,22 +20,33 @@ classes = (
operator.EnableEditingStructuralConnectionCondition, operator.EnableEditingStructuralConnectionCondition,
operator.DisableEditingStructuralConnectionCondition, operator.DisableEditingStructuralConnectionCondition,
operator.RemoveStructuralConnectionCondition, operator.RemoveStructuralConnectionCondition,
operator.EnableEditingStructuralMemberAxis, operator.EnableEditingStructuralItemAxis,
operator.DisableEditingStructuralMemberAxis, operator.DisableEditingStructuralItemAxis,
operator.EditStructuralMemberAxis, operator.EditStructuralItemAxis,
operator.EnableEditingStructuralItemConnectionCS,
operator.DisableEditingStructuralItemConnectionCS,
operator.EditStructuralItemConnectionCS,
operator.AddStructuralLoadCase, operator.AddStructuralLoadCase,
operator.EditStructuralLoadCase, operator.EditStructuralLoadCase,
operator.RemoveStructuralLoadCase, operator.RemoveStructuralLoadCase,
operator.AddStructuralLoadGroup,
operator.RemoveStructuralLoadGroup,
operator.AddStructuralActivity,
operator.EnableEditingStructuralLoadCase, operator.EnableEditingStructuralLoadCase,
operator.EnableEditingStructuralLoadCaseGroups,
operator.DisableEditingStructuralLoadCase, operator.DisableEditingStructuralLoadCase,
operator.EnableEditingStructuralLoadGroupActivities,
prop.StructuralAnalysisModel, prop.StructuralAnalysisModel,
prop.StructuralActivity,
prop.BIMStructuralProperties, prop.BIMStructuralProperties,
prop.BIMObjectStructuralProperties, prop.BIMObjectStructuralProperties,
ui.BIM_PT_structural_analysis_models, ui.BIM_PT_structural_analysis_models,
ui.BIM_PT_structural_boundary_conditions, ui.BIM_PT_structural_boundary_conditions,
ui.BIM_PT_connected_structural_members, ui.BIM_PT_connected_structural_members,
ui.BIM_PT_structural_member, ui.BIM_PT_structural_member,
ui.BIM_PT_structural_connection,
ui.BIM_UL_structural_analysis_models, ui.BIM_UL_structural_analysis_models,
ui.BIM_UL_structural_activities,
ui.BIM_PT_structural_load_cases, ui.BIM_PT_structural_load_cases,
) )
@@ -2,6 +2,7 @@ import bpy
import json import json
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import blenderbim.bim.helper
from math import degrees from math import degrees
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -259,7 +260,7 @@ class EditStructuralAnalysisModel(bpy.types.Operator):
**{ **{
"structural_analysis_model": self.file.by_id(props.active_structural_analysis_model_id), "structural_analysis_model": self.file.by_id(props.active_structural_analysis_model_id),
"attributes": attributes, "attributes": attributes,
} },
) )
Data.load(IfcStore.get_file()) Data.load(IfcStore.get_file())
bpy.ops.bim.load_structural_analysis_models() bpy.ops.bim.load_structural_analysis_models()
@@ -277,7 +278,7 @@ class RemoveStructuralAnalysisModel(bpy.types.Operator):
ifcopenshell.api.run( ifcopenshell.api.run(
"structural.remove_structural_analysis_model", "structural.remove_structural_analysis_model",
self.file, self.file,
**{"structural_analysis_model": self.file.by_id(self.structural_analysis_model)} **{"structural_analysis_model": self.file.by_id(self.structural_analysis_model)},
) )
Data.load(IfcStore.get_file()) Data.load(IfcStore.get_file())
bpy.ops.bim.load_structural_analysis_models() bpy.ops.bim.load_structural_analysis_models()
@@ -340,7 +341,7 @@ class AssignStructuralAnalysisModel(bpy.types.Operator):
**{ **{
"product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id),
"structural_analysis_model": self.file.by_id(self.structural_analysis_model), "structural_analysis_model": self.file.by_id(self.structural_analysis_model),
} },
) )
Data.load(IfcStore.get_file()) Data.load(IfcStore.get_file())
return {"FINISHED"} return {"FINISHED"}
@@ -361,15 +362,15 @@ class UnassignStructuralAnalysisModel(bpy.types.Operator):
**{ **{
"product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id), "product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id),
"structural_analysis_model": self.file.by_id(self.structural_analysis_model), "structural_analysis_model": self.file.by_id(self.structural_analysis_model),
} },
) )
Data.load(IfcStore.get_file()) Data.load(IfcStore.get_file())
return {"FINISHED"} return {"FINISHED"}
class EnableEditingStructuralMemberAxis(bpy.types.Operator): class EnableEditingStructuralItemAxis(bpy.types.Operator):
bl_idname = "bim.enable_editing_structural_member_axis" bl_idname = "bim.enable_editing_structural_item_axis"
bl_label = "Enable Editing Structural Member Axis" bl_label = "Enable Editing Structural Item Axis"
def execute(self, context): def execute(self, context):
obj = bpy.context.active_object obj = bpy.context.active_object
@@ -377,20 +378,22 @@ class EnableEditingStructuralMemberAxis(bpy.types.Operator):
props = obj.BIMStructuralProperties props = obj.BIMStructuralProperties
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
member = self.file.by_id(oprops.ifc_definition_id) item = self.file.by_id(oprops.ifc_definition_id)
z_axis = Vector(member.Axis.DirectionRatios).normalized() @ obj.matrix_world if member.Axis else None z_axis = Vector(item.Axis.DirectionRatios).normalized() @ obj.matrix_world if item.Axis else None
x_axis = (obj.data.vertices[1].co - obj.data.vertices[0].co).normalized() x_axis = (obj.data.vertices[1].co - obj.data.vertices[0].co).normalized()
location = obj.data.vertices[0].co location = obj.data.vertices[0].co
empty = bpy.data.objects.new("Member Axis", None) empty = bpy.data.objects.new("Item Axis", None)
empty.empty_display_type = "ARROWS" empty.empty_display_type = "ARROWS"
if z_axis: if z_axis:
y_axis = (z_axis.cross(x_axis)).normalized() y_axis = (z_axis.cross(x_axis)).normalized()
empty.matrix_world = Matrix(( empty.matrix_world = Matrix(
(x_axis[0], y_axis[0], z_axis[0], location[0]), (
(x_axis[1], y_axis[1], z_axis[1], location[1]), (x_axis[0], y_axis[0], z_axis[0], location[0]),
(x_axis[2], y_axis[2], z_axis[2], location[2]), (x_axis[1], y_axis[1], z_axis[1], location[1]),
(0, 0, 0, 1), (x_axis[2], y_axis[2], z_axis[2], location[2]),
)) (0, 0, 0, 1),
)
)
else: else:
empty.location = location empty.location = location
empty.rotation_mode = "QUATERNION" empty.rotation_mode = "QUATERNION"
@@ -404,9 +407,9 @@ class EnableEditingStructuralMemberAxis(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class DisableEditingStructuralMemberAxis(bpy.types.Operator): class DisableEditingStructuralItemAxis(bpy.types.Operator):
bl_idname = "bim.disable_editing_structural_member_axis" bl_idname = "bim.disable_editing_structural_item_axis"
bl_label = "Disable Editing Structural Member Axis" bl_label = "Disable Editing Structural Item Axis"
def execute(self, context): def execute(self, context):
obj = bpy.context.active_object obj = bpy.context.active_object
@@ -417,9 +420,9 @@ class DisableEditingStructuralMemberAxis(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class EditStructuralMemberAxis(bpy.types.Operator): class EditStructuralItemAxis(bpy.types.Operator):
bl_idname = "bim.edit_structural_member_axis" bl_idname = "bim.edit_structural_item_axis"
bl_label = "Edit Structural Member Axis" bl_label = "Edit Structural Item Axis"
def execute(self, context): def execute(self, context):
obj = bpy.context.active_object obj = bpy.context.active_object
@@ -429,12 +432,85 @@ class EditStructuralMemberAxis(bpy.types.Operator):
z_axis = relative_matrix.col[2][0:3] z_axis = relative_matrix.col[2][0:3]
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"structural.edit_structural_member_axis", "structural.edit_structural_item_axis",
self.file, self.file,
structural_member=self.file.by_id(oprops.ifc_definition_id), structural_item=self.file.by_id(oprops.ifc_definition_id),
axis=z_axis, axis=z_axis,
) )
bpy.ops.bim.disable_editing_structural_member_axis() bpy.ops.bim.disable_editing_structural_item_axis()
return {"FINISHED"}
class EnableEditingStructuralItemConnectionCS(bpy.types.Operator):
bl_idname = "bim.enable_editing_structural_item_connection_cs"
bl_label = "Enable Editing Structural Item Connection CS"
def execute(self, context):
obj = bpy.context.active_object
oprops = obj.BIMObjectProperties
props = obj.BIMStructuralProperties
self.file = IfcStore.get_file()
item = self.file.by_id(oprops.ifc_definition_id)
z_axis = Vector(item.Axis.DirectionRatios).normalized() @ obj.matrix_world if item.Axis else None
x_axis = (obj.data.vertices[1].co - obj.data.vertices[0].co).normalized()
location = obj.data.vertices[0].co
empty = bpy.data.objects.new("Item Connection CS", None)
empty.empty_display_type = "ARROWS"
if z_axis:
y_axis = (z_axis.cross(x_axis)).normalized()
empty.matrix_world = Matrix(
(
(x_axis[0], y_axis[0], z_axis[0], location[0]),
(x_axis[1], y_axis[1], z_axis[1], location[1]),
(x_axis[2], y_axis[2], z_axis[2], location[2]),
(0, 0, 0, 1),
)
)
else:
empty.location = location
empty.rotation_mode = "QUATERNION"
empty.rotation_quaternion = x_axis.to_track_quat("X", "Z")
props.axis_angle = degrees(empty.rotation_euler[0])
props.axis_empty = empty
context.scene.collection.objects.link(empty)
props.is_editing_axis = True
return {"FINISHED"}
class DisableEditingStructuralItemConnectionCS(bpy.types.Operator):
bl_idname = "bim.disable_editing_structural_item_connection_cs"
bl_label = "Disable Editing Structural Item Connection CS"
def execute(self, context):
obj = bpy.context.active_object
props = obj.BIMStructuralProperties
props.is_editing_axis = False
if props.axis_empty:
bpy.data.objects.remove(props.axis_empty)
return {"FINISHED"}
class EditStructuralItemConnectionCS(bpy.types.Operator):
bl_idname = "bim.edit_structural_item_connection_cs"
bl_label = "Edit Structural Item Connection CS"
def execute(self, context):
obj = bpy.context.active_object
oprops = obj.BIMObjectProperties
props = obj.BIMStructuralProperties
relative_matrix = props.axis_empty.matrix_world @ obj.matrix_world.inverted()
z_axis = relative_matrix.col[2][0:3]
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"structural.edit_structural_item_connection_cs",
self.file,
structural_item=self.file.by_id(oprops.ifc_definition_id),
axis=z_axis,
)
bpy.ops.bim.disable_editing_structural_item_axis()
return {"FINISHED"} return {"FINISHED"}
@@ -494,15 +570,7 @@ class EditStructuralLoadCase(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMStructuralProperties props = context.scene.BIMStructuralProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.load_case_attributes)
for attribute in props.load_case_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"structural.edit_structural_load_case", "structural.edit_structural_load_case",
@@ -536,33 +604,18 @@ class EnableEditingStructuralLoadCase(bpy.types.Operator):
def execute(self, context): def execute(self, context):
self.props = context.scene.BIMStructuralProperties self.props = context.scene.BIMStructuralProperties
self.props.active_load_case_id = self.load_case self.props.active_load_case_id = self.load_case
self.props.load_case_editing_type = "ATTRIBUTES"
while len(self.props.load_case_attributes) > 0: while len(self.props.load_case_attributes) > 0:
self.props.load_case_attributes.remove(0) self.props.load_case_attributes.remove(0)
self.enable_editing_structural_load_case() data = Data.load_cases[self.load_case]
blenderbim.bim.helper.import_attributes(
"IfcStructuralLoadCase", self.props.load_case_attributes, data, self.import_attributes
)
return {"FINISHED"} return {"FINISHED"}
def enable_editing_structural_load_case(self): def import_attributes(self, name, prop, data):
data = Data.load_cases[self.load_case] if name in ["SelfWeightCoefficients"]:
print(data) return False
for attribute in IfcStore.get_schema().declaration_by_name("IfcStructuralLoadCase").all_attributes():
if attribute.name() in ["SelfWeightCoefficients", "Coefficient"]:
continue
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = self.props.load_case_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
print(data_type)
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class DisableEditingStructuralLoadCase(bpy.types.Operator): class DisableEditingStructuralLoadCase(bpy.types.Operator):
@@ -571,4 +624,109 @@ class DisableEditingStructuralLoadCase(bpy.types.Operator):
def execute(self, context): def execute(self, context):
context.scene.BIMStructuralProperties.active_load_case_id = 0 context.scene.BIMStructuralProperties.active_load_case_id = 0
return {"FINISHED"} return {"FINISHED"}
class EnableEditingStructuralLoadCaseGroups(bpy.types.Operator):
bl_idname = "bim.enable_editing_structural_load_case_groups"
bl_label = "Enable Editing Structural Load Case Groups"
load_case: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMStructuralProperties
self.props.active_load_case_id = self.load_case
self.props.load_case_editing_type = "GROUPS"
return {"FINISHED"}
class AddStructuralLoadGroup(bpy.types.Operator):
bl_idname = "bim.add_structural_load_group"
bl_label = "Add Structural Load Group"
load_case: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
load_group = ifcopenshell.api.run("structural.add_structural_load_group", self.file)
ifcopenshell.api.run("group.assign_group", self.file, product=load_group, group=self.file.by_id(self.load_case))
Data.load(IfcStore.get_file())
return {"FINISHED"}
class RemoveStructuralLoadGroup(bpy.types.Operator):
bl_idname = "bim.remove_structural_load_group"
bl_label = "Remove Structural Load Group"
load_group: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"structural.remove_structural_load_group", self.file, load_group=self.file.by_id(self.load_group)
)
Data.load(self.file)
return {"FINISHED"}
class EnableEditingStructuralLoadGroupActivities(bpy.types.Operator):
bl_idname = "bim.enable_editing_structural_load_group_activities"
bl_label = "Enable Editing Structural Load Group Activities"
load_group: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
self.props = context.scene.BIMStructuralProperties
self.props.active_load_group_id = self.load_group
self.props.load_group_editing_type = "ACTIVITY"
self.load_structural_activities()
return {"FINISHED"}
def load_structural_activities(self):
while len(self.props.load_group_activities) > 0:
self.props.load_group_activities.remove(0)
for activity_id in Data.load_groups[self.load_group]["IsGroupedBy"]:
activity = Data.structural_activities[activity_id]
new = self.props.load_group_activities.add()
new.ifc_definition_id = activity_id
new.name = self.file.by_id(activity["AssignedToStructuralItem"]).Name or "Unnamed"
new.applied_load_class = self.file.by_id(activity["AppliedLoad"]).is_a()
class AddStructuralActivity(bpy.types.Operator):
bl_idname = "bim.add_structural_activity"
bl_label = "Add Structural Activity"
load_group: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMStructuralProperties
self.file = IfcStore.get_file()
for obj in context.selected_objects:
if not obj.BIMObjectProperties.ifc_definition_id:
continue
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
applied_load_class = self.props.applicable_structural_activity_types
if element.is_a("IfcStructuralPointConnection"):
if applied_load_class not in ["IfcStructuralLoadSingleForce", "IfcStructuralLoadSingleDisplacement"]:
continue
ifc_class = "IfcStructuralPointAction"
elif element.is_a("IfcStructuralCurveMember"):
if applied_load_class != "IfcStructuralLoadLinearForce":
continue
ifc_class = "IfcStructuralLinearAction"
elif element.is_a("IfcStructuralSurfaceMember"):
if applied_load_class != "IfcStructuralLoadPlanarForce":
continue
ifc_class = "IfcStructuralPlanarAction"
activity = ifcopenshell.api.run(
"structural.add_structural_activity",
self.file,
ifc_class=ifc_class,
applied_load=None, # TODO
structural_member=element
)
ifcopenshell.api.run(
"group.assign_group", self.file, product=activity, group=self.file.by_id(self.load_group)
)
Data.load(IfcStore.get_file())
bpy.ops.bim.enable_editing_structural_load_group_activities(load_group=self.load_group)
return {"FINISHED"}
@@ -1,4 +1,5 @@
import bpy import bpy
from blenderbim.bim.ifc import IfcStore
from math import radians from math import radians
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
@@ -14,6 +15,28 @@ from bpy.props import (
) )
def getApplicableStructuralActivityTypes(self, context):
ifc_file = IfcStore.get_file()
element_classes = set(
[
ifc_file.by_id(o.BIMObjectProperties.ifc_definition_id).is_a()
for o in bpy.context.selected_objects
if o.BIMObjectProperties.ifc_definition_id
]
)
types = [("IfcStructuralLoadTemperature", "IfcStructuralLoadTemperature", "")]
if "IfcStructuralPointConnection" in element_classes:
types.extend([
("IfcStructuralLoadSingleForce", "IfcStructuralLoadSingleForce", ""),
("IfcStructuralLoadSingleDisplacement", "IfcStructuralLoadSingleDisplacement", "")
])
if "IfcStructuralCurveMember" in element_classes:
types.append(("IfcStructuralLoadLinearForce", "IfcStructuralLoadLinearForce", ""))
if "IfcStructuralSurfaceMember" in element_classes:
types.append(("IfcStructuralLoadPlanarForce", "IfcStructuralLoadPlanarForce", ""))
return types
def updateAxisAngle(self, context): def updateAxisAngle(self, context):
if not self.axis_empty: if not self.axis_empty:
return return
@@ -27,11 +50,29 @@ def updateAxisAngle(self, context):
empty.rotation_euler[0] = radians(self.axis_angle) empty.rotation_euler[0] = radians(self.axis_angle)
def updateConnectionCS(self, context):
if not self.ccs_empty:
return
obj = context.active_object
empty = self.ccs_empty
empty.location = obj.data.vertices[0].co
empty.rotation_mode = "XYZ"
empty.rotation_euler[0] = radians(self.ccs_x_angle)
empty.rotation_euler[1] = radians(self.ccs_y_angle)
empty.rotation_euler[2] = radians(self.ccs_z_angle)
class StructuralAnalysisModel(PropertyGroup): class StructuralAnalysisModel(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
class StructuralActivity(PropertyGroup):
name: StringProperty(name="Name")
applied_load_class: StringProperty(name="Applied Load Class")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class BIMStructuralProperties(PropertyGroup): class BIMStructuralProperties(PropertyGroup):
structural_analysis_model_attributes: CollectionProperty( structural_analysis_model_attributes: CollectionProperty(
name="Structural Analysis Model Attributes", type=Attribute name="Structural Analysis Model Attributes", type=Attribute
@@ -40,12 +81,17 @@ class BIMStructuralProperties(PropertyGroup):
structural_analysis_models: CollectionProperty(name="Structural Analysis Models", type=StructuralAnalysisModel) structural_analysis_models: CollectionProperty(name="Structural Analysis Models", type=StructuralAnalysisModel)
active_structural_analysis_model_index: IntProperty(name="Active Structural Analysis Model Index") active_structural_analysis_model_index: IntProperty(name="Active Structural Analysis Model Index")
active_structural_analysis_model_id: IntProperty(name="Active Structural Analysis Model Id") active_structural_analysis_model_id: IntProperty(name="Active Structural Analysis Model Id")
load_case_editing_type: StringProperty(name="Load Case Editing Type")
# editing_type: StringProperty(name="Editing Type")
# active_load_case_index: IntProperty(name="Active Work Schedules Index")
load_case_attributes: CollectionProperty(name="Load Case Attributes", type=Attribute) load_case_attributes: CollectionProperty(name="Load Case Attributes", type=Attribute)
active_load_case_id: IntProperty(name="Active Load Case Id") active_load_case_id: IntProperty(name="Active Load Case Id")
load_group_editing_type: StringProperty(name="Load Group Editing Type")
# load_group_attributes: CollectionProperty(name="Load Group Attributes", type=Attribute)
active_load_group_id: IntProperty(name="Active Load Group Id")
applicable_structural_activity_types: EnumProperty(
items=getApplicableStructuralActivityTypes, name="Applicable Structural Activity Types"
)
load_group_activities: CollectionProperty(name="Load Group Activities", type=StructuralActivity)
active_load_group_activity_index: IntProperty(name="Active Load Group Activity Index")
class BIMObjectStructuralProperties(PropertyGroup): class BIMObjectStructuralProperties(PropertyGroup):
@@ -58,3 +104,8 @@ class BIMObjectStructuralProperties(PropertyGroup):
axis_empty: PointerProperty(name="Axis Empty", type=bpy.types.Object) axis_empty: PointerProperty(name="Axis Empty", type=bpy.types.Object)
# relating_structural_activity: PointerProperty(name="Relating Structural Activity", type=bpy.types.Object) # relating_structural_activity: PointerProperty(name="Relating Structural Activity", type=bpy.types.Object)
is_editing_connection_cs: BoolProperty(name="Is Editing Connection CS", default=False)
ccs_x_angle: FloatProperty(name="Connection CS X Angle", update=updateConnectionCS)
ccs_y_angle: FloatProperty(name="Connection CS Y Angle", update=updateConnectionCS)
ccs_z_angle: FloatProperty(name="Connection CS Z Angle", update=updateConnectionCS)
ccs_empty: PointerProperty(name="CCS Empty", type=bpy.types.Object)
@@ -1,3 +1,4 @@
import bpy
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.structural.data import Data from ifcopenshell.api.structural.data import Data
@@ -169,11 +170,63 @@ class BIM_PT_structural_member(Panel):
if self.props.is_editing_axis: if self.props.is_editing_axis:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.prop(self.props, "axis_angle") row.prop(self.props, "axis_angle")
row.operator("bim.edit_structural_member_axis", text="", icon="CHECKMARK") row.operator("bim.edit_structural_item_axis", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_structural_member_axis", text="", icon="CANCEL") row.operator("bim.disable_editing_structural_item_axis", text="", icon="CANCEL")
else: else:
row = self.layout.row() row = self.layout.row()
row.operator("bim.enable_editing_structural_member_axis", text="Edit Axis", icon="GREASEPENCIL") row.operator("bim.enable_editing_structural_item_axis", text="Edit Axis", icon="GREASEPENCIL")
else:
row = self.layout.row()
row.label(text="TODO")
class BIM_PT_structural_connection(Panel):
bl_label = "IFC Structural Connection"
bl_idname = "BIM_PT_structural_connection"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@classmethod
def poll(cls, context):
if not context.active_object:
return False
props = context.active_object.BIMObjectProperties
if not props.ifc_definition_id:
return False
if not IfcStore.get_file().by_id(props.ifc_definition_id).is_a("IfcStructuralConnection"):
return False
return True
def draw(self, context):
self.oprops = context.active_object.BIMObjectProperties
self.props = context.active_object.BIMStructuralProperties
self.file = IfcStore.get_file()
if self.file.by_id(self.oprops.ifc_definition_id).is_a("IfcStructuralCurveConnection"):
if self.props.is_editing_axis:
row = self.layout.row(align=True)
row.prop(self.props, "axis_angle")
row.operator("bim.edit_structural_item_axis", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_structural_item_axis", text="", icon="CANCEL")
else:
row = self.layout.row()
row.operator("bim.enable_editing_structural_item_axis", text="Edit Axis", icon="GREASEPENCIL")
elif self.file.by_id(self.oprops.ifc_definition_id).is_a("IfcStructuralPointConnection"):
if self.props.is_editing_connection_cs:
row = self.layout.row(align=True)
row.label(text="Editing Connection CS")
row.operator("bim.edit_structural_item_connection_cs", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_structural_item_connection_cs", text="", icon="CANCEL")
row = self.layout.row(align=True)
row.prop(self.props, "ccs_x_angle")
row.prop(self.props, "ccs_y_angle")
row.prop(self.props, "ccs_z_angle")
else:
row = self.layout.row()
row.operator("bim.enable_editing_structural_item_connection_cs", text="Edit Connection CS", icon="GREASEPENCIL")
else: else:
row = self.layout.row() row = self.layout.row()
row.label(text="TODO") row.label(text="TODO")
@@ -292,25 +345,76 @@ class BIM_PT_structural_load_cases(Panel):
row.label(text=load_case["Name"] or "Unnamed", icon="CON_CLAMPTO") row.label(text=load_case["Name"] or "Unnamed", icon="CON_CLAMPTO")
if self.props.active_load_case_id and self.props.active_load_case_id == load_case_id: if self.props.active_load_case_id and self.props.active_load_case_id == load_case_id:
row.operator("bim.edit_structural_load_case", text="", icon="CHECKMARK") if self.props.load_case_editing_type == "ATTRIBUTES":
row.operator("bim.edit_structural_load_case", text="", icon="CHECKMARK")
elif self.props.load_case_editing_type == "GROUPS":
row.operator("bim.add_structural_load_group", text="", icon="ADD").load_case = load_case_id
row.operator("bim.disable_editing_structural_load_case", text="", icon="CANCEL") row.operator("bim.disable_editing_structural_load_case", text="", icon="CANCEL")
elif self.props.active_load_case_id: elif self.props.active_load_case_id:
row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case_id row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case_id
else: else:
row.operator(
"bim.enable_editing_structural_load_case_groups", text="", icon="GHOST_ENABLED"
).load_case = load_case_id
row.operator( row.operator(
"bim.enable_editing_structural_load_case", text="", icon="GREASEPENCIL" "bim.enable_editing_structural_load_case", text="", icon="GREASEPENCIL"
).load_case = load_case_id ).load_case = load_case_id
row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case_id row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case_id
if self.props.active_load_case_id == load_case_id: if self.props.active_load_case_id == load_case_id:
self.draw_editable_load_case_ui() if self.props.load_case_editing_type == "ATTRIBUTES":
self.draw_editable_load_case_ui()
elif self.props.load_case_editing_type == "GROUPS":
self.draw_editable_load_case_group_ui(load_case)
def draw_editable_load_case_ui(self): def draw_editable_load_case_ui(self):
for attribute in self.props.load_case_attributes: for attribute in self.props.load_case_attributes:
row = self.layout.row(align=True) row = self.layout.row(align=True)
if attribute.data_type == "string": if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name) row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "enum": elif attribute.data_type == "enum":
row.prop(attribute, "enum_value", text=attribute.name) row.prop(attribute, "enum_value", text=attribute.name)
if attribute.is_optional: if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
def draw_editable_load_case_group_ui(self, load_case):
box = self.layout.box()
if not len(load_case["IsGroupedBy"]):
row = box.row(align=True)
row.label(text="No Load Groups Found")
for load_group_id in load_case["IsGroupedBy"]:
load_group = Data.load_groups[load_group_id]
row = box.row(align=True)
row.label(text=load_group["Name"] or "Unnamed", icon="GHOST_ENABLED")
op = row.operator("bim.enable_editing_structural_load_group_activities", text="", icon="GHOST_ENABLED")
op.load_group = load_group_id
row.operator("bim.remove_structural_load_group", text="", icon="X").load_group = load_group_id
if self.props.active_load_group_id == load_group_id:
if self.props.load_group_editing_type == "ACTIVITY":
self.draw_editable_load_group_activities_ui(box, load_group)
def draw_editable_load_group_activities_ui(self, layout, load_group):
row = layout.row(align=True)
row.prop(self.props, "applicable_structural_activity_types", text="")
op = row.operator("bim.add_structural_activity", text="", icon="ADD")
op.load_group = load_group["id"]
layout.template_list(
"BIM_UL_structural_activities",
"",
self.props,
"load_group_activities",
self.props,
"active_load_group_activity_index",
)
class BIM_UL_structural_activities(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=item.name)
row.label(text=item.applied_load_class)
+8 -3
View File
@@ -166,8 +166,11 @@ namespace IfcGeom {
std::vector<T> ts; std::vector<T> ts;
if (IfcGeom::Kernel::count(s, TopAbs_SHELL) == 0) { if (extend < 0.) {
return ts; // Shell are only required when we do the boolean based intersection check
if (IfcGeom::Kernel::count(s, TopAbs_SHELL) == 0) {
return ts;
}
} }
ts = select_box(bb, completely_within); ts = select_box(bb, completely_within);
@@ -288,7 +291,9 @@ namespace IfcGeom {
if (it.initialize()) { if (it.initialize()) {
do { do {
IfcGeom::BRepElement<double>* elem = (IfcGeom::BRepElement<double>*)it.get(); IfcGeom::BRepElement<double>* elem = (IfcGeom::BRepElement<double>*)it.get();
add((IfcUtil::IfcBaseEntity*)it.file()->instance_by_id(elem->id()), elem->geometry().as_compound()); auto compound = elem->geometry().as_compound();
compound.Move(elem->transformation().data());
add((IfcUtil::IfcBaseEntity*)it.file()->instance_by_id(elem->id()), compound);
} while (it.next()); } while (it.next());
} }
} }