merage master v6.0.0

This commit is contained in:
admin
2021-04-12 10:26:10 +08:00
parent 72fdca9e82
commit 397b6ae5fe
52 changed files with 1415 additions and 195 deletions
+3 -3
View File
@@ -52,10 +52,10 @@ endif
# Provides IfcOpenShell Python functionality
ifeq ($(PYVERSION), py37)
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-517b819-$(PLATFORM)64.zip
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-37-v0.6.0-ff7219b-$(PLATFORM)64.zip
endif
ifeq ($(PYVERSION), py39)
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-517b819-$(PLATFORM)64.zip
cd dist/working && wget https://s3.amazonaws.com/ifcopenshell-builds/ifcblender-python-39-v0.6.0-ff7219b-$(PLATFORM)64.zip
endif
cd dist/working && unzip ifcblender*
cp -r dist/working/io_import_scene_ifc/ifcopenshell dist/blenderbim/libs/site/packages/
@@ -142,7 +142,7 @@ endif
cd dist/working && unzip v0.6.0.zip
# IfcOpenBot sometimes lags behind, so we hotfix the Python utilities
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/util/* dist/blenderbim/libs/site/packages/ifcopenshell/util/
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/api dist/blenderbim/libs/site/packages/ifcopenshell/api
cp -r dist/working/IfcOpenShell-0.6.0/src/ifcopenshell-python/ifcopenshell/api/* dist/blenderbim/libs/site/packages/ifcopenshell/api/
# Provides bcf functionality
cp -r dist/working/IfcOpenShell-0.6.0/src/bcf/bcf dist/blenderbim/libs/site/packages/
# Provides IFCClash functionality
@@ -25,6 +25,7 @@ if bpy is not None:
"geometry": None,
"cobie": None,
"sequence": None,
"cost": None,
"group": None,
"structural": None,
"material": None,
+1 -1
View File
@@ -90,7 +90,7 @@ class IfcExporter:
for guid in to_delete:
product = self.file.by_id(guid)
IfcStore.unlink_element(product)
ifcopenshell.api.run("remove_product", self.file, **{"product": product})
ifcopenshell.api.run("root.remove_product", self.file, **{"product": product})
def sync_edited_objects(self):
for obj_name in IfcStore.edited_objs.copy():
+5 -1
View File
@@ -414,7 +414,11 @@ class IfcImporter:
self.exclude_elements |= self.native_elements
def is_native(self, element):
if not element.Representation or not element.Representation.Representations or element.HasOpenings:
if (
not element.Representation
or not element.Representation.Representations
or getattr(element, "HasOpenings", None)
):
return
representations = self.get_transformed_body_representations(element.Representation.Representations)
@@ -205,7 +205,8 @@ class Helper:
for edge in bm.edges:
edge_vector = edge.verts[1].co - edge.verts[0].co
unshared_verts = set(edge.verts) - face_verts_set
if len(unshared_verts) == 1 and not (edge_vector.angle(profile_face.normal) - pi / 2 < 0.001):
angle_to_normal = edge_vector.angle(profile_face.normal)
if len(unshared_verts) == 1 and (angle_to_normal < 0.001 or angle_to_normal - pi < 0.001):
if unshared_verts.pop() == edge.verts[1]:
return [edge.verts[0].index, edge.verts[1].index]
return [edge.verts[1].index, edge.verts[0].index]
@@ -398,7 +398,7 @@ class UpdateMeshRepresentation(bpy.types.Operator):
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
bpy.ops.bim.remove_representation(representation_id=old_representation.id())
bpy.ops.bim.remove_representation(representation_id=old_representation.id(), obj=obj.name)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
@@ -5,8 +5,10 @@ classes = (
operator.EnableEditingGeoreferencing,
operator.DisableEditingGeoreferencing,
operator.EditGeoreferencing,
operator.SetNorthOffset,
operator.GetNorthOffset,
operator.SetIfcGridNorth,
operator.SetBlenderGridNorth,
operator.SetIfcTrueNorth,
operator.SetBlenderTrueNorth,
operator.RemoveGeoreferencing,
operator.AddGeoreferencing,
operator.ConvertLocalToGlobal,
@@ -17,27 +17,6 @@ class EnableEditingGeoreferencing(bpy.types.Operator):
self.file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
while len(props.map_conversion) > 0:
props.map_conversion.remove(0)
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.map_conversion.add()
new.name = attribute.name()
new.is_null = Data.map_conversion[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.map_conversion[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else Data.map_conversion[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else Data.map_conversion[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else Data.map_conversion[attribute.name()]
while len(props.projected_crs) > 0:
props.projected_crs.remove(0)
@@ -69,6 +48,27 @@ class EnableEditingGeoreferencing(bpy.types.Operator):
elif props.map_unit_type == "IfcConversionBasedUnit":
props.map_unit_imperial = Data.projected_crs["MapUnit"]["Name"]
while len(props.map_conversion) > 0:
props.map_conversion.remove(0)
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or data_type == "select":
continue
print(attribute.name(), data_type)
new = props.map_conversion.add()
new.name = attribute.name()
new.is_null = Data.map_conversion[attribute.name()] is None
new.is_optional = attribute.optional()
# Enforce a string data type to prevent data loss in singpe-precision Blender props
new.data_type = "string"
new.string_value = "" if new.is_null else str(Data.map_conversion[attribute.name()])
props.has_true_north = bool(Data.true_north)
if Data.true_north:
props.true_north_abscissa = str(Data.true_north[0])
props.true_north_ordinate = str(Data.true_north[1])
props.is_editing = True
return {"FINISHED"}
@@ -91,23 +91,6 @@ class EditGeoreferencing(bpy.types.Operator):
self.file = IfcStore.get_file()
props = context.scene.BIMGeoreferenceProperties
map_conversion = {}
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
blender_attribute = props.map_conversion.get(attribute.name())
if blender_attribute.is_null:
map_conversion[attribute.name()] = None
elif blender_attribute.data_type == "string":
map_conversion[attribute.name()] = blender_attribute.string_value
elif blender_attribute.data_type == "float":
map_conversion[attribute.name()] = blender_attribute.float_value
elif blender_attribute.data_type == "integer":
map_conversion[attribute.name()] = blender_attribute.int_value
elif blender_attribute.data_type == "boolean":
map_conversion[attribute.name()] = blender_attribute.bool_value
projected_crs = {}
for attribute in IfcStore.get_schema().declaration_by_name("IfcProjectedCRS").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
@@ -129,38 +112,87 @@ class EditGeoreferencing(bpy.types.Operator):
if not props.is_map_unit_null:
map_unit = props.map_unit_si if props.map_unit_type == "IfcSIUnit" else props.map_unit_imperial
map_conversion = {}
for attribute in IfcStore.get_schema().declaration_by_name("IfcMapConversion").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity" or data_type == "select":
continue
blender_attribute = props.map_conversion.get(attribute.name())
if blender_attribute.is_null:
map_conversion[attribute.name()] = None
elif blender_attribute.data_type == "string":
# We store our floats as string to prevent single precision data loss
map_conversion[attribute.name()] = float(blender_attribute.string_value)
true_north = None
if props.has_true_north:
try:
true_north = [float(props.true_north_abscissa), float(props.true_north_ordinate)]
except:
pass
ifcopenshell.api.run(
"georeference.edit_georeferencing",
self.file,
**{"map_conversion": map_conversion, "projected_crs": projected_crs, "map_unit": map_unit}
**{
"map_conversion": map_conversion,
"projected_crs": projected_crs,
"map_unit": map_unit,
"true_north": true_north,
}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_georeferencing()
return {"FINISHED"}
class SetNorthOffset(bpy.types.Operator):
bl_idname = "bim.set_north_offset"
bl_label = "Set North Offset"
class SetBlenderGridNorth(bpy.types.Operator):
bl_idname = "bim.set_blender_grid_north"
bl_label = "Set Blender Grid North"
def execute(self, context):
context.scene.sun_pos_properties.north_offset = -radians(
ifcopenshell.util.geolocation.xy2angle(
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").float_value,
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").float_value,
ifcopenshell.util.geolocation.xaxis2angle(
float(context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").string_value),
float(context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").string_value),
)
)
return {"FINISHED"}
class GetNorthOffset(bpy.types.Operator):
bl_idname = "bim.get_north_offset"
bl_label = "Get North Offset"
class SetIfcGridNorth(bpy.types.Operator):
bl_idname = "bim.set_ifc_grid_north"
bl_label = "Set IFC Grid North"
def execute(self, context):
x_angle = -context.scene.sun_pos_properties.north_offset
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").float_value = cos(x_angle)
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").float_value = sin(x_angle)
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisAbscissa").string_value = str(cos(x_angle))
context.scene.BIMGeoreferenceProperties.map_conversion.get("XAxisOrdinate").string_value = str(sin(x_angle))
return {"FINISHED"}
class SetBlenderTrueNorth(bpy.types.Operator):
bl_idname = "bim.set_blender_true_north"
bl_label = "Set Blender True North"
def execute(self, context):
context.scene.sun_pos_properties.north_offset = -radians(
ifcopenshell.util.geolocation.yaxis2angle(
float(context.scene.BIMGeoreferenceProperties.true_north_abscissa),
float(context.scene.BIMGeoreferenceProperties.true_north_ordinate),
)
)
return {"FINISHED"}
class SetIfcTrueNorth(bpy.types.Operator):
bl_idname = "bim.set_ifc_true_north"
bl_label = "Set IFC True North"
def execute(self, context):
y_angle = -context.scene.sun_pos_properties.north_offset + radians(90)
context.scene.BIMGeoreferenceProperties.true_north_abscissa = str(cos(y_angle))
context.scene.BIMGeoreferenceProperties.true_north_ordinate = str(sin(y_angle))
return {"FINISHED"}
@@ -46,3 +46,6 @@ class BIMGeoreferenceProperties(PropertyGroup):
blender_orthogonal_height: StringProperty(name="Blender Orthogonal Height", default="0")
blender_x_axis_abscissa: StringProperty(name="Blender X Axis Abscissa", default="1")
blender_x_axis_ordinate: StringProperty(name="Blender X Axis Ordinate", default="0")
has_true_north: BoolProperty(name="Has True North", default=True)
true_north_abscissa: StringProperty(name="True North Abscissa")
true_north_ordinate: StringProperty(name="True North Ordinate")
@@ -1,7 +1,9 @@
import ifcopenshell.util.geolocation
from bpy.types import Panel
from ifcopenshell.api.georeference.data import Data
from blenderbim.bim.ifc import IfcStore
class BIM_PT_gis(Panel):
bl_label = "IFC Georeferencing"
bl_idname = "BIM_PT_gis"
@@ -15,6 +17,8 @@ class BIM_PT_gis(Panel):
return IfcStore.get_file()
def draw(self, context):
self.layout.use_property_split = True
self.layout.use_property_decorate = False
props = context.scene.BIMGeoreferenceProperties
if not Data.is_loaded:
Data.load(IfcStore.get_file())
@@ -26,30 +30,10 @@ class BIM_PT_gis(Panel):
def draw_editable_ui(self, context):
props = context.scene.BIMGeoreferenceProperties
row = self.layout.row(align=True)
row.label(text="Map Conversion", icon="GRID")
row.label(text="Projected CRS", icon="WORLD")
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
row.operator("bim.disable_editing_georeferencing", icon="X", text="")
for attribute in props.map_conversion:
if attribute.name == "XAxisAbscissa" and hasattr(context.scene, "sun_pos_properties"):
row = self.layout.row(align=True)
row.operator("bim.get_north_offset", text="Set IFC North")
row.operator("bim.set_north_offset", text="Set Blender North")
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
row = self.layout.row(align=True)
row.label(text="Projected CRS", icon="WORLD")
for attribute in props.projected_crs:
row = self.layout.row(align=True)
if attribute.data_type == "string":
@@ -70,10 +54,43 @@ class BIM_PT_gis(Panel):
row.prop(props, "map_unit_imperial", text="")
row.prop(props, "is_map_unit_null", icon="RADIOBUT_OFF" if props.is_map_unit_null else "RADIOBUT_ON", text="")
row = self.layout.row()
row.label(text="Map Conversion", icon="GRID")
for attribute in props.map_conversion:
if attribute.name == "Scale" and hasattr(context.scene, "sun_pos_properties"):
row = self.layout.row(align=True)
row.operator("bim.set_ifc_grid_north", text="Set IFC North")
row.operator("bim.set_blender_grid_north", text="Set Blender North")
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_value", text=attribute.name)
if attribute.is_optional:
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
row = self.layout.row()
row.label(text="True North", icon="LIGHT_SUN")
row = self.layout.row()
row.prop(props, "has_true_north")
row = self.layout.row()
row.prop(props, "true_north_abscissa")
row = self.layout.row()
row.prop(props, "true_north_ordinate")
if hasattr(context.scene, "sun_pos_properties"):
row = self.layout.row(align=True)
row.operator("bim.set_ifc_true_north", text="Set IFC North")
row.operator("bim.set_blender_true_north", text="Set Blender North")
def draw_ui(self, context):
props = context.scene.BIMGeoreferenceProperties
if not Data.map_conversion and IfcStore.get_file().schema != "IFC2X3":
if not Data.projected_crs and IfcStore.get_file().schema != "IFC2X3":
row = self.layout.row(align=True)
row.label(text="Not Georeferenced")
row.operator("bim.add_georeferencing", icon="ADD", text="")
@@ -100,26 +117,26 @@ class BIM_PT_gis(Panel):
row = self.layout.row(align=True)
row.label(text="XAxisOrdinate")
row.label(text=props.blender_x_axis_ordinate)
row.label(text="Derived Grid North")
row.label(
text=str(
round(
ifcopenshell.util.geolocation.xaxis2angle(
float(props.blender_x_axis_abscissa), float(props.blender_x_axis_ordinate)
),
3,
)
)
)
elif IfcStore.get_file().schema == "IFC2X3":
row = self.layout.row()
row.label(text="Not Georeferenced")
if Data.map_conversion:
row = self.layout.row(align=True)
row.label(text="Map Conversion", icon="GRID")
row.operator("bim.enable_editing_georeferencing", icon="GREASEPENCIL", text="")
row.operator("bim.remove_georeferencing", icon="X", text="")
for key, value in Data.map_conversion.items():
if key == "id" or key == "type" or key == "SourceCRS" or key == "TargetCRS" or not value:
continue
row = self.layout.row(align=True)
row.label(text=key)
row.label(text=str(value))
if Data.projected_crs:
row = self.layout.row(align=True)
row.label(text="Projected CRS", icon="WORLD")
row.operator("bim.enable_editing_georeferencing", icon="GREASEPENCIL", text="")
row.operator("bim.remove_georeferencing", icon="X", text="")
for key, value in Data.projected_crs.items():
if key == "id" or key == "type" or not value:
@@ -132,6 +149,40 @@ class BIM_PT_gis(Panel):
row.label(text=key)
row.label(text=str(value))
if Data.map_conversion:
row = self.layout.row(align=True)
row.label(text="Map Conversion", icon="GRID")
for key, value in Data.map_conversion.items():
if key == "id" or key == "type" or key == "SourceCRS" or key == "TargetCRS" or value is None:
continue
row = self.layout.row(align=True)
row.label(text=key)
row.label(text=str(value))
if key == "XAxisOrdinate":
row = self.layout.row(align=True)
row.label(text="Derived Angle")
row.label(
text=str(
round(
ifcopenshell.util.geolocation.xaxis2angle(
Data.map_conversion["XAxisAbscissa"], Data.map_conversion["XAxisOrdinate"]
),
3,
)
)
)
if Data.true_north:
row = self.layout.row()
row.label(text="True North", icon="LIGHT_SUN")
row = self.layout.row(align=True)
row.label(text="Vector")
row.label(text=str(Data.true_north[0:2])[1:-1])
row = self.layout.row(align=True)
row.label(text="Derived Angle")
row.label(text=str(round(ifcopenshell.util.geolocation.yaxis2angle(*Data.true_north[0:2]), 3)))
class BIM_PT_gis_utilities(Panel):
bl_idname = "BIM_PT_gis_utilities"
@@ -47,7 +47,7 @@ class EnableEditingLayer(bpy.types.Operator):
for attribute in IfcStore.get_schema().declaration_by_name("IfcPresentationLayerAssignment").all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
if data_type == "entity" or data_type == "select":
continue
new = props.layer_attributes.add()
new.name = attribute.name()
@@ -8,6 +8,9 @@ classes = (
operator.UnassignMaterial,
operator.AddConstituent,
operator.RemoveConstituent,
operator.AddProfile,
operator.RemoveProfile,
operator.AssignParameterizedProfile,
operator.AddLayer,
operator.RemoveLayer,
operator.ReorderMaterialSetItem,
@@ -1,8 +1,36 @@
import bpy
import json
import ifcopenshell.api
import ifcopenshell.util.attribute
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.material.data import Data
from ifcopenshell.api.profile.data import Data as ProfileData
class AssignParameterizedProfile(bpy.types.Operator):
bl_idname = "bim.assign_parameterized_profile"
bl_label = "Assign Parameterized Profile"
ifc_class: bpy.props.StringProperty()
material_profile: bpy.props.IntProperty()
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
profile = ifcopenshell.api.run(
"profile.add_parameterized_profile",
self.file,
**{"ifc_class": self.ifc_class},
)
ifcopenshell.api.run(
"material.assign_profile",
self.file,
**{"material_profile": self.file.by_id(self.material_profile), "profile": profile}
)
Data.load_profiles()
ProfileData.load(self.file)
bpy.ops.bim.enable_editing_material_set_item(obj=obj.name, material_set_item=self.material_profile)
return {"FINISHED"}
class AddMaterial(bpy.types.Operator):
@@ -115,6 +143,43 @@ class RemoveConstituent(bpy.types.Operator):
return {"FINISHED"}
class AddProfile(bpy.types.Operator):
bl_idname = "bim.add_profile"
bl_label = "Add Profile"
obj: bpy.props.StringProperty()
profile_set: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"material.add_profile",
self.file,
**{
"profile_set": self.file.by_id(self.profile_set),
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
},
)
Data.load_profiles()
return {"FINISHED"}
class RemoveProfile(bpy.types.Operator):
bl_idname = "bim.remove_profile"
bl_label = "Remove Profile"
obj: bpy.props.StringProperty()
profile: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"material.remove_profile", self.file, **{"profile": self.file.by_id(self.profile)}
)
Data.load_profiles()
return {"FINISHED"}
class AddLayer(bpy.types.Operator):
bl_idname = "bim.add_layer"
bl_label = "Add Layer"
@@ -333,8 +398,8 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj) if self.obj else bpy.context.active_object
props = obj.BIMObjectMaterialProperties
props.active_material_set_item_id = self.material_set_item
self.props = obj.BIMObjectMaterialProperties
self.props.active_material_set_item_id = self.material_set_item
product_data = Data.products[obj.BIMObjectProperties.ifc_definition_id]
material_set_item = self.file.by_id(self.material_set_item)
@@ -347,17 +412,24 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
else:
material_set_item_data = {}
props.material_set_item_material = str(material_set_item_data["Material"])
self.props.material_set_item_material = str(material_set_item_data["Material"])
while len(props.material_set_item_attributes) > 0:
props.material_set_item_attributes.remove(0)
self.load_set_item_attributes(material_set_item, material_set_item_data)
if material_set_item.is_a("IfcMaterialProfile"):
self.load_profile_attributes(material_set_item, material_set_item_data)
return {"FINISHED"}
def load_set_item_attributes(self, material_set_item, material_set_item_data):
while len(self.props.material_set_item_attributes) > 0:
self.props.material_set_item_attributes.remove(0)
for attribute in IfcStore.get_schema().declaration_by_name(material_set_item.is_a()).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
if attribute.name() in material_set_item_data:
new = props.material_set_item_attributes.add()
new = self.props.material_set_item_attributes.add()
new.name = attribute.name()
new.is_null = material_set_item_data[attribute.name()] is None
new.data_type = data_type
@@ -369,7 +441,45 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
new.int_value = 0 if new.is_null else material_set_item_data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else material_set_item_data[attribute.name()]
return {"FINISHED"}
def load_profile_attributes(self, material_set_item, material_set_item_data):
while len(self.props.material_set_item_profile_attributes) > 0:
self.props.material_set_item_profile_attributes.remove(0)
if not material_set_item_data["Profile"]:
return
profile = self.file.by_id(material_set_item_data["Profile"])
profile_data = ProfileData.profiles[material_set_item_data["Profile"]]
for attribute in IfcStore.get_schema().declaration_by_name(profile.is_a()).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
if attribute.name() in profile_data:
new = self.props.material_set_item_profile_attributes.add()
new.name = attribute.name()
new.is_null = profile_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 profile_data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else profile_data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else profile_data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else profile_data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if profile_data[attribute.name()]:
new.enum_value = profile_data[attribute.name()]
# Force null to be false if the attribute is mandatory because when we first assign a profile, all of
# its fields are null (which is illegal).
# TODO: find a better solution.
if not new.is_optional:
new.is_null = False
class DisableEditingMaterialSetItem(bpy.types.Operator):
@@ -431,16 +541,31 @@ class EditMaterialSetItem(bpy.types.Operator):
)
Data.load_layers()
elif product_data["type"] == "IfcMaterialProfileSet":
profile_attributes = {}
for attribute in props.material_set_item_profile_attributes:
if attribute.data_type == "string":
value = attribute.string_value
elif attribute.data_type == "float":
value = attribute.float_value
elif attribute.data_type == "integer":
value = attribute.int_value
elif attribute.data_type == "boolean":
value = attribute.bool_value
elif attribute.data_type == "enum":
value = attribute.enum_value
profile_attributes[attribute.name] = None if attribute.is_null else value
ifcopenshell.api.run(
"material.edit_profile",
self.file,
**{
"profile": self.file.by_id(self.material_set_item),
"attributes": attributes,
"profile_attributes": profile_attributes,
"material": self.file.by_id(int(obj.BIMObjectMaterialProperties.material_set_item_material)),
},
)
Data.load_profiles()
ProfileData.load(self.file)
else:
pass
@@ -1,5 +1,5 @@
import bpy
import blenderbim.bim.schema # refactor
import blenderbim.bim.schema # refactor
from ifcopenshell.api.material.data import Data
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute
@@ -17,6 +17,29 @@ from bpy.props import (
materials_enum = []
materialtypes_enum = []
profileclasses_enum = []
parameterizedprofileclasses_enum = []
def getProfileClasses(self, context):
global profileclasses_enum
if len(profileclasses_enum) == 0 and IfcStore.get_schema():
profileclasses_enum.clear()
profileclasses_enum = [
(t.name(), t.name(), "") for t in IfcStore.get_schema().declaration_by_name("IfcProfileDef").subtypes()
]
return profileclasses_enum
def getParameterizedProfileClasses(self, context):
global parameterizedprofileclasses_enum
if len(parameterizedprofileclasses_enum) == 0 and IfcStore.get_schema():
parameterizedprofileclasses_enum.clear()
parameterizedprofileclasses_enum = [
(t.name(), t.name(), "")
for t in IfcStore.get_schema().declaration_by_name("IfcParameterizedProfileDef").subtypes()
]
return parameterizedprofileclasses_enum
def getMaterials(self, context):
@@ -52,4 +75,9 @@ class BIMObjectMaterialProperties(PropertyGroup):
material_set_attributes: CollectionProperty(name="Material Set Attributes", type=Attribute)
active_material_set_item_id: IntProperty(name="Active Material Set ID")
material_set_item_attributes: CollectionProperty(name="Material Set Item Attributes", type=Attribute)
material_set_item_profile_attributes: CollectionProperty(name="Material Set Item Profile Attributes", type=Attribute)
material_set_item_material: EnumProperty(items=getMaterials, name="Material")
profile_classes: EnumProperty(items=getProfileClasses, name="Profile Classes")
parameterized_profile_classes: EnumProperty(
items=getParameterizedProfileClasses, name="Parameterized Profile Classes"
)
@@ -1,5 +1,6 @@
from bpy.types import Panel
from ifcopenshell.api.material.data import Data
from ifcopenshell.api.profile.data import Data as ProfileData
from blenderbim.bim.ifc import IfcStore
@@ -46,6 +47,8 @@ class BIM_PT_object_material(Panel):
Data.load(IfcStore.get_file())
if self.oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id)
if not ProfileData.is_loaded:
ProfileData.load(self.file)
self.product_data = Data.products[self.oprops.ifc_definition_id]
if not Data.materials:
@@ -172,6 +175,39 @@ class BIM_PT_object_material(Panel):
row.prop(attribute, "bool_value", text=attribute.name)
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
if self.set_item_name == "profile":
self.draw_assign_profile_ui(box, item)
self.draw_editable_profile_ui(box, item)
def draw_assign_profile_ui(self, layout, item):
row = layout.row(align=True)
row.prop(self.props, "profile_classes", text="")
if self.props.profile_classes == "IfcParameterizedProfileDef":
row.prop(self.props, "parameterized_profile_classes", text="")
op = row.operator("bim.assign_parameterized_profile", icon="GREASEPENCIL" if item["Profile"] else "ADD", text="")
op.ifc_class = self.props.parameterized_profile_classes
op.material_profile = item["id"]
else:
# TODO: support non parametric profiles by showing a list of named profiles to select from, or an
# eyedropper to pick profile geometry from the scene
row.operator("bim.disable_editing_material_set_item", icon="X", text="")
def draw_editable_profile_ui(self, layout, item):
for attribute in self.props.material_set_item_profile_attributes:
row = layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_value", text=attribute.name)
elif attribute.data_type == "integer":
row.prop(attribute, "int_value", text=attribute.name)
elif attribute.data_type == "float":
row.prop(attribute, "float_value", text=attribute.name)
elif attribute.data_type == "boolean":
row.prop(attribute, "bool_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_read_only_set_item_ui(self, set_item_id, index, is_first=False, is_last=False):
if self.product_data["type"] == "IfcMaterialList":
item = Data.materials[set_item_id]
@@ -84,6 +84,8 @@ class PieAddOpening(bpy.types.Operator):
for obj in context.selected_objects:
if "IfcOpeningElement" in obj.name or not obj.BIMObjectProperties.ifc_definition_id:
opening_name = obj.name
elif len(obj.children) == 1 and not obj.children[0].BIMObjectProperties.ifc_definition_id:
opening_name = obj.children[0].name
else:
opj_name = obj.name
bpy.ops.bim.add_opening(obj=opj_name, opening=opening_name)
@@ -3,6 +3,7 @@ from . import ui, prop, operator
classes = (
operator.CreateProject,
operator.CreateProjectLibrary,
operator.ValidateIfcFile,
prop.BIMProjectProperties,
ui.BIM_PT_project,
@@ -53,6 +53,30 @@ class CreateProject(bpy.types.Operator):
return {"FINISHED"}
class CreateProjectLibrary(bpy.types.Operator):
bl_idname = "bim.create_project_library"
bl_label = "Create Project Library"
def execute(self, context):
self.file = IfcStore.get_file()
if self.file:
return {"FINISHED"}
IfcStore.file = ifcopenshell.api.run(
"project.create_file", **{"version": bpy.context.scene.BIMProperties.export_schema}
)
self.file = IfcStore.get_file()
if self.file.schema == "IFC2X3":
bpy.ops.bim.add_person()
bpy.ops.bim.add_organisation()
project_library = bpy.data.objects.new("My Project Library", None)
bpy.ops.bim.assign_class(obj=project_library.name, ifc_class="IfcProjectLibrary")
bpy.ops.bim.assign_unit()
return {"FINISHED"}
class ValidateIfcFile(bpy.types.Operator):
bl_idname = "bim.validate_ifc_file"
bl_label = "Validate IFC File"
@@ -63,3 +63,6 @@ class BIM_PT_project(Panel):
row.prop(props, "volume_unit", text="Volume Unit")
row = self.layout.row()
row.operator("bim.create_project")
if props.export_schema != "IFC2X3":
row = self.layout.row()
row.operator("bim.create_project_library")
@@ -2,21 +2,55 @@ import bpy
from . import ui, prop, operator
classes = (
operator.LoadWorkPlans,
operator.DisableWorkPlanEditingUI,
operator.AddWorkPlan,
operator.EditWorkPlan,
operator.RemoveWorkPlan,
operator.EnableEditingWorkPlan,
operator.DisableEditingWorkPlan,
operator.LoadWorkSchedules,
operator.DisableWorkScheduleEditingUI,
operator.AddWorkSchedule,
operator.EditWorkSchedule,
operator.RemoveWorkSchedule,
operator.EnableEditingWorkSchedule,
operator.DisableEditingWorkSchedule,
operator.LoadTasks,
operator.DisableTaskEditingUI,
operator.AddWorkPlan,
operator.RemoveWorkPlan,
operator.LoadWorkCalendars,
operator.DisableWorkCalendarEditingUI,
operator.AddWorkCalendar,
operator.EditWorkCalendar,
operator.RemoveWorkCalendar,
operator.EnableEditingWorkCalendar,
operator.DisableEditingWorkCalendar,
prop.WorkPlan,
prop.BIMWorkPlanProperties,
prop.WorkSchedule,
prop.BIMWorkScheduleProperties,
prop.WorkCalendar,
prop.BIMWorkCalendarProperties,
prop.Task,
prop.BIMTaskProperties,
ui.BIM_PT_work_plans,
ui.BIM_UL_work_plans,
ui.BIM_PT_work_schedules,
ui.BIM_UL_work_schedules,
ui.BIM_PT_work_calendars,
ui.BIM_UL_work_calendars,
ui.BIM_PT_tasks,
ui.BIM_UL_tasks,
)
def register():
bpy.types.Scene.BIMTaskProperties = bpy.props.PointerProperty(type=prop.BIMTaskProperties)
bpy.types.Scene.BIMWorkPlanProperties = bpy.props.PointerProperty(type=prop.BIMWorkPlanProperties)
bpy.types.Scene.BIMWorkScheduleProperties = bpy.props.PointerProperty(type=prop.BIMWorkScheduleProperties)
bpy.types.Scene.BIMWorkCalendarProperties = bpy.props.PointerProperty(type=prop.BIMWorkCalendarProperties)
def unregister():
del bpy.types.Scene.BIMTaskProperties
del bpy.types.Scene.BIMWorkPlanProperties
del bpy.types.Scene.BIMWorkScheduleProperties
del bpy.types.Scene.BIMWorkCalendarProperties
@@ -1,9 +1,36 @@
import bpy
import json
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.sequence.data import Data
class LoadWorkPlans(bpy.types.Operator):
bl_idname = "bim.load_work_plans"
bl_label = "Load Work Plans"
def execute(self, context):
props = context.scene.BIMWorkPlanProperties
while len(props.work_plans) > 0:
props.work_plans.remove(0)
for ifc_definition_id, work_plan in Data.work_plans.items():
new = props.work_plans.add()
new.ifc_definition_id = ifc_definition_id
new.name = work_plan["Name"] or "Unnamed"
props.is_editing = True
bpy.ops.bim.disable_editing_work_plan()
return {"FINISHED"}
class DisableWorkPlanEditingUI(bpy.types.Operator):
bl_idname = "bim.disable_work_plan_editing_ui"
bl_label = "Disable WorkPlan Editing UI"
def execute(self, context):
context.scene.BIMWorkPlanProperties.is_editing = False
return {"FINISHED"}
class AddWorkPlan(bpy.types.Operator):
bl_idname = "bim.add_work_plan"
bl_label = "Add Work Plan"
@@ -11,6 +38,33 @@ class AddWorkPlan(bpy.types.Operator):
def execute(self, context):
ifcopenshell.api.run("sequence.add_work_plan", IfcStore.get_file())
Data.load(IfcStore.get_file())
bpy.ops.bim.load_work_plans()
return {"FINISHED"}
class EditWorkPlan(bpy.types.Operator):
bl_idname = "bim.edit_work_plan"
bl_label = "Edit Work Plan"
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
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"sequence.edit_work_plan",
self.file,
**{"work_plan": self.file.by_id(props.active_work_plan_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.load_work_plans()
return {"FINISHED"}
@@ -20,10 +74,290 @@ class RemoveWorkPlan(bpy.types.Operator):
work_plan: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run("sequence.remove_work_plan", self.file, **{"work_plan": self.file.by_id(self.work_plan)})
Data.load(IfcStore.get_file())
bpy.ops.bim.load_work_plans()
return {"FINISHED"}
class EnableEditingWorkPlan(bpy.types.Operator):
bl_idname = "bim.enable_editing_work_plan"
bl_label = "Enable Editing Work Plan"
work_plan: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMWorkPlanProperties
while len(props.work_plan_attributes) > 0:
props.work_plan_attributes.remove(0)
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()]
props.active_work_plan_id = self.work_plan
return {"FINISHED"}
class DisableEditingWorkPlan(bpy.types.Operator):
bl_idname = "bim.disable_editing_work_plan"
bl_label = "Disable Editing Work Plan"
def execute(self, context):
context.scene.BIMWorkPlanProperties.active_work_plan_id = 0
return {"FINISHED"}
class LoadWorkSchedules(bpy.types.Operator):
bl_idname = "bim.load_work_schedules"
bl_label = "Load Work Schedules"
def execute(self, context):
props = context.scene.BIMWorkScheduleProperties
while len(props.work_schedules) > 0:
props.work_schedules.remove(0)
for ifc_definition_id, work_schedule in Data.work_schedules.items():
new = props.work_schedules.add()
new.ifc_definition_id = ifc_definition_id
new.name = work_schedule["Name"] or "Unnamed"
props.is_editing = True
bpy.ops.bim.disable_editing_work_schedule()
return {"FINISHED"}
class DisableWorkScheduleEditingUI(bpy.types.Operator):
bl_idname = "bim.disable_work_schedule_editing_ui"
bl_label = "Disable WorkSchedule Editing UI"
def execute(self, context):
context.scene.BIMWorkScheduleProperties.is_editing = False
return {"FINISHED"}
class AddWorkSchedule(bpy.types.Operator):
bl_idname = "bim.add_work_schedule"
bl_label = "Add Work Schedule"
def execute(self, context):
ifcopenshell.api.run("sequence.add_work_schedule", IfcStore.get_file())
Data.load(IfcStore.get_file())
bpy.ops.bim.load_work_schedules()
return {"FINISHED"}
class EditWorkSchedule(bpy.types.Operator):
bl_idname = "bim.edit_work_schedule"
bl_label = "Edit Work Schedule"
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
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"sequence.remove_work_plan", IfcStore.get_file(), work_plan=IfcStore.get_file().by_id(self.work_plan)
"sequence.edit_work_schedule",
self.file,
**{"work_schedule": self.file.by_id(props.active_work_schedule_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.load_work_schedules()
return {"FINISHED"}
class RemoveWorkSchedule(bpy.types.Operator):
bl_idname = "bim.remove_work_schedule"
bl_label = "Remove Work Schedule"
work_schedule: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"sequence.remove_work_schedule", self.file, **{"work_schedule": self.file.by_id(self.work_schedule)}
)
Data.load(self.file)
bpy.ops.bim.load_work_schedules()
return {"FINISHED"}
class EnableEditingWorkSchedule(bpy.types.Operator):
bl_idname = "bim.enable_editing_work_schedule"
bl_label = "Enable Editing Work Schedule"
work_schedule: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMWorkScheduleProperties
while len(props.work_schedule_attributes) > 0:
props.work_schedule_attributes.remove(0)
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 = 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()]
props.active_work_schedule_id = self.work_schedule
return {"FINISHED"}
class DisableEditingWorkSchedule(bpy.types.Operator):
bl_idname = "bim.disable_editing_work_schedule"
bl_label = "Disable Editing Work Schedule"
def execute(self, context):
context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
return {"FINISHED"}
class LoadWorkCalendars(bpy.types.Operator):
bl_idname = "bim.load_work_calendars"
bl_label = "Load Work Calendars"
def execute(self, context):
props = context.scene.BIMWorkCalendarProperties
while len(props.work_calendars) > 0:
props.work_calendars.remove(0)
for ifc_definition_id, work_calendar in Data.work_calendars.items():
new = props.work_calendars.add()
new.ifc_definition_id = ifc_definition_id
new.name = work_calendar["Name"] or "Unnamed"
props.is_editing = True
bpy.ops.bim.disable_editing_work_calendar()
return {"FINISHED"}
class DisableWorkCalendarEditingUI(bpy.types.Operator):
bl_idname = "bim.disable_work_calendar_editing_ui"
bl_label = "Disable WorkCalendar Editing UI"
def execute(self, context):
context.scene.BIMWorkCalendarProperties.is_editing = False
return {"FINISHED"}
class AddWorkCalendar(bpy.types.Operator):
bl_idname = "bim.add_work_calendar"
bl_label = "Add Work Calendar"
def execute(self, context):
ifcopenshell.api.run("sequence.add_work_calendar", IfcStore.get_file())
Data.load(IfcStore.get_file())
bpy.ops.bim.load_work_calendars()
return {"FINISHED"}
class EditWorkCalendar(bpy.types.Operator):
bl_idname = "bim.edit_work_calendar"
bl_label = "Edit Work Calendar"
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
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"sequence.edit_work_calendar",
self.file,
**{"work_calendar": self.file.by_id(props.active_work_calendar_id), "attributes": attributes}
)
Data.load(IfcStore.get_file())
bpy.ops.bim.load_work_calendars()
return {"FINISHED"}
class RemoveWorkCalendar(bpy.types.Operator):
bl_idname = "bim.remove_work_calendar"
bl_label = "Remove Work Plan"
work_calendar: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"sequence.remove_work_calendar", self.file, **{"work_calendar": self.file.by_id(self.work_calendar)}
)
Data.load(self.file)
bpy.ops.bim.load_work_calendars()
return {"FINISHED"}
class EnableEditingWorkCalendar(bpy.types.Operator):
bl_idname = "bim.enable_editing_work_calendar"
bl_label = "Enable Editing Work Plan"
work_calendar: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMWorkCalendarProperties
while len(props.work_calendar_attributes) > 0:
props.work_calendar_attributes.remove(0)
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 = 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()]
props.active_work_calendar_id = self.work_calendar
return {"FINISHED"}
class DisableEditingWorkCalendar(bpy.types.Operator):
bl_idname = "bim.disable_editing_work_calendar"
bl_label = "Disable Editing Work Calendar"
def execute(self, context):
context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0
return {"FINISHED"}
@@ -23,3 +23,42 @@ class BIMTaskProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Editing", default=False)
tasks: CollectionProperty(name="Tasks", type=Task)
active_task_index: IntProperty(name="Active Task Index")
class WorkPlan(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class BIMWorkPlanProperties(PropertyGroup):
work_plan_attributes: CollectionProperty(name="Work Plan Attributes", type=Attribute)
is_editing: BoolProperty(name="Is Editing", default=False)
work_plans: CollectionProperty(name="Work Plans", type=WorkPlan)
active_work_plan_index: IntProperty(name="Active Work Plan Index")
active_work_plan_id: IntProperty(name="Active Work Plan Id")
class WorkSchedule(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class BIMWorkScheduleProperties(PropertyGroup):
work_schedule_attributes: CollectionProperty(name="Work Schedule Attributes", type=Attribute)
is_editing: BoolProperty(name="Is Editing", default=False)
work_schedules: CollectionProperty(name="Work Schedules", type=WorkSchedule)
active_work_schedule_index: IntProperty(name="Active Work Schedules Index")
active_work_schedule_id: IntProperty(name="Active Work Schedules Id")
class WorkCalendar(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class BIMWorkCalendarProperties(PropertyGroup):
work_calendar_attributes: CollectionProperty(name="Work Calendar Attributes", type=Attribute)
is_editing: BoolProperty(name="Is Editing", default=False)
work_calendars: CollectionProperty(name="Work Calendar", type=WorkCalendar)
active_work_calendar_index: IntProperty(name="Active Work Calendar Index")
active_work_calendar_id: IntProperty(name="Active Work Calendar Id")
@@ -18,15 +18,181 @@ class BIM_PT_work_plans(Panel):
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
self.props = context.scene.BIMWorkPlanProperties
row = self.layout.row(align=True)
row.label(text="{} Work Plans Found".format(len(Data.work_plans)), icon="TEXT")
if self.props.is_editing:
row.operator("bim.add_work_plan", text="", icon="ADD")
row.operator("bim.disable_work_plan_editing_ui", text="", icon="CHECKMARK")
else:
row.operator("bim.load_work_plans", text="", icon="GREASEPENCIL")
row = self.layout.row()
row.operator("bim.add_work_plan", icon="ADD")
if self.props.is_editing:
self.layout.template_list(
"BIM_UL_work_plans",
"",
self.props,
"work_plans",
self.props,
"active_work_plan_index",
)
for work_plan_id, work_plan in Data.work_plans.items():
if self.props.active_work_plan_id:
self.draw_editable_ui(context)
def draw_editable_ui(self, context):
for attribute in self.props.work_plan_attributes:
row = self.layout.row(align=True)
row.label(text=work_plan["Name"] or "Unnamed", icon="TEXT")
row.operator("bim.add_work_plan", text="", icon="GREASEPENCIL")
row.operator("bim.remove_work_plan", text="", icon="X").work_plan = work_plan_id
if attribute.data_type == "string":
row.prop(attribute, "string_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="")
class BIM_UL_work_plans(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)
if context.scene.BIMWorkPlanProperties.active_work_plan_id == item.ifc_definition_id:
row.operator("bim.edit_work_plan", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_work_plan", text="", icon="X")
elif context.scene.BIMWorkPlanProperties.active_work_plan_id:
row.operator("bim.remove_work_plan", text="", icon="X").work_plan = item.ifc_definition_id
else:
op = row.operator("bim.enable_editing_work_plan", text="", icon="GREASEPENCIL")
op.work_plan = item.ifc_definition_id
row.operator("bim.remove_work_plan", text="", icon="X").work_plan = item.ifc_definition_id
class BIM_PT_work_schedules(Panel):
bl_label = "IFC Work Schedules"
bl_idname = "BIM_PT_work_schedules"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@classmethod
def poll(cls, context):
return IfcStore.get_file()
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
self.props = context.scene.BIMWorkScheduleProperties
row = self.layout.row(align=True)
row.label(text="{} Work Schedules Found".format(len(Data.work_schedules)), icon="TEXT")
if self.props.is_editing:
row.operator("bim.add_work_schedule", text="", icon="ADD")
row.operator("bim.disable_work_schedule_editing_ui", text="", icon="CHECKMARK")
else:
row.operator("bim.load_work_schedules", text="", icon="GREASEPENCIL")
if self.props.is_editing:
self.layout.template_list(
"BIM_UL_work_schedules",
"",
self.props,
"work_schedules",
self.props,
"active_work_schedule_index",
)
if self.props.active_work_schedule_id:
self.draw_editable_ui(context)
def draw_editable_ui(self, context):
for attribute in self.props.work_schedule_attributes:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_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="")
class BIM_UL_work_schedules(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)
if context.scene.BIMWorkScheduleProperties.active_work_schedule_id == item.ifc_definition_id:
row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_work_schedule", text="", icon="X")
elif context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = item.ifc_definition_id
else:
op = row.operator("bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL")
op.work_schedule = item.ifc_definition_id
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = item.ifc_definition_id
class BIM_PT_work_calendars(Panel):
bl_label = "IFC Work Calendars"
bl_idname = "BIM_PT_work_calendars"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@classmethod
def poll(cls, context):
return IfcStore.get_file()
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
self.props = context.scene.BIMWorkCalendarProperties
row = self.layout.row(align=True)
row.label(text="{} Work Calendar Found".format(len(Data.work_calendars)), icon="TEXT")
if self.props.is_editing:
row.operator("bim.add_work_calendar", text="", icon="ADD")
row.operator("bim.disable_work_calendar_editing_ui", text="", icon="CHECKMARK")
else:
row.operator("bim.load_work_calendars", text="", icon="GREASEPENCIL")
if self.props.is_editing:
self.layout.template_list(
"BIM_UL_work_calendars",
"",
self.props,
"work_calendars",
self.props,
"active_work_calendar_index",
)
if self.props.active_work_calendar_id:
self.draw_editable_ui(context)
def draw_editable_ui(self, context):
for attribute in self.props.work_calendar_attributes:
row = self.layout.row(align=True)
if attribute.data_type == "string":
row.prop(attribute, "string_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="")
class BIM_UL_work_calendars(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)
if context.scene.BIMWorkCalendarProperties.active_work_calendar_id == item.ifc_definition_id:
row.operator("bim.edit_work_calendar", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_work_calendar", text="", icon="X")
elif context.scene.BIMWorkCalendarProperties.active_work_calendar_id:
row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = item.ifc_definition_id
else:
op = row.operator("bim.enable_editing_work_calendar", text="", icon="GREASEPENCIL")
op.work_calendar = item.ifc_definition_id
row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = item.ifc_definition_id
class BIM_PT_tasks(Panel):
@@ -27,6 +27,8 @@ class BIM_PT_voids(Panel):
for obj in context.selected_objects:
if "IfcOpeningElement" in obj.name or not obj.BIMObjectProperties.ifc_definition_id:
op.opening = obj.name
elif len(obj.children) == 1 and not obj.children[0].BIMObjectProperties.ifc_definition_id:
op.opening = obj.children[0].name
else:
op.obj = obj.name
+4 -1
View File
@@ -1,3 +1,6 @@
import ifcopenshell
import ezdxf
class Dxf2Ifc:
def execute(self):
self.create_ifc_file()
@@ -14,7 +17,7 @@ class Dxf2Ifc:
[
self.file.createIfcFaceOuterBound(
self.file.createIfcPolyLoop(
[self.file.createIfcCartesianPoint((v.dxf.location)) for v in face[0:3]]
[self.file.createIfcCartesianPoint((face[index].dxf.location)) for index in range(len(face) -1)]
),
True,
)