diff --git a/src/blenderbim/blenderbim/bim/module/geometry/data.py b/src/blenderbim/blenderbim/bim/module/geometry/data.py index d72efb2934..4bd32ab0fa 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/data.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/data.py @@ -108,7 +108,8 @@ class RepresentationsData: matching_shape_aspects.append(shape_aspect) # blender enum items - return [(str(s.id()), s.Name or "Unnamed", "") for s in matching_shape_aspects] + new_shape_aspect = [("NEW", "Create A New Shape Aspect", "")] + return new_shape_aspect + [(str(s.id()), s.Name or "Unnamed", "") for s in matching_shape_aspects] class RepresentationItemsData: diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 1fa3f14cd0..67433d5c27 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -1692,7 +1692,7 @@ class EnableEditingRepresentationItemShapeAspect(bpy.types.Operator, Operator): props = context.active_object.BIMGeometryProperties props.is_editing_item_shape_aspect = True - # set dropdown to currently active style + # set dropdown to currently active shape aspect shape_aspect_id = props.items[props.active_item_index].shape_aspect_id if shape_aspect_id != 0: props.representation_item_shape_aspect = str(shape_aspect_id) @@ -1705,21 +1705,36 @@ class EditRepresentationItemShapeAspect(bpy.types.Operator, Operator): def _execute(self, context): obj = context.active_object + element = tool.Ifc.get_entity(obj) props = obj.BIMGeometryProperties props.is_editing_item_shape_aspect = False ifc_file = tool.Ifc.get() - shape_aspect = ifc_file.by_id(int(props.representation_item_shape_aspect)) + representation_item_id = props.items[props.active_item_index].ifc_definition_id + representation_item = ifc_file.by_id(representation_item_id) + + if props.representation_item_shape_aspect == "NEW": + active_representation = tool.Geometry.get_active_representation(obj) + # find IfcProductRepresentationSelect based on current representation + if hasattr(element, "Representation"): # IfcProduct + product_shape = element.Representation + else: # IfcTypeProduct + for representation_map in element.RepresentationMaps: + if representation_map.MappedRepresentation == active_representation: + product_shape = representation_map + previous_shape_aspect_id = props.items[props.active_item_index].shape_aspect_id + # will be None if item didn't had a shape aspect + previous_shape_aspect = tool.Ifc.get_entity_by_id(previous_shape_aspect_id) + shape_aspect = tool.Geometry.create_shape_aspect(product_shape, active_representation, [representation_item], previous_shape_aspect) + else: + shape_aspect = ifc_file.by_id(int(props.representation_item_shape_aspect)) + tool.Geometry.add_representation_item_to_shape_aspect([representation_item], shape_aspect) # set attributes from UI shape_aspect_attrs = props.shape_aspect_attrs shape_aspect.Name = shape_aspect_attrs.name shape_aspect.Description = shape_aspect_attrs.description - representation_item_id = props.items[props.active_item_index].ifc_definition_id - representation_item = ifc_file.by_id(representation_item_id) - - tool.Geometry.add_representation_item_to_shape_aspect(representation_item, shape_aspect) # reload style ui bpy.ops.bim.disable_editing_representation_items() bpy.ops.bim.enable_editing_representation_items() @@ -1749,7 +1764,7 @@ class RemoveRepresentationItemFromShapeAspect(bpy.types.Operator, Operator): representation_item = ifc_file.by_id(representation_item_id) shape_aspect = ifc_file.by_id(props.items[props.active_item_index].shape_aspect_id) - tool.Geometry.remove_representation_item_from_shape_aspect(representation_item, shape_aspect) + tool.Geometry.remove_representation_items_from_shape_aspect([representation_item], shape_aspect) # reload style ui bpy.ops.bim.disable_editing_representation_items() bpy.ops.bim.enable_editing_representation_items() diff --git a/src/blenderbim/blenderbim/bim/module/geometry/prop.py b/src/blenderbim/blenderbim/bim/module/geometry/prop.py index 0f8edc2521..5b7ba67564 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/prop.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/prop.py @@ -72,9 +72,13 @@ def update_shape_aspect(self, context): shape_aspect_id = self.representation_item_shape_aspect attrs = self.shape_aspect_attrs - shape_aspect = tool.Ifc.get().by_id(int(shape_aspect_id)) - attrs.name = shape_aspect.Name or "" - attrs.description = shape_aspect.Description or "" + if shape_aspect_id == "NEW": # new shape aspect + attrs.name = tool.Blender.get_blender_prop_default_value(attrs, "name") + attrs.description = tool.Blender.get_blender_prop_default_value(attrs, "description") + else: + shape_aspect = tool.Ifc.get().by_id(int(shape_aspect_id)) + attrs.name = shape_aspect.Name or "" + attrs.description = shape_aspect.Description or "" class RepresentationItem(PropertyGroup): diff --git a/src/blenderbim/blenderbim/tool/geometry.py b/src/blenderbim/blenderbim/tool/geometry.py index e398a9f966..57a820bf15 100644 --- a/src/blenderbim/blenderbim/tool/geometry.py +++ b/src/blenderbim/blenderbim/tool/geometry.py @@ -299,6 +299,7 @@ class Geometry(blenderbim.core.tool.Geometry): @classmethod def get_active_representation(cls, obj): + """< IfcShapeRepresentation or None""" if obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.ifc_definition_id: return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id) @@ -744,31 +745,56 @@ class Geometry(blenderbim.core.tool.Geometry): return shape_aspects @classmethod - def remove_representation_item_from_shape_aspect(cls, representation_item, shape_aspect): + def create_shape_aspect(cls, product_shape, base_representation, items, previous_shape_aspect=None): + """ + > `product_shape` - IfcProductDefinitionShape or IfcRepresentationMap\n + > `base_representation` - base representation to get context attributes from\n + > `items` - representation items\n + > `previous_shape_aspect` - (optional) previous shape aspect, if provided\n + items will be removed the previous shape aspect first\n + + < IfcShapeAspect + """ + + if previous_shape_aspect is not None: + cls.remove_representation_items_from_shape_aspect(items, previous_shape_aspect) + + shape_aspect = tool.Ifc.get().createIfcShapeAspect( + PartOfProductDefinitionShape=product_shape, ShapeRepresentations=() + ) + # keep IfcShapeAspect and IfcShapeRepresentation valid + rep = tool.Geometry.add_shape_aspect_representation(shape_aspect, base_representation) + rep.Items = items + + return shape_aspect + + @classmethod + def remove_representation_items_from_shape_aspect(cls, representation_items, shape_aspect): ifc_file = tool.Ifc.get() # as shape aspect might have multiple representations # it's easier to find it from the item - for inverse in ifc_file.get_inverse(representation_item): + for inverse in ifc_file.get_inverse(representation_items[0]): if inverse.is_a("IfcShapeRepresentation") and shape_aspect in inverse.OfShapeAspect: representation = inverse break # removing last item would make representation invalid - if len(representation.Items) == 1: + if len(representation.Items) == len(representation_items): # removing last representation would make shape aspect invalid. # remove shape aspect first otherwise remove_representation won't remove it because of the inverse if len(shape_aspect.ShapeRepresentations) == 1: ifc_file.remove(shape_aspect) tool.Ifc.run("geometry.remove_representation", representation=representation) else: - items = set(representation.Items) - {representation_item} + items = set(representation.Items) - set(representation_items) representation.Items = tuple(items) @classmethod - def add_representation_item_to_shape_aspect(cls, representation_item, shape_aspect): + def add_representation_item_to_shape_aspect(cls, representation_items, shape_aspect): + """NOTE: we assume that all items belonged to the same representation and to the same shape aspect""" ifc_file = tool.Ifc.get() previous_shape_aspect = None - for inverse in ifc_file.get_inverse(representation_item): + for inverse in ifc_file.get_inverse(representation_items[0]): if inverse.is_a("IfcShapeRepresentation"): if inverse.OfShapeAspect: # item is already added to the shape aspect @@ -780,11 +806,11 @@ class Geometry(blenderbim.core.tool.Geometry): # remove item from previous shape aspect if previous_shape_aspect: - cls.remove_representation_item_from_shape_aspect(representation_item, previous_shape_aspect) + cls.remove_representation_items_from_shape_aspect(representation_items, previous_shape_aspect) shape_aspect_representation = cls.get_shape_aspect_representation( shape_aspect, base_representation, create_new=True ) - shape_aspect_representation.Items = shape_aspect_representation.Items + (representation_item,) + shape_aspect_representation.Items = shape_aspect_representation.Items + tuple(representation_items) @classmethod def get_shape_aspect_representation(cls, shape_aspect, base_representation, create_new=False): @@ -799,11 +825,15 @@ class Geometry(blenderbim.core.tool.Geometry): if not create_new: return None + return cls.add_shape_aspect_representation(shape_aspect, base_representation) + + @classmethod + def add_shape_aspect_representation(cls, shape_aspect, base_representation): shape_aspect_representation = tool.Ifc.get().createIfcShapeRepresentation( ContextOfItems=base_representation.ContextOfItems, RepresentationIdentifier=base_representation.RepresentationIdentifier, RepresentationType=base_representation.RepresentationType, ) shape_aspect.ShapeRepresentations = shape_aspect.ShapeRepresentations + (shape_aspect_representation,) - return shape_aspect_representation +