mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 02:23:34 +00:00
merage
This commit is contained in:
@@ -1,153 +1,64 @@
|
||||
import bpy
|
||||
import json
|
||||
import math
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.attribute
|
||||
from mathutils import geometry
|
||||
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:
|
||||
prefixes = {
|
||||
"EXA": 1e18,
|
||||
"PETA": 1e15,
|
||||
"TERA": 1e12,
|
||||
"GIGA": 1e9,
|
||||
"MEGA": 1e6,
|
||||
"KILO": 1e3,
|
||||
"HECTO": 1e2,
|
||||
"DECA": 1e1,
|
||||
"DECI": 1e-1,
|
||||
"CENTI": 1e-2,
|
||||
"MILLI": 1e-3,
|
||||
"MICRO": 1e-6,
|
||||
"NANO": 1e-9,
|
||||
"PICO": 1e-12,
|
||||
"FEMTO": 1e-15,
|
||||
"ATTO": 1e-18,
|
||||
}
|
||||
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,
|
||||
}
|
||||
def export_attributes(props, callback=None):
|
||||
attributes = {}
|
||||
for attribute in props:
|
||||
is_handled_by_callback = callback(attributes, attribute) if callback else False
|
||||
if attribute.is_null:
|
||||
attributes[attribute.name] = None
|
||||
elif is_handled_by_callback:
|
||||
pass # Our job is done
|
||||
elif 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 == "float":
|
||||
attributes[attribute.name] = attribute.float_value
|
||||
elif attribute.data_type == "enum":
|
||||
attributes[attribute.name] = attribute.enum_value
|
||||
return attributes
|
||||
|
||||
@staticmethod
|
||||
def get_prefix(text):
|
||||
for prefix in SIUnitHelper.prefixes.keys():
|
||||
if prefix in text.upper():
|
||||
return prefix
|
||||
|
||||
@staticmethod
|
||||
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
|
||||
# TODO: migrate the below helper functions into the drawing module, since it is specific to that module
|
||||
|
||||
|
||||
# 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):
|
||||
"""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:
|
||||
raise RuntimeError("project collection missing or not unique")
|
||||
return colls[0]
|
||||
@@ -315,7 +226,7 @@ def get_active_drawing(scene):
|
||||
return None, None
|
||||
try:
|
||||
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):
|
||||
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
|
||||
size = camera.ortho_scale
|
||||
hwidth = size * .5
|
||||
hheight = size * .5 * aspect
|
||||
hwidth = size * 0.5
|
||||
hheight = size * 0.5 * aspect
|
||||
scale = parse_diagram_scale(camera)
|
||||
xmarg = margin * scale
|
||||
ymarg = margin * scale * aspect
|
||||
|
||||
@@ -718,7 +718,7 @@ class IfcImporter:
|
||||
# Create structural collections
|
||||
self.structural_member_collection = bpy.data.collections.new("Members")
|
||||
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_connection_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)
|
||||
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(
|
||||
|
||||
@@ -76,7 +76,7 @@ class ReassignClass(bpy.types.Operator):
|
||||
class AssignClass(bpy.types.Operator):
|
||||
bl_idname = "bim.assign_class"
|
||||
bl_label = "Assign IFC Class"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
obj: bpy.props.StringProperty()
|
||||
ifc_class: bpy.props.StringProperty()
|
||||
predefined_type: bpy.props.StringProperty()
|
||||
@@ -92,10 +92,6 @@ class AssignClass(bpy.types.Operator):
|
||||
elif self.predefined_type == "":
|
||||
predefined_type = None
|
||||
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)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ classes = (
|
||||
operator.UnassignProduct,
|
||||
operator.GenerateGanttChart,
|
||||
operator.ImportP6,
|
||||
operator.ImportMSP,
|
||||
operator.LoadTaskProperties,
|
||||
operator.SelectTaskRelatedProducts,
|
||||
operator.VisualiseWorkScheduleDate,
|
||||
@@ -82,6 +83,7 @@ classes = (
|
||||
|
||||
def menu_func_import(self, context):
|
||||
self.layout.operator(operator.ImportP6.bl_idname, text="P6 (.xml)")
|
||||
self.layout.operator(operator.ImportMSP.bl_idname, text="Microsoft Project (.xml)")
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import re
|
||||
import os
|
||||
import bpy
|
||||
import json
|
||||
@@ -7,6 +8,7 @@ import pystache
|
||||
import webbrowser
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.date
|
||||
import blenderbim.bim.helper
|
||||
import blenderbim.bim.module.sequence.helper as helper
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
@@ -32,15 +34,7 @@ class EditWorkPlan(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMWorkPlanProperties
|
||||
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
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.work_plan_attributes, self.export_attributes)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_work_plan",
|
||||
@@ -51,6 +45,14 @@ class EditWorkPlan(bpy.types.Operator):
|
||||
bpy.ops.bim.disable_editing_work_plan()
|
||||
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):
|
||||
bl_idname = "bim.remove_work_plan"
|
||||
@@ -76,27 +78,17 @@ class EnableEditingWorkPlan(bpy.types.Operator):
|
||||
|
||||
data = Data.work_plans[self.work_plan]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkPlan").all_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()]
|
||||
blenderbim.bim.helper.import_attributes("IfcWorkPlan", props.work_plan_attributes, data, self.import_attributes)
|
||||
|
||||
props.active_work_plan_id = self.work_plan
|
||||
props.editing_type = "ATTRIBUTES"
|
||||
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):
|
||||
bl_idname = "bim.disable_editing_work_plan"
|
||||
@@ -175,15 +167,7 @@ class EditWorkSchedule(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMWorkScheduleProperties
|
||||
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
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.work_schedule_attributes, self.export_attributes)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_work_schedule",
|
||||
@@ -194,6 +178,14 @@ class EditWorkSchedule(bpy.types.Operator):
|
||||
bpy.ops.bim.disable_editing_work_schedule()
|
||||
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):
|
||||
bl_idname = "bim.remove_work_schedule"
|
||||
@@ -226,23 +218,14 @@ class EnableEditingWorkSchedule(bpy.types.Operator):
|
||||
def enable_editing_work_schedule(self):
|
||||
data = Data.work_schedules[self.work_schedule]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkSchedule").all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity":
|
||||
continue
|
||||
new = self.props.work_schedule_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()]
|
||||
blenderbim.bim.helper.import_attributes(
|
||||
"IfcWorkSchedule", self.props.work_schedule_attributes, data, self.import_attributes
|
||||
)
|
||||
|
||||
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 EnableEditingTasks(bpy.types.Operator):
|
||||
@@ -258,10 +241,11 @@ class EnableEditingTasks(bpy.types.Operator):
|
||||
self.tprops.tasks.remove(0)
|
||||
|
||||
self.contracted_tasks = json.loads(self.props.contracted_tasks)
|
||||
sort_keys = {
|
||||
i: Data.tasks[i]["Identification"] for i in Data.work_schedules[self.work_schedule]["RelatedObjects"]
|
||||
self.sort_keys = {
|
||||
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)
|
||||
bpy.ops.bim.load_task_properties()
|
||||
self.props.editing_type = "TASKS"
|
||||
@@ -276,10 +260,13 @@ class EnableEditingTasks(bpy.types.Operator):
|
||||
if task["RelatedObjects"]:
|
||||
new.has_children = True
|
||||
if new.is_expanded:
|
||||
sort_keys = {i: Data.tasks[i]["Identification"] for i in task["RelatedObjects"]}
|
||||
for related_object_id in sorted(sort_keys, key=sort_keys.__getitem__):
|
||||
self.sort_keys = {i: Data.tasks[i]["Identification"] or "" for i in task["RelatedObjects"]}
|
||||
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)
|
||||
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):
|
||||
@@ -322,6 +309,10 @@ class LoadTaskProperties(bpy.types.Operator):
|
||||
item.start = "-"
|
||||
item.finish = "-"
|
||||
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
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -434,39 +425,23 @@ class EnableEditingTaskTime(bpy.types.Operator):
|
||||
|
||||
data = Data.task_times[task_time_id]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcTaskTime").all_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()]
|
||||
blenderbim.bim.helper.import_attributes("IfcTaskTime", props.task_time_attributes, data, self.import_attributes)
|
||||
props.active_task_time_id = task_time_id
|
||||
props.active_task_id = self.task
|
||||
props.editing_task_type = "TASKTIME"
|
||||
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):
|
||||
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.file.by_id(self.task))
|
||||
Data.load(IfcStore.get_file())
|
||||
@@ -479,20 +454,7 @@ class EditTaskTime(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMWorkScheduleProperties
|
||||
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 = blenderbim.bim.helper.export_attributes(props.task_time_attributes, self.export_attributes)
|
||||
attributes = self.convert_strings_to_date_times(attributes)
|
||||
|
||||
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)
|
||||
return {"FINISHED"}
|
||||
|
||||
def convert_strings_to_date_times(self, attributes):
|
||||
for key, value in attributes.items():
|
||||
if not value:
|
||||
continue
|
||||
if "Start" in key or "Finish" in key or key == "StatusTime":
|
||||
try:
|
||||
attributes[key] = parser.isoparse(value)
|
||||
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
|
||||
def export_attributes(self, attributes, prop):
|
||||
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
|
||||
attributes[prop.name] = helper.parse_datetime(value)
|
||||
return True
|
||||
elif prop.name == "ScheduleDuration":
|
||||
attributes[prop.name] = helper.parse_duration(value)
|
||||
return True
|
||||
|
||||
|
||||
class EnableEditingTask(bpy.types.Operator):
|
||||
@@ -535,25 +489,7 @@ class EnableEditingTask(bpy.types.Operator):
|
||||
|
||||
data = Data.tasks[self.task]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcTask").all_attributes():
|
||||
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()]
|
||||
blenderbim.bim.helper.import_attributes("IfcTask", props.task_attributes, data)
|
||||
props.active_task_id = self.task
|
||||
props.editing_task_type = "ATTRIBUTES"
|
||||
return {"FINISHED"}
|
||||
@@ -575,19 +511,7 @@ class EditTask(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMWorkScheduleProperties
|
||||
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
|
||||
attributes = blenderbim.bim.export_attributes(props.task_attributes)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"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):
|
||||
props = context.scene.BIMWorkCalendarProperties
|
||||
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
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.work_calendar_attributes)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_work_calendar",
|
||||
@@ -819,21 +735,7 @@ class EnableEditingWorkCalendar(bpy.types.Operator):
|
||||
|
||||
data = Data.work_calendars[self.work_calendar]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkCalendar").all_attributes():
|
||||
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()]
|
||||
blenderbim.bim.helper.import_attributes("IfcWorkCalendar", self.props.work_calendar_attributes, data)
|
||||
self.props.active_work_calendar_id = self.work_calendar
|
||||
self.props.editing_type = "ATTRIBUTES"
|
||||
return {"FINISHED"}
|
||||
@@ -869,6 +771,27 @@ class ImportP6(bpy.types.Operator, ImportHelper):
|
||||
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):
|
||||
bl_idname = "bim.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]
|
||||
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkTime").all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
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()]
|
||||
blenderbim.bim.helper.import_attributes(
|
||||
"IfcWorkTime", self.props.work_time_attributes, data, self.import_attributes
|
||||
)
|
||||
|
||||
self.initialise_recurrence_components()
|
||||
self.load_recurrence_pattern_data(data)
|
||||
self.props.active_work_time_id = self.work_time
|
||||
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):
|
||||
if len(self.props.day_components) == 0:
|
||||
for i in range(0, 31):
|
||||
@@ -988,15 +902,7 @@ class EditWorkTime(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMWorkCalendarProperties
|
||||
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
|
||||
attributes = blenderbim.bim.helper.export_attributes(self.props.work_time_attributes)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_work_time",
|
||||
@@ -1163,6 +1069,7 @@ class EditTaskCalendar(bpy.types.Operator):
|
||||
},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_task_properties(task=self.task)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1183,6 +1090,7 @@ class RemoveTaskCalendar(bpy.types.Operator):
|
||||
},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_task_properties(task=self.task)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1225,21 +1133,7 @@ class EnableEditingSequenceAttributes(bpy.types.Operator):
|
||||
|
||||
def enable_editing_sequence_attributes(self):
|
||||
data = Data.sequences[self.sequence]
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcRelSequence").all_attributes():
|
||||
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()]
|
||||
blenderbim.bim.helper.import_attributes("IfcRelSequence", self.props.sequence_attributes, data)
|
||||
|
||||
|
||||
class EnableEditingSequenceTimeLag(bpy.types.Operator):
|
||||
@@ -1259,34 +1153,22 @@ class EnableEditingSequenceTimeLag(bpy.types.Operator):
|
||||
|
||||
def enable_editing_attributes(self):
|
||||
data = Data.lag_times[self.lag_time]
|
||||
for attribute in IfcStore.get_schema().declaration_by_name("IfcLagTime").all_attributes():
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
|
||||
if data_type == "entity":
|
||||
continue
|
||||
new = self.props.time_lag_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() == "LagValue":
|
||||
if isinstance(data[attribute.name()], isodate.Duration):
|
||||
new.data_type = "string"
|
||||
new.string_value = (
|
||||
""
|
||||
if new.is_null
|
||||
else ifcopenshell.util.date.datetime2ifc(data[attribute.name()], "IfcDuration")
|
||||
)
|
||||
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()]
|
||||
blenderbim.bim.helper.import_attributes(
|
||||
"IfcLagTime", self.props.time_lag_attributes, data, self.import_attributes
|
||||
)
|
||||
|
||||
def import_attributes(self, name, prop, data):
|
||||
if name == "LagValue":
|
||||
if isinstance(data[name], isodate.Duration):
|
||||
prop.data_type = "string"
|
||||
prop.string_value = (
|
||||
"" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration")
|
||||
)
|
||||
return True
|
||||
else:
|
||||
prop.data_type = "float"
|
||||
prop.float_value = 0.0 if prop.is_null else data[name]
|
||||
return True
|
||||
|
||||
|
||||
class UnassignLagTime(bpy.types.Operator):
|
||||
@@ -1325,15 +1207,7 @@ class EditSequenceAttributes(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMWorkScheduleProperties
|
||||
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
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.sequence_attributes)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_sequence",
|
||||
@@ -1352,17 +1226,7 @@ class EditSequenceTimeLag(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMWorkScheduleProperties
|
||||
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
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.time_lag_attributes)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"sequence.edit_lag_time",
|
||||
|
||||
@@ -159,6 +159,7 @@ class Task(PropertyGroup):
|
||||
derived_start: StringProperty(name="Derived Start")
|
||||
derived_finish: StringProperty(name="Derived Finish")
|
||||
derived_duration: StringProperty(name="Derived Duration")
|
||||
calendar: StringProperty(name="Calendar")
|
||||
is_predecessor: BoolProperty(name="Is Predecessor")
|
||||
is_successor: BoolProperty(name="Is Successor")
|
||||
|
||||
@@ -189,6 +190,7 @@ class BIMWorkScheduleProperties(PropertyGroup):
|
||||
task_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
|
||||
should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", 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")
|
||||
task_time_attributes: CollectionProperty(name="Task Time Attributes", type=Attribute)
|
||||
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")
|
||||
elif self.props.editing_type == "TASKS":
|
||||
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.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
|
||||
@@ -336,6 +337,9 @@ class BIM_UL_tasks(UIList):
|
||||
else:
|
||||
row.prop(item, "duration", emboss=False, text="")
|
||||
|
||||
if props.should_show_calendars:
|
||||
row.label(text=item.calendar)
|
||||
|
||||
if context.active_object:
|
||||
oprops = context.active_object.BIMObjectProperties
|
||||
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.editing_task_type == "TASKTIME":
|
||||
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":
|
||||
row.operator("bim.edit_task", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_task", text="", icon="CANCEL")
|
||||
|
||||
@@ -20,22 +20,33 @@ classes = (
|
||||
operator.EnableEditingStructuralConnectionCondition,
|
||||
operator.DisableEditingStructuralConnectionCondition,
|
||||
operator.RemoveStructuralConnectionCondition,
|
||||
operator.EnableEditingStructuralMemberAxis,
|
||||
operator.DisableEditingStructuralMemberAxis,
|
||||
operator.EditStructuralMemberAxis,
|
||||
operator.EnableEditingStructuralItemAxis,
|
||||
operator.DisableEditingStructuralItemAxis,
|
||||
operator.EditStructuralItemAxis,
|
||||
operator.EnableEditingStructuralItemConnectionCS,
|
||||
operator.DisableEditingStructuralItemConnectionCS,
|
||||
operator.EditStructuralItemConnectionCS,
|
||||
operator.AddStructuralLoadCase,
|
||||
operator.EditStructuralLoadCase,
|
||||
operator.RemoveStructuralLoadCase,
|
||||
operator.AddStructuralLoadGroup,
|
||||
operator.RemoveStructuralLoadGroup,
|
||||
operator.AddStructuralActivity,
|
||||
operator.EnableEditingStructuralLoadCase,
|
||||
operator.EnableEditingStructuralLoadCaseGroups,
|
||||
operator.DisableEditingStructuralLoadCase,
|
||||
operator.EnableEditingStructuralLoadGroupActivities,
|
||||
prop.StructuralAnalysisModel,
|
||||
prop.StructuralActivity,
|
||||
prop.BIMStructuralProperties,
|
||||
prop.BIMObjectStructuralProperties,
|
||||
ui.BIM_PT_structural_analysis_models,
|
||||
ui.BIM_PT_structural_boundary_conditions,
|
||||
ui.BIM_PT_connected_structural_members,
|
||||
ui.BIM_PT_structural_member,
|
||||
ui.BIM_PT_structural_connection,
|
||||
ui.BIM_UL_structural_analysis_models,
|
||||
ui.BIM_UL_structural_activities,
|
||||
ui.BIM_PT_structural_load_cases,
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import blenderbim.bim.helper
|
||||
from math import degrees
|
||||
from mathutils import Vector, Matrix
|
||||
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),
|
||||
"attributes": attributes,
|
||||
}
|
||||
},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_structural_analysis_models()
|
||||
@@ -277,7 +278,7 @@ class RemoveStructuralAnalysisModel(bpy.types.Operator):
|
||||
ifcopenshell.api.run(
|
||||
"structural.remove_structural_analysis_model",
|
||||
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())
|
||||
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),
|
||||
"structural_analysis_model": self.file.by_id(self.structural_analysis_model),
|
||||
}
|
||||
},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
@@ -361,15 +362,15 @@ class UnassignStructuralAnalysisModel(bpy.types.Operator):
|
||||
**{
|
||||
"product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id),
|
||||
"structural_analysis_model": self.file.by_id(self.structural_analysis_model),
|
||||
}
|
||||
},
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingStructuralMemberAxis(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_structural_member_axis"
|
||||
bl_label = "Enable Editing Structural Member Axis"
|
||||
class EnableEditingStructuralItemAxis(bpy.types.Operator):
|
||||
bl_idname = "bim.enable_editing_structural_item_axis"
|
||||
bl_label = "Enable Editing Structural Item Axis"
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
@@ -377,20 +378,22 @@ class EnableEditingStructuralMemberAxis(bpy.types.Operator):
|
||||
props = obj.BIMStructuralProperties
|
||||
|
||||
self.file = IfcStore.get_file()
|
||||
member = self.file.by_id(oprops.ifc_definition_id)
|
||||
z_axis = Vector(member.Axis.DirectionRatios).normalized() @ obj.matrix_world if member.Axis else None
|
||||
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("Member Axis", None)
|
||||
empty = bpy.data.objects.new("Item Axis", 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),
|
||||
))
|
||||
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"
|
||||
@@ -404,9 +407,9 @@ class EnableEditingStructuralMemberAxis(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class DisableEditingStructuralMemberAxis(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_structural_member_axis"
|
||||
bl_label = "Disable Editing Structural Member Axis"
|
||||
class DisableEditingStructuralItemAxis(bpy.types.Operator):
|
||||
bl_idname = "bim.disable_editing_structural_item_axis"
|
||||
bl_label = "Disable Editing Structural Item Axis"
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
@@ -417,9 +420,9 @@ class DisableEditingStructuralMemberAxis(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditStructuralMemberAxis(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_structural_member_axis"
|
||||
bl_label = "Edit Structural Member Axis"
|
||||
class EditStructuralItemAxis(bpy.types.Operator):
|
||||
bl_idname = "bim.edit_structural_item_axis"
|
||||
bl_label = "Edit Structural Item Axis"
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
@@ -429,12 +432,85 @@ class EditStructuralMemberAxis(bpy.types.Operator):
|
||||
z_axis = relative_matrix.col[2][0:3]
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"structural.edit_structural_member_axis",
|
||||
"structural.edit_structural_item_axis",
|
||||
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,
|
||||
)
|
||||
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"}
|
||||
|
||||
|
||||
@@ -494,15 +570,7 @@ class EditStructuralLoadCase(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
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
|
||||
attributes = blenderbim.bim.helper.export_attributes(props.load_case_attributes)
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"structural.edit_structural_load_case",
|
||||
@@ -536,33 +604,18 @@ class EnableEditingStructuralLoadCase(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props.active_load_case_id = self.load_case
|
||||
self.props.load_case_editing_type = "ATTRIBUTES"
|
||||
while len(self.props.load_case_attributes) > 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"}
|
||||
|
||||
def enable_editing_structural_load_case(self):
|
||||
data = Data.load_cases[self.load_case]
|
||||
print(data)
|
||||
|
||||
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()]
|
||||
def import_attributes(self, name, prop, data):
|
||||
if name in ["SelfWeightCoefficients"]:
|
||||
return False
|
||||
|
||||
|
||||
class DisableEditingStructuralLoadCase(bpy.types.Operator):
|
||||
@@ -571,4 +624,109 @@ class DisableEditingStructuralLoadCase(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
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
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from math import radians
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
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):
|
||||
if not self.axis_empty:
|
||||
return
|
||||
@@ -27,11 +50,29 @@ def updateAxisAngle(self, context):
|
||||
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):
|
||||
name: StringProperty(name="Name")
|
||||
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):
|
||||
structural_analysis_model_attributes: CollectionProperty(
|
||||
name="Structural Analysis Model Attributes", type=Attribute
|
||||
@@ -40,12 +81,17 @@ class BIMStructuralProperties(PropertyGroup):
|
||||
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_id: IntProperty(name="Active Structural Analysis Model Id")
|
||||
|
||||
# editing_type: StringProperty(name="Editing Type")
|
||||
# active_load_case_index: IntProperty(name="Active Work Schedules Index")
|
||||
|
||||
load_case_editing_type: StringProperty(name="Load Case Editing Type")
|
||||
load_case_attributes: CollectionProperty(name="Load Case Attributes", type=Attribute)
|
||||
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):
|
||||
@@ -58,3 +104,8 @@ class BIMObjectStructuralProperties(PropertyGroup):
|
||||
axis_empty: PointerProperty(name="Axis Empty", 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 blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.structural.data import Data
|
||||
@@ -169,11 +170,63 @@ class BIM_PT_structural_member(Panel):
|
||||
if self.props.is_editing_axis:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "axis_angle")
|
||||
row.operator("bim.edit_structural_member_axis", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_structural_member_axis", text="", icon="CANCEL")
|
||||
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_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:
|
||||
row = self.layout.row()
|
||||
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")
|
||||
|
||||
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")
|
||||
elif self.props.active_load_case_id:
|
||||
row.operator("bim.remove_structural_load_case", text="", icon="X").load_case = load_case_id
|
||||
else:
|
||||
row.operator(
|
||||
"bim.enable_editing_structural_load_case_groups", text="", icon="GHOST_ENABLED"
|
||||
).load_case = load_case_id
|
||||
row.operator(
|
||||
"bim.enable_editing_structural_load_case", text="", icon="GREASEPENCIL"
|
||||
).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:
|
||||
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):
|
||||
for attribute in self.props.load_case_attributes:
|
||||
row = self.layout.row(align=True)
|
||||
if attribute.data_type == "string":
|
||||
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":
|
||||
row.prop(attribute, "enum_value", text=attribute.name)
|
||||
if attribute.is_optional:
|
||||
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)
|
||||
|
||||
@@ -166,8 +166,11 @@ namespace IfcGeom {
|
||||
|
||||
std::vector<T> ts;
|
||||
|
||||
if (IfcGeom::Kernel::count(s, TopAbs_SHELL) == 0) {
|
||||
return ts;
|
||||
if (extend < 0.) {
|
||||
// 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);
|
||||
@@ -288,7 +291,9 @@ namespace IfcGeom {
|
||||
if (it.initialize()) {
|
||||
do {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user