mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-16 21:42:19 +00:00
Fetch latest commits
This commit is contained in:
@@ -108,7 +108,7 @@ def export_attributes(props, callback=None):
|
||||
return attributes
|
||||
|
||||
|
||||
def prop_with_search(layout, data, prop_name, **kwargs):
|
||||
def prop_with_search(layout, data, prop_name, **kwargs):
|
||||
# kwargs are layout.prop arguments (text, icon, etc.)
|
||||
row = layout.row(align=True)
|
||||
# Magick courtesy of https://blender.stackexchange.com/a/203443/86891
|
||||
@@ -118,15 +118,18 @@ def prop_with_search(layout, data, prop_name, **kwargs):
|
||||
op.prop_name = prop_name
|
||||
|
||||
|
||||
def col_with_margins(layout, margin_left=0.025, margin_right=None):
|
||||
margin_right = margin_left if margin_right is None else margin_right
|
||||
split = layout.split(factor=margin_left, align=True)
|
||||
cols = [split.column() for _ in range(2)]
|
||||
cols[0].label(text="")
|
||||
subsplit = cols[-1].split(factor=(1. - margin_right), align=True)
|
||||
subcol = subsplit.column()
|
||||
subsplit.column().label(text="")
|
||||
return subcol
|
||||
def get_enum_items(data, prop_name, context):
|
||||
# Retrieve items from a dynamic EnumProperty, which is otherwise not supported
|
||||
# Or throws an error in the console when the items callback returns an empty list
|
||||
# See https://blender.stackexchange.com/q/215781/86891
|
||||
prop = data.__annotations__[prop_name]
|
||||
items = prop.keywords.get("items")
|
||||
if items is None:
|
||||
return
|
||||
if not isinstance(items, (list, tuple)):
|
||||
# items are retrieved through a callback, not a static list :
|
||||
items = items(data, context)
|
||||
return items
|
||||
|
||||
|
||||
class IfcHeaderExtractor:
|
||||
|
||||
@@ -69,9 +69,3 @@ class BIMBrickProperties(PropertyGroup):
|
||||
libraries: EnumProperty(name="Libraries", items=get_libraries)
|
||||
namespace: EnumProperty(name="Namespace", items=get_namespaces)
|
||||
brick_equipment_class: EnumProperty(name="Brick Equipment Class", items=get_brick_equipment_classes)
|
||||
|
||||
getter_enum = {
|
||||
"libraries": get_libraries,
|
||||
"namespace": get_namespaces,
|
||||
"brick_equipment_class": get_brick_equipment_classes,
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ class BIM_PT_context(bpy.types.Panel):
|
||||
row.operator("bim.remove_context", icon="X", text="").context = ifc_context["id"]
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(props, "subcontexts", text="")
|
||||
row.prop(props, "target_views", text="")
|
||||
blenderbim.bim.helper.prop_with_search(row, props, "subcontexts", text="")
|
||||
blenderbim.bim.helper.prop_with_search(row, props, "target_views", text="")
|
||||
op = row.operator("bim.add_context", icon="ADD", text="")
|
||||
op.context_type = ifc_context["context_type"]
|
||||
op.context_identifier = props.subcontexts
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import ifccsv
|
||||
import ifcopenshell
|
||||
import json
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
@@ -44,15 +45,15 @@ class VisualiseDiff(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
# ifc_file = IfcStore.get_file() # In case we get from Store
|
||||
ifc_file = ifcopenshell.open(context.scene.DiffProperties.diff_new_file) # for Now refer to the new file
|
||||
ifc_file = tool.Ifc.get()
|
||||
with open(context.scene.DiffProperties.diff_json_file, "r") as file:
|
||||
diff = json.load(file)
|
||||
for obj in context.visible_objects:
|
||||
obj.color = (1.0, 1.0, 1.0, 0.2)
|
||||
global_id = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
|
||||
if not global_id:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
global_id = element.GlobalId
|
||||
if global_id in diff["deleted"]:
|
||||
obj.color = (1.0, 0.0, 0.0, 0.2)
|
||||
elif global_id in diff["added"]:
|
||||
|
||||
@@ -137,5 +137,3 @@ class BIMObjectMaterialProperties(PropertyGroup):
|
||||
parameterized_profile_classes: EnumProperty(
|
||||
items=getParameterizedProfileClasses, name="Parameterized Profile Classes"
|
||||
)
|
||||
|
||||
getter_enum = {"material": get_materials}
|
||||
|
||||
@@ -51,7 +51,7 @@ class BIM_PT_materials(Panel):
|
||||
row.operator("bim.disable_editing_materials", text="", icon="CANCEL")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "material_type", text="")
|
||||
prop_with_search(row, self.props, "material_type", text="")
|
||||
row.operator("bim.load_materials", text="", icon="IMPORT")
|
||||
return
|
||||
|
||||
@@ -189,7 +189,7 @@ class BIM_PT_object_material(Panel):
|
||||
return self.draw_material_ui()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "material_type", text="")
|
||||
prop_with_search(row, self.props, "material_type", text="")
|
||||
if self.props.material_type == "IfcMaterial" or self.props.material_type == "IfcMaterialList":
|
||||
prop_with_search(row, self.props, "material", text="")
|
||||
row.operator("bim.assign_material", icon="ADD", text="")
|
||||
|
||||
@@ -575,10 +575,13 @@ def ensure_material_assigned(usecase_path, ifc_file, settings):
|
||||
if om is not None and om.BIMObjectProperties.ifc_definition_id
|
||||
]
|
||||
|
||||
if material[0].id() in object_material_ids:
|
||||
if material and material[0].id() in object_material_ids:
|
||||
continue
|
||||
|
||||
if len(obj.data.materials) == 1:
|
||||
obj.data.materials.clear()
|
||||
|
||||
if not material:
|
||||
continue
|
||||
|
||||
obj.data.materials.append(IfcStore.get_element(material[0].id()))
|
||||
|
||||
@@ -66,7 +66,3 @@ class BIMPatchProperties(PropertyGroup):
|
||||
ifc_patch_output: StringProperty(default="", name="IFC Patch Output IFC")
|
||||
ifc_patch_args: StringProperty(default="", name="Arguments")
|
||||
ifc_patch_args_attr: CollectionProperty(type=Attribute, name="Arguments")
|
||||
|
||||
getter_enum = {
|
||||
"ifc_patch_recipes": get_ifcpatch_recipes,
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ classes = (
|
||||
operator.RefreshLibrary,
|
||||
operator.RewindLibrary,
|
||||
operator.SaveLibraryFile,
|
||||
operator.AppendEntiryLibrary,
|
||||
operator.AppendEntireLibrary,
|
||||
operator.SelectLibraryFile,
|
||||
operator.ToggleFilterCategories,
|
||||
operator.ToggleLinkVisibility,
|
||||
|
||||
@@ -107,10 +107,9 @@ class SelectLibraryFile(bpy.types.Operator, IFCFileSelector):
|
||||
def commit(self, data):
|
||||
IfcStore.library_path = data["filepath"]
|
||||
IfcStore.library_file = ifcopenshell.open(data["filepath"])
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
IFCFileSelector.draw(self, context)
|
||||
self.layout.prop(self, "append_all", text= "Append Entire Library")
|
||||
self.layout.prop(self, "append_all", text="Append Entire Library")
|
||||
|
||||
|
||||
class RefreshLibrary(bpy.types.Operator):
|
||||
@@ -289,10 +288,10 @@ class SaveLibraryFile(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AppendEntiryLibrary(bpy.types.Operator):
|
||||
class AppendEntireLibrary(bpy.types.Operator):
|
||||
bl_idname = "bim.append_entire_library"
|
||||
bl_label = "Append Entiry Library"
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
@@ -303,13 +302,15 @@ class AppendEntiryLibrary(bpy.types.Operator):
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
self.library = IfcStore.library_file
|
||||
|
||||
lib_elements = ifcopenshell.util.selector.Selector().parse(self.library, '.IfcTypeProduct | .IfcMaterial | .IfcCostSchedule| .IfcProfileDef')
|
||||
|
||||
lib_elements = ifcopenshell.util.selector.Selector().parse(
|
||||
self.library, ".IfcTypeProduct | .IfcMaterial | .IfcCostSchedule| .IfcProfileDef"
|
||||
)
|
||||
for element in lib_elements:
|
||||
bpy.ops.bim.append_library_element(definition= element.id())
|
||||
bpy.ops.bim.append_library_element(definition=element.id())
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
|
||||
class AppendLibraryElement(bpy.types.Operator):
|
||||
bl_idname = "bim.append_library_element"
|
||||
bl_label = "Append Library Element"
|
||||
@@ -661,16 +662,19 @@ class LinkIfc(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Link a Blender file"
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
files: bpy.props.CollectionProperty(name="Files", type=bpy.types.OperatorFileListElement)
|
||||
directory: bpy.props.StringProperty(subtype="DIR_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.blend;*.blend1", options={"HIDDEN"})
|
||||
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
||||
|
||||
def execute(self, context):
|
||||
new = context.scene.BIMProjectProperties.links.add()
|
||||
filepath = self.filepath
|
||||
if self.use_relative_path:
|
||||
filepath = os.path.relpath(filepath, bpy.path.abspath("//"))
|
||||
new.name = filepath
|
||||
bpy.ops.bim.load_link(filepath=self.filepath)
|
||||
for file in self.files:
|
||||
filepath = os.path.join(self.directory, file.name)
|
||||
new = context.scene.BIMProjectProperties.links.add()
|
||||
if self.use_relative_path:
|
||||
filepath = os.path.relpath(filepath, bpy.path.abspath("//"))
|
||||
new.name = filepath
|
||||
bpy.ops.bim.load_link(filepath=self.filepath)
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
@@ -167,28 +167,3 @@ class BIMProjectProperties(PropertyGroup):
|
||||
|
||||
def get_library_element_index(self, lib_element):
|
||||
return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element))
|
||||
|
||||
getter_enum = {
|
||||
"collection_mode": lambda self, context: [
|
||||
("DECOMPOSITION", "Decomposition", "Collections represent aggregates and spatial containers"),
|
||||
("SPATIAL_DECOMPOSITION", "Spatial Decomposition", "Collections represent spatial containers"),
|
||||
("IFC_CLASS", "IFC Class", "Collections represent IFC class"),
|
||||
("NONE", "None", "No collections are created"),
|
||||
],
|
||||
"filter_mode": lambda self, context: [
|
||||
("NONE", "None", "No filtering is performed"),
|
||||
("DECOMPOSITION", "Decomposition", "Filter objects by decomposition"),
|
||||
("IFC_CLASS", "IFC Class", "Filter objects by class"),
|
||||
("IFC_TYPE", "IFC Type", "Filter objects by type"),
|
||||
("WHITELIST", "Whitelist", "Filter objects using a custom whitelist query"),
|
||||
("BLACKLIST", "Blacklist", "Filter objects using a custom blacklist query"),
|
||||
],
|
||||
"merge_mode": lambda self, context: [
|
||||
("NONE", "None", "No objects are merged"),
|
||||
("IFC_CLASS", "IFC Class", "One object per IFC class"),
|
||||
("IFC_TYPE", "IFC Type", "One object per IFC construction type"),
|
||||
("MATERIAL", "Material", "One object per material"),
|
||||
],
|
||||
"export_schema": get_export_schema,
|
||||
"template_file": get_template_file,
|
||||
}
|
||||
|
||||
@@ -124,43 +124,52 @@ class EnablePsetEditing(bpy.types.Operator):
|
||||
continue # Other types not yet supported
|
||||
if prop_template.TemplateType == "P_SINGLEVALUE":
|
||||
self.load_single_value(prop_template, data)
|
||||
elif prop_template.TemplateType.startswith("Q_"):
|
||||
self.load_single_value(prop_template, data)
|
||||
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
||||
self.load_enumerated_value(prop_template, data)
|
||||
|
||||
def load_single_value(self, prop_template, data):
|
||||
try:
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(
|
||||
IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
|
||||
)
|
||||
except:
|
||||
# TODO: Occurs if the data type is something that exists in
|
||||
# IFC4 and not in IFC2X3. To fully fix this we need to
|
||||
# generate the IFC2X3 pset template definitions.
|
||||
return
|
||||
|
||||
prop = self.props.properties.add()
|
||||
prop.name = prop_template.Name
|
||||
prop.value_type = "IfcPropertySingleValue"
|
||||
metadata = prop.metadata
|
||||
metadata.name = prop_template.Name
|
||||
metadata.is_null = data.get(prop_template.Name, None) is None
|
||||
metadata.is_optional = True
|
||||
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
|
||||
metadata.data_type = data_type
|
||||
metadata.data_type = self.get_data_type(prop_template)
|
||||
|
||||
if data_type == "string":
|
||||
if metadata.data_type == "string":
|
||||
metadata.string_value = "" if metadata.is_null else data[prop_template.Name]
|
||||
elif data_type == "integer":
|
||||
elif metadata.data_type == "integer":
|
||||
metadata.int_value = 0 if metadata.is_null else data[prop_template.Name]
|
||||
elif data_type == "float":
|
||||
elif metadata.data_type == "float":
|
||||
metadata.float_value = 0.0 if metadata.is_null else data[prop_template.Name]
|
||||
elif data_type == "boolean":
|
||||
elif metadata.data_type == "boolean":
|
||||
metadata.bool_value = False if metadata.is_null else data[prop_template.Name]
|
||||
|
||||
def get_data_type(self, prop_template):
|
||||
if prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
|
||||
return "float"
|
||||
elif prop_template.TemplateType == "Q_COUNT":
|
||||
return "integer"
|
||||
try:
|
||||
return ifcopenshell.util.attribute.get_primitive_type(
|
||||
IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel")
|
||||
)
|
||||
except:
|
||||
# TODO: Occurs if the data type is something that exists in
|
||||
# IFC4 and not in IFC2X3. To fully fix this we need to
|
||||
# generate the IFC2X3 pset template definitions.
|
||||
pass
|
||||
|
||||
def load_enumerated_value(self, prop_template, data):
|
||||
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
|
||||
selected_enum_items = data.get(prop_template.Name, [])
|
||||
|
||||
prop = self.props.properties.add()
|
||||
prop.name = prop_template.Name
|
||||
prop.value_type = "IfcPropertyEnumeratedValue"
|
||||
metadata = prop.metadata
|
||||
metadata.name = prop_template.Name
|
||||
@@ -358,7 +367,7 @@ class GuessQuantity(bpy.types.Operator):
|
||||
self.qto_calculator = QtoCalculator()
|
||||
obj = context.active_object
|
||||
prop = obj.PsetProperties.properties.get(self.prop)
|
||||
prop.float_value = self.guess_quantity(obj, context)
|
||||
prop.metadata.float_value = self.guess_quantity(obj, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def guess_quantity(self, obj, context):
|
||||
@@ -402,7 +411,7 @@ class CopyPropertyToSelection(bpy.types.Operator, Operator):
|
||||
def _execute(self, context):
|
||||
is_pset = tool.Ifc.get().by_id(context.active_object.PsetProperties.active_pset_id).is_a("IfcPropertySet")
|
||||
pset_name = context.active_object.PsetProperties.active_pset_name
|
||||
prop_value = context.active_object.PsetProperties.properties.get(self.name).get_value()
|
||||
prop_value = context.active_object.PsetProperties.properties.get(self.name).metadata.get_value()
|
||||
for obj in context.selected_objects:
|
||||
core.copy_property_to_selection(
|
||||
tool.Ifc,
|
||||
|
||||
@@ -158,16 +158,11 @@ class PsetProperties(PropertyGroup):
|
||||
pset_name: EnumProperty(items=get_pset_names, name="Pset Name")
|
||||
qto_name: EnumProperty(items=get_qto_names, name="Qto Name")
|
||||
|
||||
getter_enum = {
|
||||
"qto_name": get_qto_names,
|
||||
"pset_name": get_pset_names,
|
||||
}
|
||||
|
||||
|
||||
class MaterialPsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=getMaterialPsetNames, name="Pset Name")
|
||||
|
||||
|
||||
@@ -189,14 +184,14 @@ class ResourcePsetProperties(PropertyGroup):
|
||||
class ProfilePsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=getProfilePsetNames, name="Pset Name")
|
||||
|
||||
|
||||
class WorkSchedulePsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=getWorkSchedulePsetNames, name="Pset Name")
|
||||
|
||||
|
||||
@@ -223,14 +218,6 @@ class AddEditProperties(PropertyGroup):
|
||||
)
|
||||
enum_values: CollectionProperty(name="Enum Values", type=Attribute)
|
||||
|
||||
getter_enum = {
|
||||
"primary_measure_type": get_primary_measure_type,
|
||||
"template_type": lambda self, context: [
|
||||
("IfcPropertySingleValue", "IfcPropertySingleValue", "IfcPropertySingleValue"),
|
||||
("IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue", "IfcPropertyEnumeratedValue"),
|
||||
],
|
||||
}
|
||||
|
||||
def get_value_name(self):
|
||||
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
|
||||
data_type = ifcopenshell.util.attribute.get_primitive_type(ifc_data_type)
|
||||
|
||||
@@ -252,7 +252,7 @@ class BIM_PT_material_psets(Panel):
|
||||
|
||||
props = context.active_object.active_material.PsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj = context.active_object.active_material.name
|
||||
op.obj_type = "Material"
|
||||
@@ -348,7 +348,7 @@ class BIM_PT_resource_psets(Panel):
|
||||
|
||||
props = context.scene.ResourcePsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj_type = "Resource"
|
||||
|
||||
@@ -381,7 +381,7 @@ class BIM_PT_profile_psets(Panel):
|
||||
|
||||
props = context.scene.ProfilePsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj_type = "Profile"
|
||||
|
||||
@@ -410,7 +410,7 @@ class BIM_PT_work_schedule_psets(Panel):
|
||||
|
||||
props = context.scene.WorkSchedulePsetProperties
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(props, "pset_name", text="")
|
||||
prop_with_search(row, props, "pset_name", text="")
|
||||
op = row.operator("bim.add_pset", icon="ADD", text="")
|
||||
op.obj_type = "WorkSchedule"
|
||||
|
||||
@@ -450,7 +450,7 @@ class BIM_PT_rename_parameters(Panel):
|
||||
if props:
|
||||
for index, prop in enumerate(props):
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, "pset_name", text="")
|
||||
prop_with_search(row, prop, "pset_name", text="")
|
||||
row.prop(prop, "existing_property_name", text="")
|
||||
row.prop(prop, "new_property_name", text="")
|
||||
op = row.operator("bim.remove_property_to_edit", icon="X", text="")
|
||||
@@ -485,12 +485,12 @@ class BIM_PT_add_edit_custom_properties(Panel):
|
||||
if props:
|
||||
for index, prop in enumerate(props):
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, "pset_name", text="")
|
||||
prop_with_search(row, prop, "pset_name", text="")
|
||||
row.prop(prop, "property_name", text="")
|
||||
if prop.template_type == "IfcPropertySingleValue":
|
||||
row.prop(prop, prop.get_value_name(), text="")
|
||||
prop_with_search(row, prop, "primary_measure_type", text="")
|
||||
prop_with_search(row, prop, "template_type", text="")
|
||||
row.prop(prop, "template_type", text="")
|
||||
op = row.operator("bim.remove_property_to_edit", icon="X", text="")
|
||||
op.index = index
|
||||
op.option = "AddEditProperties"
|
||||
@@ -537,7 +537,7 @@ class BIM_PT_delete_psets(Panel):
|
||||
if props:
|
||||
for index, prop in enumerate(props):
|
||||
row = layout.row(align=True)
|
||||
row.prop(prop, "pset_name", text="")
|
||||
prop_with_search(row, prop, "pset_name", text="")
|
||||
op = row.operator("bim.remove_property_to_edit", icon="X", text="")
|
||||
op.index = index
|
||||
op.option = "DeletePsets"
|
||||
|
||||
@@ -146,10 +146,6 @@ class PsetTemplate(PropertyGroup):
|
||||
template_type: EnumProperty(items=get_template_type, name="Template Type")
|
||||
applicable_entity: StringProperty(name="Applicable Entity")
|
||||
|
||||
getter_enum = {
|
||||
"template_type": get_template_type,
|
||||
}
|
||||
|
||||
|
||||
class EnumerationValues(PropertyGroup):
|
||||
string_value: StringProperty(name="Value")
|
||||
@@ -168,9 +164,6 @@ class PropTemplate(PropertyGroup):
|
||||
name="Template Type",
|
||||
)
|
||||
enum_values: CollectionProperty(type=EnumerationValues)
|
||||
getter_enum = {
|
||||
"primary_measure_type": get_primary_measure_type,
|
||||
}
|
||||
|
||||
def get_value_name(self):
|
||||
ifc_data_type = IfcStore.get_schema().declaration_by_name(self.primary_measure_type)
|
||||
@@ -195,8 +188,3 @@ class BIMPsetTemplateProperties(PropertyGroup):
|
||||
active_pset_template: PointerProperty(type=PsetTemplate)
|
||||
active_prop_template: PointerProperty(type=PropTemplate)
|
||||
new_template_filename: StringProperty("New TemplateFileName")
|
||||
|
||||
getter_enum = {
|
||||
"pset_template_files": getPsetTemplateFiles,
|
||||
"pset_templates": getPsetTemplates,
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from collections import defaultdict
|
||||
import bpy
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -35,6 +37,7 @@ class IfcClassData:
|
||||
cls.data = {}
|
||||
cls.data["ifc_products"] = cls.ifc_products()
|
||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||
cls.data["ifc_classes_suggestions"] = cls.ifc_classes_suggestions()
|
||||
cls.data["contexts"] = cls.contexts()
|
||||
cls.data["has_entity"] = cls.has_entity()
|
||||
cls.data["name"] = cls.name()
|
||||
@@ -74,6 +77,41 @@ class IfcClassData:
|
||||
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
|
||||
return [(c, c, "") for c in sorted(names)]
|
||||
|
||||
@classmethod
|
||||
def ifc_classes_suggestions(cls):
|
||||
suggestions = defaultdict(list)
|
||||
suggestions.update(
|
||||
{
|
||||
"IfcWall": ["Glazing", "Glass", "Pane"],
|
||||
"IfcWindow": ["Glazing", "Glass", "Pane"],
|
||||
"IfcPlate": ["Glazing", "Glass", "Pane"],
|
||||
"IfcFurniture": ["Signage"],
|
||||
"IfcSlab": ["Hob"],
|
||||
"IfcCovering": ["Flashing", "Capping"],
|
||||
"IfcCableSegment": ["Lighting Rod"],
|
||||
"IfcSensor": ["Card Reader", "Fob Reader"],
|
||||
"IfcSwitchingDevice": ["Reed Switch", "Electric Isolating Switch"],
|
||||
"IfcActuator": ["Electric Strike"],
|
||||
"IfcAirTerminalBox": ["VAV Box"],
|
||||
"IfcUnitaryEquipment": ["Fan Coil Unit"],
|
||||
}
|
||||
)
|
||||
file = IfcStore.get_file()
|
||||
if file:
|
||||
for ifc_class in cls.ifc_classes():
|
||||
ifc_class = ifc_class[0]
|
||||
declaration = IfcStore.get_schema().declaration_by_name(ifc_class)
|
||||
for attribute in declaration.attributes():
|
||||
if attribute.name() == "PredefinedType":
|
||||
for e in attribute.type_of_attribute().declared_type().enumeration_items():
|
||||
if e in (
|
||||
"NOTDEFINED",
|
||||
"USERDEFINED",
|
||||
):
|
||||
continue
|
||||
suggestions[ifc_class].append(e.title())
|
||||
return suggestions
|
||||
|
||||
@classmethod
|
||||
def contexts(cls):
|
||||
results = []
|
||||
|
||||
@@ -31,6 +31,7 @@ import blenderbim.core.root as core
|
||||
import blenderbim.tool as tool
|
||||
from ifcopenshell.api.void.data import Data as VoidData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import get_enum_items
|
||||
|
||||
|
||||
class Operator:
|
||||
@@ -131,7 +132,7 @@ class AssignClass(bpy.types.Operator, Operator):
|
||||
ifc_class = self.ifc_class or props.ifc_class
|
||||
predefined_type = self.userdefined_type if self.predefined_type == "USERDEFINED" else self.predefined_type
|
||||
ifc_context = self.context_id
|
||||
if not ifc_context and props.getter_enum["contexts"](props, context):
|
||||
if not ifc_context and get_enum_items(props, "contexts", context):
|
||||
ifc_context = int(props.contexts or "0") or None
|
||||
if ifc_context:
|
||||
ifc_context = tool.Ifc.get().by_id(ifc_context)
|
||||
|
||||
@@ -88,6 +88,11 @@ def get_ifc_classes(self, context):
|
||||
return IfcClassData.data["ifc_classes"]
|
||||
|
||||
|
||||
def get_ifc_classes_suggestions():
|
||||
if not IfcClassData.is_loaded:
|
||||
IfcClassData.load()
|
||||
return IfcClassData.data["ifc_classes_suggestions"]
|
||||
|
||||
def get_contexts(self, context):
|
||||
if not IfcClassData.is_loaded:
|
||||
IfcClassData.load()
|
||||
@@ -101,9 +106,6 @@ class BIMRootProperties(PropertyGroup):
|
||||
ifc_predefined_type: EnumProperty(items=getIfcPredefinedTypes, name="Predefined Type", default=None)
|
||||
ifc_userdefined_type: StringProperty(name="Userdefined Type")
|
||||
|
||||
getter_enum = {
|
||||
"contexts": get_contexts,
|
||||
"ifc_product": get_ifc_products,
|
||||
"ifc_class": get_ifc_classes,
|
||||
"ifc_predefined_type": getIfcPredefinedTypes,
|
||||
getter_enum_suggestions = {
|
||||
"ifc_class": get_ifc_classes_suggestions,
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.EditBlenderCollection,
|
||||
operator.ActivateIfcClassFilter,
|
||||
operator.ActivateIfcBuildingStoreyFilter,
|
||||
operator.ColourByAttribute,
|
||||
@@ -31,18 +32,32 @@ classes = (
|
||||
operator.SelectGlobalId,
|
||||
operator.SelectIfcClass,
|
||||
operator.SelectPset,
|
||||
operator.UnhideAllElements,
|
||||
operator.FilterModelElements,
|
||||
operator.IfcSelector,
|
||||
operator.SaveSelectorQuery,
|
||||
operator.OpenQueryLibrary,
|
||||
operator.LoadQuery,
|
||||
prop.BIMFilterClasses,
|
||||
prop.BIMFilterBuildingStoreys,
|
||||
prop.BIMSearchProperties,
|
||||
prop.SearchCollection,
|
||||
prop.SearchQueryFilter,
|
||||
prop.SearchQuery,
|
||||
prop.SearchQueryGroup,
|
||||
prop.IfcSelectorProperties,
|
||||
ui.BIM_PT_search,
|
||||
ui.BIM_UL_ifc_class_filter,
|
||||
ui.BIM_UL_ifc_building_storey_filter,
|
||||
ui.BIM_PT_IFCSelector,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.Scene.BIMSearchProperties = bpy.props.PointerProperty(type=prop.BIMSearchProperties)
|
||||
bpy.types.Scene.IfcSelectorProperties = bpy.props.PointerProperty(type=prop.IfcSelectorProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
del bpy.types.Scene.BIMSearchProperties
|
||||
del bpy.types.Scene.IfcSelectorProperties
|
||||
|
||||
@@ -20,9 +20,21 @@ import re
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
from ifcopenshell.util.selector import Selector
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from itertools import cycle
|
||||
from bpy.types import PropertyGroup, Operator
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
EnumProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
colour_list = [
|
||||
@@ -54,13 +66,29 @@ def does_keyword_exist(pattern, string, context):
|
||||
return True
|
||||
|
||||
|
||||
class SelectGlobalId(bpy.types.Operator):
|
||||
class EditBlenderCollection(Operator):
|
||||
bl_idname = "bim.edit_blender_collection"
|
||||
bl_label = "Add or Remove blender collection item"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
option: StringProperty()
|
||||
collection: StringProperty()
|
||||
index: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
if self.option == "add":
|
||||
getattr(context.bim_prop_group, self.collection).add()
|
||||
else:
|
||||
getattr(context.bim_prop_group, self.collection).remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectGlobalId(Operator):
|
||||
"""Click to select the objects that match with the given Global ID"""
|
||||
|
||||
bl_idname = "bim.select_global_id"
|
||||
bl_label = "Select GlobalId"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
global_id: bpy.props.StringProperty()
|
||||
global_id: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
ifc_file = tool.Ifc.get()
|
||||
@@ -73,13 +101,13 @@ class SelectGlobalId(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectIfcClass(bpy.types.Operator):
|
||||
class SelectIfcClass(Operator):
|
||||
"""Click to select all objects that match with the given IFC class"""
|
||||
|
||||
bl_idname = "bim.select_ifc_class"
|
||||
bl_label = "Select IFC Class"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
ifc_class: bpy.props.StringProperty()
|
||||
ifc_class: StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
@@ -92,7 +120,7 @@ class SelectIfcClass(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectAttribute(bpy.types.Operator):
|
||||
class SelectAttribute(Operator):
|
||||
"""Click to select all objects that match with the given Attribute Name and Value"""
|
||||
|
||||
bl_idname = "bim.select_attribute"
|
||||
@@ -118,7 +146,7 @@ class SelectAttribute(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class SelectPset(bpy.types.Operator):
|
||||
class SelectPset(Operator):
|
||||
"""Click to select all objects that match with the given Pset Name, Properties Name and Value"""
|
||||
|
||||
bl_idname = "bim.select_pset"
|
||||
@@ -152,7 +180,7 @@ class SelectPset(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ColourByAttribute(bpy.types.Operator):
|
||||
class ColourByAttribute(Operator):
|
||||
"""Click to colour different objects according to given Attribute Name"""
|
||||
|
||||
bl_idname = "bim.colour_by_attribute"
|
||||
@@ -203,7 +231,7 @@ class ColourByAttribute(bpy.types.Operator):
|
||||
data["area"].spaces[0].shading.color_type = "OBJECT"
|
||||
|
||||
|
||||
class ColourByPset(bpy.types.Operator):
|
||||
class ColourByPset(Operator):
|
||||
"""Click to colour different objects according to given Prop Name"""
|
||||
|
||||
bl_idname = "bim.colour_by_pset"
|
||||
@@ -262,7 +290,7 @@ class ColourByPset(bpy.types.Operator):
|
||||
data["area"].spaces[0].shading.color_type = "OBJECT"
|
||||
|
||||
|
||||
class ColourByClass(bpy.types.Operator):
|
||||
class ColourByClass(Operator):
|
||||
"""Click to colour different objects according to their IFC Classes"""
|
||||
|
||||
bl_idname = "bim.colour_by_class"
|
||||
@@ -308,7 +336,7 @@ class ColourByClass(bpy.types.Operator):
|
||||
data["area"].spaces[0].shading.color_type = "OBJECT"
|
||||
|
||||
|
||||
class ResetObjectColours(bpy.types.Operator):
|
||||
class ResetObjectColours(Operator):
|
||||
"""Reset the colour of selected objects"""
|
||||
|
||||
bl_idname = "bim.reset_object_colours"
|
||||
@@ -320,11 +348,11 @@ class ResetObjectColours(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ToggleFilterSelection(bpy.types.Operator):
|
||||
class ToggleFilterSelection(Operator):
|
||||
"Click to select/deselect current selection"
|
||||
bl_idname = "bim.toggle_filter_selection"
|
||||
bl_label = "Toggle Filter Selection"
|
||||
action: bpy.props.EnumProperty(items=(("SELECT", "Select", ""), ("DESELECT", "Deselect", "")))
|
||||
action: EnumProperty(items=(("SELECT", "Select", ""), ("DESELECT", "Deselect", "")))
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.scene.BIMSearchProperties
|
||||
@@ -341,7 +369,7 @@ class ToggleFilterSelection(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ActivateIfcClassFilter(bpy.types.Operator):
|
||||
class ActivateIfcClassFilter(Operator):
|
||||
"""Filter the current selection by IFC class"""
|
||||
|
||||
bl_idname = "bim.activate_ifc_class_filter"
|
||||
@@ -387,7 +415,7 @@ class ActivateIfcClassFilter(bpy.types.Operator):
|
||||
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
|
||||
|
||||
|
||||
class ActivateIfcBuildingStoreyFilter(bpy.types.Operator):
|
||||
class ActivateIfcBuildingStoreyFilter(Operator):
|
||||
"""Filter the current selection by Building Storey"""
|
||||
|
||||
bl_idname = "bim.activate_ifc_building_storey_filter"
|
||||
@@ -433,3 +461,188 @@ class ActivateIfcBuildingStoreyFilter(bpy.types.Operator):
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
|
||||
row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT"
|
||||
|
||||
|
||||
class UnhideAllElements(Operator):
|
||||
"""Filter model elements based on selection"""
|
||||
|
||||
bl_idname = "bim.reset_3d_view"
|
||||
bl_label = "Reset 3D View"
|
||||
bl_idname = "bim.unhide_all_elements"
|
||||
bl_label = "Unhide All Elements"
|
||||
|
||||
def execute(self, context):
|
||||
for obj in bpy.data.scenes["Scene"].objects:
|
||||
obj.hide_set(False)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class FilterModelElements(Operator):
|
||||
"""Filter model elements based on selection"""
|
||||
|
||||
bl_idname = "bim.filter_model_elements"
|
||||
bl_label = "Filter Model Elements"
|
||||
option: StringProperty("select|isolate|hide")
|
||||
|
||||
def execute(self, context):
|
||||
selector = context.scene.IfcSelectorProperties
|
||||
selection = selector.selector_query_syntax if selector.manual_override else self.add_groups(selector)
|
||||
selector.selector_query_syntax = selection
|
||||
self.update_model_view(context, selection)
|
||||
return {"FINISHED"}
|
||||
|
||||
def add_groups(self, selector):
|
||||
selection = ""
|
||||
for group_index, group in enumerate(selector.groups):
|
||||
if group_index != 0:
|
||||
selection += " | "
|
||||
selection += "(" if len(selector.groups) > 1 else ""
|
||||
selection = self.add_queries(selection, group)
|
||||
|
||||
selection += ")" if len(selector.groups) > 1 else ""
|
||||
return selection
|
||||
|
||||
def add_queries(self, selection, group):
|
||||
for query_index, query in enumerate(group.queries):
|
||||
if query_index != 0:
|
||||
selection += " & " if query.and_or == "and" else " | "
|
||||
|
||||
if query.selector == "IFC Class":
|
||||
active_option = query.active_option.split(": ")[1]
|
||||
selection += f".{active_option}"
|
||||
selection = self.add_filters(selection, query)
|
||||
|
||||
elif query.selector == "GlobalId":
|
||||
selection += f"#{query.global_id}"
|
||||
|
||||
elif query.selector == "IfcElementType":
|
||||
index = int(query.active_sub_option.split(":")[0])
|
||||
selection += f"* #{query.sub_options[index].global_id}"
|
||||
|
||||
elif query.selector == "IfcSpatialElement":
|
||||
index = int(query.active_sub_option.split(":")[0])
|
||||
selection += f"@ #{query.sub_options[index].global_id}"
|
||||
return selection
|
||||
|
||||
def add_filters(self, selection, query):
|
||||
for f_index, f in enumerate(query.filters):
|
||||
|
||||
if f_index != 0:
|
||||
selection += " & " if f.and_or == "and" else " | "
|
||||
selection += f".{query.active_option}"
|
||||
|
||||
selection += "["
|
||||
|
||||
if f.selector == "IfcPropertySet":
|
||||
selection += f'{f.active_option.split(": ")[1]}.{f.active_sub_option.split(": ")[1]} {"!" if f.negation else ""}="{f.value}"'
|
||||
elif f.selector == "Attribute":
|
||||
selection += f'{f.attribute} {"!" if f.negation else ""}= "{f.value}"'
|
||||
|
||||
selection += "]"
|
||||
return selection
|
||||
|
||||
def update_model_view(self, context, selection):
|
||||
query = Selector.parse(IfcStore.file, selection)
|
||||
sel_element_ids = [e.id() for e in query]
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
|
||||
for obj in bpy.data.scenes["Scene"].objects:
|
||||
obj.hide_set(False) # reset 3d view
|
||||
|
||||
if self.option == "select":
|
||||
if obj.BIMObjectProperties.ifc_definition_id in sel_element_ids:
|
||||
obj.select_set(True)
|
||||
elif self.option == "isolate":
|
||||
if obj.BIMObjectProperties.ifc_definition_id not in sel_element_ids:
|
||||
obj.hide_set(True)
|
||||
elif self.option == "hide":
|
||||
if obj.BIMObjectProperties.ifc_definition_id in sel_element_ids:
|
||||
obj.hide_set(True)
|
||||
|
||||
|
||||
class IfcSelector(Operator):
|
||||
"""Select elements in model with IFC Selector"""
|
||||
|
||||
bl_idname = "bim.ifc_selector"
|
||||
bl_label = "Select elements with IFC Selector"
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self, width=800)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def execute(self, context):
|
||||
return {"FINISHED"}
|
||||
|
||||
def draw(self, context):
|
||||
from . import ui
|
||||
ui.IfcSelectorUI.draw(context, self.layout)
|
||||
|
||||
|
||||
class SaveSelectorQuery(Operator):
|
||||
bl_idname = "bim.save_selector_query"
|
||||
bl_label = "Save Selector Query"
|
||||
save_name: StringProperty()
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self, width=400)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.prop(self, "save_name")
|
||||
|
||||
def execute(self, context):
|
||||
ifc_selector = context.scene.IfcSelectorProperties
|
||||
new = ifc_selector.query_library.add()
|
||||
new.name = self.save_name
|
||||
new.query = ifc_selector.selector_query_syntax
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class OpenQueryLibrary(Operator):
|
||||
"""Open Query Library"""
|
||||
|
||||
bl_idname = "bim.open_query_library"
|
||||
bl_label = "Open Query Library"
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self, width=400)
|
||||
|
||||
def close_panel(event):
|
||||
x, y = event.mouse_x, event.mouse_y
|
||||
bpy.context.window.cursor_warp(10, 10)
|
||||
|
||||
move_back = lambda: bpy.context.window.cursor_warp(x, y)
|
||||
bpy.app.timers.register(move_back, first_interval=0.001)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
ifc_selector = context.scene.IfcSelectorProperties
|
||||
|
||||
for index, query in enumerate(ifc_selector.query_library):
|
||||
row = layout.row(align=True)
|
||||
row.prop(query, "query", text=query.name)
|
||||
op = row.operator("bim.load_query", icon="SORT_ASC", text="")
|
||||
op.index = index
|
||||
|
||||
row.context_pointer_set(name="bim_prop_group", data=ifc_selector)
|
||||
op = row.operator("bim.edit_blender_collection", icon="REMOVE", text="")
|
||||
op.option = "remove"
|
||||
op.collection = "query_library"
|
||||
op.index = index
|
||||
|
||||
def execute(self, context):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class LoadQuery(Operator):
|
||||
bl_idname = "bim.load_query"
|
||||
bl_label = "Load Query"
|
||||
index: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
ifc_selector = context.scene.IfcSelectorProperties
|
||||
ifc_selector.selector_query_syntax = ifc_selector.query_library[self.index].query
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -17,9 +17,13 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from ifcopenshell import util
|
||||
from ifcopenshell.util.selector import Selector
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import ObjProperty
|
||||
from blenderbim.bim.prop import ObjProperty, StrProperty
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from bpy.types import PropertyGroup
|
||||
from blenderbim.tool.ifc import Ifc
|
||||
from . import ui, prop, operator
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -90,3 +94,146 @@ class BIMSearchProperties(PropertyGroup):
|
||||
filter_classes_index: IntProperty(name="Filter Classes Index")
|
||||
filter_building_storeys: CollectionProperty(type=BIMFilterBuildingStoreys, name="Filter Level")
|
||||
filter_building_storeys_index: IntProperty(name="Filter Level Index")
|
||||
|
||||
|
||||
def get_classes(self, ifc_product):
|
||||
declaration = tool.Ifc.schema().declaration_by_name(ifc_product)
|
||||
declarations = util.schema.get_subtypes(declaration)
|
||||
names = [d.name() for d in declarations]
|
||||
return [(c, c, "") for c in sorted(names)]
|
||||
|
||||
|
||||
def load_sub_options(self, context):
|
||||
if self.selector not in ["IfcClass"]:
|
||||
self.load_option = "sub_options"
|
||||
load_selection_options(self, context)
|
||||
|
||||
|
||||
def load_selection_options(self, context):
|
||||
ifc = IfcStore.file
|
||||
load_option = self.load_option
|
||||
op = getattr(self, load_option)
|
||||
op.clear()
|
||||
options = []
|
||||
|
||||
if load_option == "options":
|
||||
self.sub_options.clear()
|
||||
if self.selector == "IFC Class":
|
||||
options = get_classes(self, "IfcElement")
|
||||
options.append(("IfcSpace", "IfcSpace", ""))
|
||||
elif self.selector == "IfcSpatialElement":
|
||||
options = get_classes(self, "IfcSpatialElement")
|
||||
elif self.selector == "IfcElementType":
|
||||
options = get_classes(self, "IfcElementType")
|
||||
elif self.selector == "GlobalId":
|
||||
return
|
||||
elif self.selector == "IfcPropertySet":
|
||||
psets = Selector.parse(ifc, ".IfcPropertySet")
|
||||
options = set([o.Name for o in psets])
|
||||
|
||||
elif load_option == "sub_options":
|
||||
if self.selector in ["IfcSpatialElement", "IfcElementType"]:
|
||||
active_option = self.active_option.split(": ")[1]
|
||||
options = Selector.parse(ifc, f".{active_option}")
|
||||
|
||||
elif self.selector == "IfcPropertySet":
|
||||
options = set()
|
||||
active_pset = self.active_option.split(": ")[1]
|
||||
psets_in_file = Selector.parse(ifc, f'.IfcPropertySet[Name="{active_pset}"]')
|
||||
for pset in psets_in_file:
|
||||
for prop in pset.HasProperties:
|
||||
options.add(prop.Name)
|
||||
|
||||
for index, option in enumerate(options):
|
||||
new = op.add()
|
||||
if self.selector in ["IfcSpatialElement", "IfcElementType"]:
|
||||
if self.load_option == "sub_options":
|
||||
new.name = f"{index}: {option.Name}"
|
||||
new.global_id = option.GlobalId
|
||||
else:
|
||||
new.name = f"{index}: {option[0]}"
|
||||
elif self.selector == "IfcPropertySet":
|
||||
new.name = f"{index}: {option}"
|
||||
else:
|
||||
new.name = f"{index}: {option[0]}"
|
||||
self.load_option = "options"
|
||||
|
||||
|
||||
def load_spatial_elements(self, context):
|
||||
ifc = IfcStore.file
|
||||
col_items = Selector.parse(ifc, f".{self.selected_spatial_element}")
|
||||
collection = getattr(self, "sub_spatial_elements", None)
|
||||
collection.clear()
|
||||
for index, c in enumerate(col_items):
|
||||
new = collection.add()
|
||||
new.name = f"{index}-{c.Name}"
|
||||
|
||||
|
||||
class SearchCollection(PropertyGroup):
|
||||
name: StringProperty()
|
||||
long_name: StringProperty()
|
||||
global_id: StringProperty()
|
||||
query: StringProperty()
|
||||
|
||||
class IfcSelector:
|
||||
and_or: EnumProperty(
|
||||
items=[(i, i, i) for i in ["and", "or"]],
|
||||
)
|
||||
negation: BoolProperty(name="not")
|
||||
comparison: EnumProperty(
|
||||
items=[
|
||||
("=", "equal to", ""),
|
||||
("*=", "contains", ""),
|
||||
(">=", "greater than or equal to", ""),
|
||||
("<=", "lesser than or equal to", ""),
|
||||
(">", "greater than", ""),
|
||||
("<", "less than", ""),
|
||||
],
|
||||
)
|
||||
load_option: StringProperty(
|
||||
default="options", description="controls whether or not options or sub_options are loaded"
|
||||
)
|
||||
options: CollectionProperty(type=SearchCollection)
|
||||
active_option: StringProperty(update=load_sub_options)
|
||||
sub_options: CollectionProperty(type=SearchCollection)
|
||||
active_sub_option: StringProperty()
|
||||
value: StringProperty(description="generic 'value' that can be used in multiple scenarios")
|
||||
|
||||
|
||||
class SearchQueryFilter(PropertyGroup, IfcSelector):
|
||||
selector: EnumProperty(
|
||||
items=[(i, i, i) for i in ["-", "IfcPropertySet", "Attribute"]],
|
||||
name="Filter selection by",
|
||||
update=load_selection_options,
|
||||
default="-",
|
||||
)
|
||||
attribute: EnumProperty(
|
||||
items=[(i, i, i) for i in ["GlobalId", "Name", "Description", "ObjectType", "Tag", "PredefinedType"]],
|
||||
name="Filter selection by",
|
||||
update=load_selection_options,
|
||||
)
|
||||
|
||||
|
||||
class SearchQuery(PropertyGroup, IfcSelector):
|
||||
filters: CollectionProperty(type=SearchQueryFilter)
|
||||
selector: EnumProperty(
|
||||
items=[(i, i, i) for i in ["-", "IFC Class", "IfcSpatialElement", "IfcElementType", "GlobalId"]],
|
||||
name="Selector type",
|
||||
update=load_selection_options,
|
||||
default="-",
|
||||
)
|
||||
|
||||
|
||||
class SearchQueryGroup(PropertyGroup, IfcSelector):
|
||||
queries: CollectionProperty(type=SearchQuery)
|
||||
|
||||
|
||||
class IfcSelectorProperties(PropertyGroup, IfcSelector):
|
||||
groups: CollectionProperty(type=SearchQueryGroup)
|
||||
selector_query_syntax: StringProperty()
|
||||
|
||||
query_library: CollectionProperty(type=SearchCollection)
|
||||
active_query: StringProperty()
|
||||
|
||||
active_query: StringProperty()
|
||||
manual_override: BoolProperty(default=False, description="Toggle to allow manual typing of query-syntax")
|
||||
|
||||
@@ -102,3 +102,152 @@ class BIM_UL_ifc_building_storey_filter(bpy.types.UIList):
|
||||
split = split.column()
|
||||
split.scale_x = 0.5
|
||||
split.label(text=str(item.total))
|
||||
|
||||
|
||||
class BIM_PT_IFCSelector(Panel):
|
||||
bl_label = "IFC Selector"
|
||||
bl_idname = "BIM_PT_ifc_selector"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_collaboration"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator("bim.ifc_selector")
|
||||
|
||||
|
||||
# this doesn't inherit from Panel since it's just a class to abstract away UI code from the IfcSelector Operator
|
||||
class IfcSelectorUI:
|
||||
@classmethod
|
||||
def draw(self, context, layout):
|
||||
ifc_selector = context.scene.IfcSelectorProperties
|
||||
row = layout.row()
|
||||
|
||||
row.context_pointer_set(name="bim_prop_group", data=ifc_selector)
|
||||
op = row.operator("bim.edit_blender_collection", text="Add selection group")
|
||||
op.option = "add"
|
||||
op.collection = "groups"
|
||||
layout.separator()
|
||||
|
||||
self.draw_query_group_ui(self, ifc_selector, layout)
|
||||
|
||||
if len(ifc_selector.groups) != 0:
|
||||
row = layout.row(align=True)
|
||||
row.prop(ifc_selector, "selector_query_syntax", text="Query Syntax")
|
||||
row.prop(ifc_selector, "manual_override", text="")
|
||||
|
||||
select = row.operator("bim.filter_model_elements", text="", icon="RESTRICT_SELECT_OFF")
|
||||
select.option = "select"
|
||||
isolate = row.operator("bim.filter_model_elements", text="", icon="ZOOM_SELECTED")
|
||||
isolate.option = "isolate"
|
||||
hide = row.operator("bim.filter_model_elements", text="", icon="HIDE_ON")
|
||||
hide.option = "hide"
|
||||
row.operator("bim.unhide_all_elements", text="", icon="HIDE_OFF")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.alignment = "CENTER"
|
||||
row.operator("bim.save_selector_query", text="Save Query")
|
||||
row.operator("bim.open_query_library", text="Load Query")
|
||||
|
||||
def draw_query_group_ui(self, ifc_selector, layout):
|
||||
for index, group in enumerate(ifc_selector.groups):
|
||||
row = layout.row()
|
||||
row.alignment = "CENTER"
|
||||
row.label(text="or") if index != 0 else None
|
||||
|
||||
box = layout.box()
|
||||
row = box.row(align=True)
|
||||
row.alignment = "CENTER"
|
||||
row.label(text=f"Group #{str(index+1)}")
|
||||
row.context_pointer_set(name="bim_prop_group", data=group)
|
||||
op = row.operator("bim.edit_blender_collection", text="Add query", icon="PLUS")
|
||||
op.option = "add"
|
||||
op.collection = "queries"
|
||||
|
||||
row.context_pointer_set(name="bim_prop_group", data=ifc_selector)
|
||||
op = row.operator("bim.edit_blender_collection", text="Remove selection group")
|
||||
op.option = "remove"
|
||||
op.collection = "groups"
|
||||
op.index = index
|
||||
|
||||
self.draw_query_ui(self, group, box)
|
||||
|
||||
def draw_query_ui(self, group, box):
|
||||
for index, query in enumerate(group.queries):
|
||||
row = box.row()
|
||||
row.alignment = "LEFT"
|
||||
|
||||
row.prop(query, "and_or", text="") if index != 0 else None
|
||||
if query.and_or == "or":
|
||||
row = box.row()
|
||||
row.alignment = "LEFT"
|
||||
row.prop(query, "selector", text="")
|
||||
|
||||
if query.selector in ["IFC Class", "IfcSpatialElement", "IfcElementType"]:
|
||||
row.label(text="Equals")
|
||||
row.prop_search(query, "active_option", query, "options", text="")
|
||||
row.prop_search(query, "active_sub_option", query, "sub_options", text="") if len(
|
||||
query.sub_options
|
||||
) != 0 else None
|
||||
self.draw_filter_ui(self, box, query)
|
||||
|
||||
elif query.selector in ["GlobalId", "Attribute"]:
|
||||
row.prop(query, "negation", text="")
|
||||
row.prop(query, "comparison", text="")
|
||||
row.prop(query, "value", text="")
|
||||
|
||||
row.context_pointer_set(name="bim_prop_group", data=group)
|
||||
op = row.operator("bim.edit_blender_collection", icon="REMOVE", text="")
|
||||
op.option = "remove"
|
||||
op.collection = "queries"
|
||||
op.index = index
|
||||
|
||||
row.context_pointer_set(name="bim_prop_group", data=query)
|
||||
op = (
|
||||
row.operator("bim.edit_blender_collection", text="Add filter")
|
||||
if query.selector == "IFC Class"
|
||||
else None
|
||||
)
|
||||
if op:
|
||||
op.option = "add"
|
||||
op.collection = "filters"
|
||||
|
||||
def draw_filter_ui(self, box, query):
|
||||
for filter_index, f in enumerate(query.filters):
|
||||
row = box.row()
|
||||
row.alignment = "LEFT"
|
||||
row.label(text=" ↪")
|
||||
row.prop(f, "and_or", text="") if filter_index != 0 else None
|
||||
if f.and_or == "or":
|
||||
row = box.row()
|
||||
row.alignment = "LEFT"
|
||||
row.label(text=" ↪")
|
||||
row.prop(f, "selector", text="")
|
||||
|
||||
if f.selector == "Attribute":
|
||||
row.prop(f, "attribute", text="")
|
||||
row.prop(
|
||||
f,
|
||||
"negation",
|
||||
)
|
||||
row.prop(f, "comparison", text="")
|
||||
row.prop(f, "value", text="")
|
||||
|
||||
elif f.selector == "IfcPropertySet":
|
||||
row.prop_search(f, "active_option", f, "options", text="")
|
||||
row.prop_search(f, "active_sub_option", f, "sub_options", text="")
|
||||
row.prop(f, "negation")
|
||||
row.prop(f, "comparison", text="")
|
||||
row.prop(f, "value", text="")
|
||||
|
||||
row.context_pointer_set(name="bim_prop_group", data=query)
|
||||
op = row.operator("bim.edit_blender_collection", icon="REMOVE", text="")
|
||||
op.option = "remove"
|
||||
op.collection = "filters"
|
||||
op.index = filter_index
|
||||
|
||||
@@ -20,7 +20,7 @@ import bpy
|
||||
import blenderbim.bim.helper
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import draw_attributes
|
||||
from blenderbim.bim.helper import draw_attributes, prop_with_search
|
||||
from ifcopenshell.api.structural.data import Data
|
||||
from blenderbim.bim.module.structural.data import StructuralData
|
||||
|
||||
@@ -482,7 +482,7 @@ class BIM_PT_structural_loads(Panel):
|
||||
row.operator("bim.disable_structural_load_editing_ui", text="", icon="SCREEN_BACK")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "structural_load_types", text="")
|
||||
prop_with_search(row, self.props, "structural_load_types", text="")
|
||||
row.operator("bim.add_structural_load", text="", icon="ADD").ifc_class = self.props.structural_load_types
|
||||
else:
|
||||
row.operator("bim.load_structural_loads", text="", icon="GREASEPENCIL")
|
||||
@@ -551,7 +551,7 @@ class BIM_PT_boundary_conditions(Panel):
|
||||
row.operator("bim.disable_boundary_condition_editing_ui", text="", icon="SCREEN_BACK")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "boundary_condition_types", text="")
|
||||
prop_with_search(row, self.props, "boundary_condition_types", text="")
|
||||
row.operator(
|
||||
"bim.add_boundary_condition", text="", icon="ADD"
|
||||
).ifc_class = self.props.boundary_condition_types
|
||||
|
||||
@@ -47,7 +47,7 @@ class BIM_PT_styles(Panel):
|
||||
if self.props.is_editing:
|
||||
row.operator("bim.disable_editing_styles", text="", icon="CANCEL")
|
||||
else:
|
||||
row.prop(self.props, "style_type", text="")
|
||||
blenderbim.bim.helper.prop_with_search(row, self.props, "style_type", text="")
|
||||
row.operator("bim.load_styles", text="", icon="IMPORT").style_type = self.props.style_type
|
||||
return
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
import blenderbim.tool as tool
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -46,7 +47,7 @@ class BIM_PT_systems(Panel):
|
||||
row.operator("bim.disable_system_editing_ui", text="", icon="CANCEL")
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "system_class", text="")
|
||||
prop_with_search(row, self.props, "system_class", text="")
|
||||
row.operator("bim.add_system", text="", icon="ADD")
|
||||
else:
|
||||
row.operator("bim.load_systems", text="", icon="GREASEPENCIL")
|
||||
|
||||
@@ -68,9 +68,3 @@ class BIMUnitProperties(PropertyGroup):
|
||||
conversion_unit_types: EnumProperty(items=get_conversion_unit_types, name="Conversion Unit Types")
|
||||
named_unit_types: EnumProperty(items=get_named_unit_types, name="Named Unit Types")
|
||||
unit_attributes: CollectionProperty(name="Unit Attributes", type=Attribute)
|
||||
|
||||
getter_enum = {
|
||||
"unit_classes": get_unit_classes,
|
||||
"conversion_unit_types": get_conversion_unit_types,
|
||||
"named_unit_types": get_named_unit_types,
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ from . import schema
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.prop import StrProperty
|
||||
from blenderbim.bim.ui import IFCFileSelector
|
||||
from blenderbim.bim.helper import get_enum_items
|
||||
from mathutils import Vector, Matrix, Euler
|
||||
from math import radians
|
||||
|
||||
@@ -527,9 +528,10 @@ class ConfigureVisibility(bpy.types.Operator):
|
||||
|
||||
|
||||
def update_enum_property_search_prop(self, context):
|
||||
for i, prop in enumerate(self.collection_name):
|
||||
for i, prop in enumerate(self.collection_names):
|
||||
if prop.name == self.dummy_name:
|
||||
setattr(context.data, self.prop_name, self.collection_identifier[i].name)
|
||||
setattr(context.data, self.prop_name, self.collection_identifiers[i].name)
|
||||
break
|
||||
|
||||
|
||||
class BIM_OT_enum_property_search(bpy.types.Operator):
|
||||
@@ -537,28 +539,53 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
|
||||
bl_label = "Search For Property"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
dummy_name: bpy.props.StringProperty(name="Property", update=update_enum_property_search_prop)
|
||||
collection_name: bpy.props.CollectionProperty(type=StrProperty)
|
||||
collection_identifier: bpy.props.CollectionProperty(type=StrProperty)
|
||||
collection_names: bpy.props.CollectionProperty(type=StrProperty)
|
||||
collection_identifiers: bpy.props.CollectionProperty(type=StrProperty)
|
||||
prop_name: bpy.props.StringProperty()
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.clear_collections()
|
||||
self.data = context.data
|
||||
getter = self.data.getter_enum.get(self.prop_name, None)
|
||||
if getter is None:
|
||||
items = get_enum_items(self.data, self.prop_name, context)
|
||||
if items is None:
|
||||
return {"FINISHED"}
|
||||
self.collection_name.clear()
|
||||
self.collection_identifier.clear()
|
||||
for item in getter(self.data, context):
|
||||
self.collection_identifier.add().name = item[0]
|
||||
if item[0] == getattr(self.data, self.prop_name):
|
||||
self.dummy_name = item[1] # We found the current enum value
|
||||
self.collection_name.add().name = item[1]
|
||||
self.add_items_regular(items)
|
||||
self.add_items_suggestions()
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def execute(self, context):
|
||||
return {"FINISHED"}
|
||||
|
||||
def draw(self, context):
|
||||
# Mandatory to access context.data in update :
|
||||
self.layout.context_pointer_set(name="data", data=self.data)
|
||||
self.layout.prop_search(self, "dummy_name", self, "collection_name")
|
||||
self.layout.prop_search(self, "dummy_name", self, "collection_names")
|
||||
|
||||
def execute(self, context):
|
||||
return {"FINISHED"}
|
||||
|
||||
def clear_collections(self):
|
||||
self.collection_names.clear()
|
||||
self.collection_identifiers.clear()
|
||||
|
||||
def add_item(self, identifier: str, name: str):
|
||||
self.collection_identifiers.add().name = identifier
|
||||
self.collection_names.add().name = name
|
||||
|
||||
def add_items_regular(self, items):
|
||||
self.identifiers = []
|
||||
for item in items:
|
||||
self.identifiers.append(item[0])
|
||||
self.add_item(identifier=item[0], name=item[1])
|
||||
if item[0] == getattr(self.data, self.prop_name):
|
||||
self.dummy_name = item[1] # We found the current enum name
|
||||
|
||||
def add_items_suggestions(self):
|
||||
getter_suggestions = getattr(self.data, "getter_enum_suggestions", None)
|
||||
if getter_suggestions is not None:
|
||||
mapping = getter_suggestions.get(self.prop_name)
|
||||
if mapping is None:
|
||||
return
|
||||
for key, values in mapping().items():
|
||||
if key in self.identifiers:
|
||||
if not isinstance(values, (tuple, list)):
|
||||
values = [values]
|
||||
for value in values:
|
||||
self.add_item(identifier=key, name=key + " (" + value + ")")
|
||||
|
||||
@@ -240,10 +240,6 @@ class Attribute(PropertyGroup):
|
||||
value = str(value)
|
||||
setattr(self, self.get_value_name(), value)
|
||||
|
||||
getter_enum = {
|
||||
"enum_value": getAttributeEnumValues,
|
||||
}
|
||||
|
||||
|
||||
class ModuleVisibility(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
|
||||
@@ -65,7 +65,7 @@ class Material(blenderbim.core.tool.Material):
|
||||
materials = sorted(tool.Ifc.get().by_type(material_type), key=get_name)
|
||||
categories = {}
|
||||
if material_type == "IfcMaterial":
|
||||
[categories.setdefault(m.Category, []).append(m) for m in materials]
|
||||
[categories.setdefault(getattr(m, "Category", "Uncategorised"), []).append(m) for m in materials]
|
||||
for category, mats in categories.items():
|
||||
cat = props.materials.add()
|
||||
cat.name = category or ""
|
||||
|
||||
@@ -108,12 +108,12 @@ On Windows:
|
||||
$ mklink /D "\path\to\blender\X.XX\scripts\addons\blenderbim\bim" "src\blenderbim\blenderbim\bim"
|
||||
|
||||
# Remove the IfcOpenShell dependency Python code
|
||||
$ rd \S \Q "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\api"
|
||||
$ rd \S \Q "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\util"
|
||||
$ rd /S /Q "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\api"
|
||||
$ rd /S /Q "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\util"
|
||||
|
||||
# Replace them with links to the Git repository
|
||||
$ mklink \D "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\api" "src\ifcopenshell-python\ifcopenshell\api"
|
||||
$ mklink \D "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\util" "src\ifcopenshell-python\ifcopenshell\util"
|
||||
$ mklink /D "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\api" "src\ifcopenshell-python\ifcopenshell\api"
|
||||
$ mklink /D "\path\to\blender\X.XX\scripts\addons\blenderbim\libs\site\packages\ifcopenshell\util" "src\ifcopenshell-python\ifcopenshell\util"
|
||||
|
||||
|
||||
After you modify your code in the Git repository, you will need to restart
|
||||
|
||||
@@ -111,6 +111,17 @@ Scenario: Assign material - Assign a material profile set
|
||||
And I press "bim.assign_material"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Assign material - Assign a material constituent set
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
When I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialConstituentSet"
|
||||
And I press "bim.assign_material"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Select by material
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_materials"
|
||||
|
||||
@@ -1,6 +1,78 @@
|
||||
@pset
|
||||
Feature: Pset
|
||||
|
||||
Scenario: Enable pset editing - object
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I set "active_object.PsetProperties.pset_name" to "Pset_WallCommon"
|
||||
And I press "bim.add_pset(obj='IfcWall/Cube', obj_type='Object')"
|
||||
And the variable "pset" is "{ifc}.by_type('IfcPropertySet')[-1].id()"
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='IfcWall/Cube', obj_type='Object')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Enable pset editing - material
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterial"
|
||||
And I press "bim.assign_material"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.add_pset(obj='Default', obj_type='Material')"
|
||||
And the variable "pset" is "{ifc}.by_type('IfcMaterialProperties')[-1].id()"
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='Default', obj_type='Material')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Enable pset editing - profile
|
||||
Given an empty IFC project
|
||||
And I add an empty
|
||||
And the object "Empty" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_material(obj='')"
|
||||
And I set "active_object.BIMObjectMaterialProperties.material_type" to "IfcMaterialProfileSet"
|
||||
And I press "bim.assign_material"
|
||||
And I press "bim.enable_editing_assigned_material()"
|
||||
And the variable "profile_set" is "{ifc}.by_type('IfcMaterialProfileSet')[-1].id()"
|
||||
And I press "bim.add_profile(profile_set={profile_set})"
|
||||
And the variable "material_profile" is "{ifc}.by_type('IfcMaterialProfile')[-1].id()"
|
||||
And I press "bim.enable_editing_material_set_item(material_set_item={material_profile})"
|
||||
And I set "active_object.BIMObjectMaterialProperties.profile_classes" to "IfcParameterizedProfileDef"
|
||||
And I press "bim.assign_parameterized_profile(ifc_class="IfcIShapeProfileDef", material_profile={material_profile})"
|
||||
And I press "bim.load_profiles"
|
||||
And I set "scene.ProfilePsetProperties.pset_name" to "Pset_ProfileMechanical"
|
||||
And I press "bim.add_pset(obj_type='Profile')"
|
||||
And the variable "pset" is "{ifc}.by_type('IfcProfileProperties')[-1].id()"
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='', obj_type='Profile')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Enable pset editing - work schedule
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_work_schedule"
|
||||
And the variable "work_schedule" is "{ifc}.by_type('IfcWorkSchedule')[0].id()"
|
||||
And I press "bim.enable_editing_tasks(work_schedule={work_schedule})"
|
||||
And I set "scene.WorkSchedulePsetProperties.pset_name" to "Pset_WorkControlCommon"
|
||||
And I press "bim.add_pset(obj_type='WorkSchedule')"
|
||||
And the variable "pset" is "{ifc}.by_type('IfcPropertySet')[-1].id()"
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='', obj_type='WorkSchedule')"
|
||||
|
||||
Scenario: Enable pset editing - resource
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_resources"
|
||||
And I press "bim.add_resource(ifc_class='IfcSubContractResource', resource=0)"
|
||||
And I set "scene.ResourcePsetProperties.pset_name" to "Pset_ConstructionResource"
|
||||
And I press "bim.add_pset(obj_type='Resource')"
|
||||
And the variable "pset" is "{ifc}.by_type('IfcPropertySet')[-1].id()"
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='', obj_type='Resource')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Copy property to selected - copy property
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
@@ -17,26 +89,6 @@ Scenario: Copy property to selected - copy property
|
||||
And I press "bim.add_pset(obj='IfcWall/Cube.001', obj_type='Object')"
|
||||
And the variable "pset" is "{ifc}.by_type('IfcPropertySet')[-1].id()"
|
||||
And I press "bim.enable_pset_editing(obj='IfcWall/Cube.001', obj_type='Object', pset_id={pset})"
|
||||
And I set "active_object.PsetProperties.properties[2].string_value" to "Foo"
|
||||
And I set "active_object.PsetProperties.properties[2].metadata.string_value" to "Foo"
|
||||
When I press "bim.copy_property_to_selection(name='FireRating')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Copy property to selected - copy quantity
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And additionally the object "IfcWall/Cube.001" is selected
|
||||
And I set "active_object.PsetProperties.pset_name" to "Pset_BuildingElementCommon"
|
||||
And I press "bim.add_qto(obj='IfcWall/Cube.001', obj_type='Object')"
|
||||
And the variable "qto" is "{ifc}.by_type('IfcQuantitySet')[-1].id()"
|
||||
And I press "bim.enable_pset_editing(obj='IfcWall/Cube.001', obj_type='Object', pset_id={qto})"
|
||||
And I set "active_object.PsetProperties.properties[0].float_value" to "1"
|
||||
When I press "bim.copy_property_to_selection(name='Length')"
|
||||
Then nothing happens
|
||||
|
||||
Reference in New Issue
Block a user