mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 06:32:09 +00:00
Fixed issues applying arrays
because pset wasn't removed BBIM was recognizing those elements as parts of an array resulting in issues during deletion and duplication. This issue is a regression after ec4f0e5, the root problem was that `keep_objs` wasn't working with `sync_children`.
Since `keep_objs` was used only for applying array layers I've replaced it with `array_layers_to_apply` that works for both `sync_children` = True and False.
https://community.osarch.org/discussion/1831/duplicating-failed-for-array-items-after-apply-array
This commit is contained in:
@@ -189,7 +189,7 @@ class RemoveArray(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
if not self.keep_objs:
|
if not self.keep_objs:
|
||||||
data[self.item]["count"] = 1
|
data[self.item]["count"] = 1
|
||||||
tool.Model.regenerate_array(parent, data, self.keep_objs)
|
tool.Model.regenerate_array(parent, data, array_layers_to_apply=[self.item] if self.keep_objs else [])
|
||||||
|
|
||||||
pset = tool.Ifc.get().by_id(pset["id"])
|
pset = tool.Ifc.get().by_id(pset["id"])
|
||||||
if len(data) == 1:
|
if len(data) == 1:
|
||||||
|
|||||||
@@ -624,13 +624,16 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def regenerate_array(cls, parent_obj, data, keep_objs=False):
|
def regenerate_array(cls, parent_obj, data, array_layers_to_apply=tuple()):
|
||||||
|
"""`array_layers_to_apply` - list of array layer indices to apply"""
|
||||||
tool.Blender.Modifier.Array.remove_constraints(tool.Ifc.get_entity(parent_obj))
|
tool.Blender.Modifier.Array.remove_constraints(tool.Ifc.get_entity(parent_obj))
|
||||||
|
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
obj_stack = [parent_obj]
|
obj_stack = [parent_obj]
|
||||||
|
|
||||||
for array in data:
|
for array_i, array in enumerate(data):
|
||||||
|
# for `sync_children` we remove all previously generated children to regenerate them again
|
||||||
|
# to assure they are in complete sync (psets, etc) with the array parent
|
||||||
if array["sync_children"]:
|
if array["sync_children"]:
|
||||||
removed_children = set(array["children"])
|
removed_children = set(array["children"])
|
||||||
for removed_child in removed_children:
|
for removed_child in removed_children:
|
||||||
@@ -645,16 +648,21 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
total_existing_children = len(array["children"])
|
total_existing_children = len(array["children"])
|
||||||
children_elements = []
|
children_elements = []
|
||||||
children_objs = []
|
children_objs = []
|
||||||
|
|
||||||
|
# calculate offset
|
||||||
if array["method"] == "DISTRIBUTE":
|
if array["method"] == "DISTRIBUTE":
|
||||||
divider = 1 if ((array["count"] - 1) == 0) else (array["count"] - 1)
|
divider = 1 if ((array["count"] - 1) == 0) else (array["count"] - 1)
|
||||||
base_offset = Vector([array["x"] / divider, array["y"] / divider, array["z"] / divider]) * unit_scale
|
base_offset = Vector([array["x"], array["y"], array["z"]]) / divider * unit_scale
|
||||||
else:
|
else:
|
||||||
base_offset = Vector([array["x"], array["y"], array["z"]]) * unit_scale
|
base_offset = Vector([array["x"], array["y"], array["z"]]) * unit_scale
|
||||||
|
|
||||||
for i in range(array["count"]):
|
for i in range(array["count"]):
|
||||||
if i == 0:
|
if i == 0:
|
||||||
continue
|
continue
|
||||||
offset = base_offset * i
|
offset = base_offset * i
|
||||||
|
|
||||||
for obj in obj_stack:
|
for obj in obj_stack:
|
||||||
|
# get currently proccesed array element and it's object
|
||||||
if child_i >= total_existing_children:
|
if child_i >= total_existing_children:
|
||||||
child_obj = tool.Spatial.duplicate_object_and_data(obj)
|
child_obj = tool.Spatial.duplicate_object_and_data(obj)
|
||||||
child_element = tool.Spatial.run_root_copy_class(obj=child_obj)
|
child_element = tool.Spatial.run_root_copy_class(obj=child_obj)
|
||||||
@@ -668,16 +676,17 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
child_obj = tool.Spatial.duplicate_object_and_data(obj)
|
child_obj = tool.Spatial.duplicate_object_and_data(obj)
|
||||||
child_element = tool.Spatial.run_root_copy_class(obj=child_obj)
|
child_element = tool.Spatial.run_root_copy_class(obj=child_obj)
|
||||||
|
|
||||||
child_psets = ifcopenshell.util.element.get_psets(child_element)
|
# add child pset
|
||||||
child_pset = child_psets.get("BBIM_Array")
|
child_pset = tool.Pset.get_element_pset(child_element, "BBIM_Array")
|
||||||
if child_pset:
|
if child_pset:
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"pset.edit_pset",
|
"pset.edit_pset",
|
||||||
tool.Ifc.get(),
|
tool.Ifc.get(),
|
||||||
pset=tool.Ifc.get().by_id(child_pset["id"]),
|
pset=child_pset,
|
||||||
properties={"Data": None},
|
properties={"Data": None},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# set child object position
|
||||||
new_matrix = obj.matrix_world.copy()
|
new_matrix = obj.matrix_world.copy()
|
||||||
if array["use_local_space"]:
|
if array["use_local_space"]:
|
||||||
current_obj_translation = obj.matrix_world @ offset
|
current_obj_translation = obj.matrix_world @ offset
|
||||||
@@ -685,23 +694,29 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
current_obj_translation = obj.matrix_world.translation + offset
|
current_obj_translation = obj.matrix_world.translation + offset
|
||||||
new_matrix.translation = current_obj_translation
|
new_matrix.translation = current_obj_translation
|
||||||
child_obj.matrix_world = new_matrix
|
child_obj.matrix_world = new_matrix
|
||||||
|
|
||||||
children_objs.append(child_obj)
|
children_objs.append(child_obj)
|
||||||
children_elements.append(child_element)
|
children_elements.append(child_element)
|
||||||
child_i += 1
|
child_i += 1
|
||||||
|
|
||||||
obj_stack.extend(children_objs)
|
obj_stack.extend(children_objs)
|
||||||
array["children"] = [e.GlobalId for e in children_elements]
|
array["children"] = [e.GlobalId for e in children_elements]
|
||||||
|
|
||||||
|
# handle elements unused in the array after regeneration
|
||||||
removed_children = set(existing_children) - set(array["children"])
|
removed_children = set(existing_children) - set(array["children"])
|
||||||
for removed_child in removed_children:
|
for removed_child in removed_children:
|
||||||
element = tool.Ifc.get().by_guid(removed_child)
|
element = tool.Ifc.get().by_guid(removed_child)
|
||||||
obj = tool.Ifc.get_object(element)
|
obj = tool.Ifc.get_object(element)
|
||||||
if obj:
|
if obj:
|
||||||
if keep_objs:
|
tool.Geometry.delete_ifc_object(obj)
|
||||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
|
||||||
pset = tool.Ifc.get().by_id(pset["id"])
|
if array_i in array_layers_to_apply:
|
||||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
for child_element in children_elements:
|
||||||
else:
|
pset = tool.Pset.get_element_pset(child_element, "BBIM_Array")
|
||||||
tool.Geometry.delete_ifc_object(obj)
|
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=child_element, pset=pset)
|
||||||
|
|
||||||
|
array["children"] = []
|
||||||
|
array["count"] = 1
|
||||||
|
|
||||||
bpy.context.view_layer.update()
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
|
|||||||
@@ -342,3 +342,81 @@ class TestGenerateStair2DProfile(NewFile):
|
|||||||
expected_profile = (verts_data, edges_data, faces_data)
|
expected_profile = (verts_data, edges_data, faces_data)
|
||||||
generated_profile = subject.generate_stair_2d_profile(**kwargs)
|
generated_profile = subject.generate_stair_2d_profile(**kwargs)
|
||||||
self.compare_data(generated_profile, expected_profile)
|
self.compare_data(generated_profile, expected_profile)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUsingArrays(NewFile):
|
||||||
|
def setup_array(self, add_second_layer=False, sync_children=False):
|
||||||
|
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||||
|
bpy.ops.bim.create_project()
|
||||||
|
|
||||||
|
bpy.ops.mesh.primitive_cube_add()
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
bpy.context.scene.BIMRootProperties.ifc_product = "IfcElement"
|
||||||
|
bpy.ops.bim.assign_class(ifc_class="IfcActuator", predefined_type="ELECTRICACTUATOR", userdefined_type="")
|
||||||
|
|
||||||
|
bpy.ops.bim.add_array()
|
||||||
|
bpy.ops.bim.enable_editing_array(item=0)
|
||||||
|
obj.BIMArrayProperties.count = 4
|
||||||
|
obj.BIMArrayProperties.x = 4
|
||||||
|
obj.BIMArrayProperties.sync_children = sync_children
|
||||||
|
bpy.ops.bim.edit_array(item=0)
|
||||||
|
|
||||||
|
if add_second_layer:
|
||||||
|
bpy.ops.bim.add_array()
|
||||||
|
bpy.ops.bim.enable_editing_array(item=1)
|
||||||
|
obj.BIMArrayProperties.count = 3
|
||||||
|
obj.BIMArrayProperties.y = 4
|
||||||
|
obj.BIMArrayProperties.sync_children = sync_children
|
||||||
|
bpy.ops.bim.edit_array(item=1)
|
||||||
|
|
||||||
|
def test_remove_array_last_to_first(self):
|
||||||
|
self.setup_array(add_second_layer=True)
|
||||||
|
bpy.ops.bim.remove_array(item=1)
|
||||||
|
assert len(bpy.context.selected_objects) == 4
|
||||||
|
bpy.ops.bim.remove_array(item=0)
|
||||||
|
assert len(bpy.context.selected_objects) == 1
|
||||||
|
|
||||||
|
def test_remove_array_first_to_last(self):
|
||||||
|
self.setup_array(add_second_layer=True)
|
||||||
|
bpy.ops.bim.remove_array(item=0)
|
||||||
|
assert len(bpy.context.selected_objects) == 3
|
||||||
|
bpy.ops.bim.remove_array(item=0)
|
||||||
|
assert len(bpy.context.selected_objects) == 1
|
||||||
|
|
||||||
|
def test_apply_array_1_layer(self):
|
||||||
|
self.setup_array()
|
||||||
|
bpy.ops.bim.apply_array()
|
||||||
|
|
||||||
|
objs = bpy.context.selected_objects
|
||||||
|
assert len(objs) == 4
|
||||||
|
# check BBIM_Array psets are removed
|
||||||
|
for obj in objs:
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||||
|
assert pset is None, (obj, pset)
|
||||||
|
|
||||||
|
def test_apply_array_multiple_layers(self):
|
||||||
|
self.setup_array(add_second_layer=True)
|
||||||
|
bpy.ops.bim.apply_array() # apply second layer
|
||||||
|
bpy.ops.bim.apply_array() # apply first layer
|
||||||
|
|
||||||
|
objs = bpy.context.selected_objects
|
||||||
|
assert len(objs) == 12
|
||||||
|
|
||||||
|
# check BBIM_Array psets are removed
|
||||||
|
for obj in objs:
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||||
|
assert pset is None, (obj, pset)
|
||||||
|
|
||||||
|
def test_apply_array_with_sync_children(self):
|
||||||
|
self.setup_array(sync_children=True)
|
||||||
|
bpy.ops.bim.apply_array()
|
||||||
|
|
||||||
|
objs = bpy.context.selected_objects
|
||||||
|
assert len(objs) == 4
|
||||||
|
# check BBIM_Array psets are removed
|
||||||
|
for obj in objs:
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||||
|
assert pset is None, (obj, pset)
|
||||||
|
|||||||
Reference in New Issue
Block a user