mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Non dynamically voided objects now auto update when their opening is removed
This commit is contained in:
@@ -21,7 +21,6 @@ import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.void.data import Data
|
||||
from ifcopenshell.api.context.data import Data as ContextData
|
||||
|
||||
|
||||
class AddOpening(bpy.types.Operator):
|
||||
@@ -87,6 +86,7 @@ class RemoveOpening(bpy.types.Operator):
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
||||
self.file = IfcStore.get_file()
|
||||
is_modifier_removed = False
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type != "BOOLEAN":
|
||||
continue
|
||||
@@ -94,10 +94,19 @@ class RemoveOpening(bpy.types.Operator):
|
||||
IfcStore.unlink_element(obj=modifier.object)
|
||||
if "/" in modifier.object.name and modifier.object.name[0:3] == "Ifc":
|
||||
modifier.object.name = "/".join(modifier.object.name.split("/")[1:])
|
||||
is_modifier_removed = True
|
||||
obj.modifiers.remove(modifier)
|
||||
break
|
||||
|
||||
ifcopenshell.api.run("void.remove_opening", self.file, **{"opening": self.file.by_id(self.opening_id)})
|
||||
|
||||
if not is_modifier_removed:
|
||||
bpy.ops.bim.switch_representation(
|
||||
ifc_definition_id=obj.data.BIMMeshProperties.ifc_definition_id,
|
||||
should_reload=True,
|
||||
should_switch_all_meshes=True,
|
||||
)
|
||||
|
||||
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -60,11 +60,19 @@ def i_add_a_cube():
|
||||
bpy.ops.mesh.primitive_cube_add()
|
||||
|
||||
|
||||
def i_add_a_cube_of_size_size_at_location(size, location):
|
||||
bpy.ops.mesh.primitive_cube_add(size=float(size), location=[float(co) for co in location.split(",")])
|
||||
|
||||
|
||||
def the_object_name_is_selected(name):
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
additionally_the_object_name_is_selected(name)
|
||||
|
||||
|
||||
def additionally_the_object_name_is_selected(name):
|
||||
obj = bpy.context.scene.objects.get(name)
|
||||
if not obj:
|
||||
assert False, 'The object "{name}" could not be selected'
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
|
||||
@@ -81,7 +89,10 @@ def i_enable_prop(prop):
|
||||
|
||||
|
||||
def i_press_operator(operator):
|
||||
exec(f"bpy.ops.{operator}()")
|
||||
if "(" in operator:
|
||||
exec(f"bpy.ops.{operator}")
|
||||
else:
|
||||
exec(f"bpy.ops.{operator}()")
|
||||
|
||||
|
||||
def the_object_name_exists(name):
|
||||
@@ -153,14 +164,53 @@ def the_file_name_should_contain_value(name, value):
|
||||
assert value in f.read()
|
||||
|
||||
|
||||
def the_object_name1_has_a_boolean_difference_by_name2(name1, name2):
|
||||
obj = the_object_name_exists(name1)
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type == "BOOLEAN" and modifier.object.name == name2:
|
||||
return True
|
||||
assert False, "No boolean found"
|
||||
|
||||
|
||||
def the_object_name1_has_no_boolean_difference_by_name2(name1, name2):
|
||||
obj = the_object_name_exists(name1)
|
||||
for modifier in obj.modifiers:
|
||||
if modifier.type == "BOOLEAN" and modifier.object.name == name2:
|
||||
assert False, "A boolean was found"
|
||||
|
||||
|
||||
def the_object_name_is_voided_by_void(name, void):
|
||||
ifc = IfcStore.get_file()
|
||||
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id)
|
||||
for rel in element.HasOpenings:
|
||||
if rel.RelatedOpeningElement.Name == void:
|
||||
return True
|
||||
assert False, "No void found"
|
||||
|
||||
|
||||
def the_object_name_is_not_voided_by_void(name, void):
|
||||
ifc = IfcStore.get_file()
|
||||
element = ifc.by_id(the_object_name_exists(name).BIMObjectProperties.ifc_definition_id)
|
||||
for rel in element.HasOpenings:
|
||||
if rel.RelatedOpeningElement.Name == void:
|
||||
assert False, "A void was found"
|
||||
|
||||
|
||||
def the_object_name_should_display_as_mode(name, mode):
|
||||
assert the_object_name_exists(name).display_type == mode
|
||||
|
||||
|
||||
def the_object_name_has_number_vertices(name, number):
|
||||
total = len(the_object_name_exists(name).data.vertices)
|
||||
assert total == int(number), f"We found {total} vertices"
|
||||
|
||||
|
||||
definitions = {
|
||||
"an empty IFC project": an_empty_ifc_project,
|
||||
"I add a cube": i_add_a_cube,
|
||||
'I add a cube of size "([0-9]+)" at "(.*)"': i_add_a_cube_of_size_size_at_location,
|
||||
'the object "(.*)" is selected': the_object_name_is_selected,
|
||||
'additionally the object "(.*)" is selected': additionally_the_object_name_is_selected,
|
||||
'I set "(.*)" to "(.*)"': i_set_prop_to_value,
|
||||
'I enable "(.*)"': i_enable_prop,
|
||||
'I press "(.*)"': i_press_operator,
|
||||
@@ -174,20 +224,28 @@ definitions = {
|
||||
"I duplicate the selected objects": i_duplicate_the_selected_objects,
|
||||
'the object "(.*)" and "(.*)" are different elements': the_object_name1_and_name2_are_different_elements,
|
||||
'the file "(.*)" should contain "(.*)"': the_file_name_should_contain_value,
|
||||
'the object "(.*)" has a boolean difference by "(.*)"': the_object_name1_has_a_boolean_difference_by_name2,
|
||||
'the object "(.*)" has no boolean difference by "(.*)"': the_object_name1_has_no_boolean_difference_by_name2,
|
||||
'the object "(.*)" is voided by "(.*)"': the_object_name_is_voided_by_void,
|
||||
'the object "(.*)" is not voided by "(.*)"': the_object_name_is_not_voided_by_void,
|
||||
'the object "(.*)" should display as "(.*)"': the_object_name_should_display_as_mode,
|
||||
'the object "(.*)" has "([0-9]+)" vertices': the_object_name_has_number_vertices,
|
||||
}
|
||||
|
||||
|
||||
# Super lightweight Gherkin implementation
|
||||
def run(scenario):
|
||||
keywords = ["Given", "When", "Then", "And", "But"]
|
||||
for line in scenario.split("\n"):
|
||||
line = line.strip()
|
||||
line = line.replace("{cwd}", os.getcwd())
|
||||
for keyword in keywords:
|
||||
line = line.replace(keyword, "")
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
match = None
|
||||
for definition, callback in definitions.items():
|
||||
match = re.search(definition, line)
|
||||
match = re.search("^" + definition + "$", line)
|
||||
if match:
|
||||
try:
|
||||
callback(*match.groups())
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
import test.bim.bootstrap
|
||||
|
||||
|
||||
class TestAddOpening(test.bim.bootstrap.NewFile):
|
||||
@test.bim.bootstrap.scenario
|
||||
def test_adding_an_opening(self):
|
||||
return """
|
||||
Given an empty IFC project
|
||||
When 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 "IfcWall/Cube" is selected
|
||||
And additionally the object "Cube" is selected
|
||||
And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')"
|
||||
Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement"
|
||||
And the object "IfcOpeningElement/Cube" should display as "WIRE"
|
||||
And the object "IfcWall/Cube" has a boolean difference by "IfcOpeningElement/Cube"
|
||||
And the object "IfcWall/Cube" is voided by "Cube"
|
||||
"""
|
||||
|
||||
|
||||
class TestRemoveOpening(test.bim.bootstrap.NewFile):
|
||||
@test.bim.bootstrap.scenario
|
||||
def test_removing_an_opening(self):
|
||||
return """
|
||||
Given an empty IFC project
|
||||
When 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 of size "1" at "1,0,0"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And additionally the object "Cube" is selected
|
||||
And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')"
|
||||
And I press "bim.remove_opening(opening_id=97, obj='IfcWall/Cube')"
|
||||
Then the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube"
|
||||
And the object "IfcWall/Cube" is not voided by "Cube"
|
||||
"""
|
||||
|
||||
@test.bim.bootstrap.scenario
|
||||
def test_removing_a_non_dynamic_opening(self):
|
||||
return """
|
||||
Given an empty IFC project
|
||||
When 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 of size "1" at "1,0,0"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And additionally the object "Cube" is selected
|
||||
And I press "bim.add_opening(opening='Cube', obj='IfcWall/Cube')"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.print_ifc_file"
|
||||
And I press "bim.switch_representation(ifc_definition_id=86, should_reload=True)"
|
||||
Then the object "IfcWall/Cube" has no boolean difference by "IfcOpeningElement/Cube"
|
||||
And the object "IfcWall/Cube" has "16" vertices
|
||||
When I press "bim.remove_opening(opening_id=97, obj='IfcWall/Cube')"
|
||||
Then the object "IfcWall/Cube" has "8" vertices
|
||||
"""
|
||||
Reference in New Issue
Block a user