Fix bug when shifting object placements and their children. Add support for specifying placement matrixes in project units or SI units.

This commit is contained in:
Dion Moult
2021-09-17 16:54:53 +10:00
parent 02a68f6827
commit 2b82eaf019
2 changed files with 369 additions and 20 deletions
@@ -8,40 +8,35 @@ import ifcopenshell.util.placement
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"product": None, "matrix": np.eye(4), "should_transform_children": False}
self.settings = {"product": None, "matrix": np.eye(4), "is_si": True, "should_transform_children": False}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
print('EXECUTING', self.settings["product"])
if not hasattr(self.settings["product"], "ObjectPlacement"):
return
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
if not self.settings["is_si"]:
self.settings["matrix"][0][3] *= self.unit_scale
self.settings["matrix"][1][3] *= self.unit_scale
self.settings["matrix"][2][3] *= self.unit_scale
children_settings = []
if not self.settings["should_transform_children"]:
print("THIS IS RUNNING")
children_settings = self.get_children_settings(self.settings["product"].ObjectPlacement)
placement_rel_to = None
if hasattr(self.settings["product"], "ContainedInStructure") and self.settings["product"].ContainedInStructure:
placement_rel_to = self.settings["product"].ContainedInStructure[0].RelatingStructure.ObjectPlacement
elif hasattr(self.settings["product"], "Decomposes") and self.settings["product"].Decomposes:
relating_object = self.settings["product"].Decomposes[0].RelatingObject
placement_rel_to = relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
elif hasattr(self.settings["product"], "VoidsElements") and self.settings["product"].VoidsElements:
relating_object = self.settings["product"].VoidsElements[0].RelatingBuildingElement
placement_rel_to = relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
elif hasattr(self.settings["product"], "FillsVoids") and self.settings["product"].FillsVoids:
relating_object = self.settings["product"].FillsVoids[0].RelatingOpeningElement
placement_rel_to = relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
elif hasattr(self.settings["product"], "ProjectsElements") and self.settings["product"].ProjectsElements:
relating_object = self.settings["product"].ProjectsElements[0].RelatingElement
placement_rel_to = relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
placement_rel_to = self.get_placement_rel_to()
placement = self.file.createIfcLocalPlacement(placement_rel_to, self.get_relative_placement(placement_rel_to))
old_placement = self.settings["product"].ObjectPlacement
if old_placement and len(self.file.get_inverse(old_placement)) == 1:
old_placement.PlacementRelTo = None
if old_placement:
self.settings["product"].ObjectPlacement = None
inverses = self.file.get_inverse(old_placement)
for inverse in inverses:
ifcopenshell.util.element.replace_attribute(inverse, old_placement, placement)
old_placement.PlacementRelTo = None
ifcopenshell.util.element.remove_deep(self.file, old_placement)
self.settings["product"].ObjectPlacement = placement
@@ -53,6 +48,22 @@ class Usecase:
return placement
def get_placement_rel_to(self):
if getattr(self.settings["product"], "ContainedInStructure", None):
return self.settings["product"].ContainedInStructure[0].RelatingStructure.ObjectPlacement
elif getattr(self.settings["product"], "Decomposes", None):
relating_object = self.settings["product"].Decomposes[0].RelatingObject
return relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
elif getattr(self.settings["product"], "VoidsElements", None):
relating_object = self.settings["product"].VoidsElements[0].RelatingBuildingElement
return relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
elif getattr(self.settings["product"], "FillsVoids", None):
relating_object = self.settings["product"].FillsVoids[0].RelatingOpeningElement
return relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
elif getattr(self.settings["product"], "ProjectsElements", None):
relating_object = self.settings["product"].ProjectsElements[0].RelatingElement
return relating_object.ObjectPlacement if hasattr(relating_object, "ObjectPlacement") else None
def get_children_settings(self, placement):
if not placement:
return []
@@ -60,7 +71,7 @@ class Usecase:
for referenced_placement in placement.ReferencedByPlacements:
matrix = ifcopenshell.util.placement.get_local_placement(referenced_placement)
for obj in referenced_placement.PlacesObject:
results.append({"product": obj, "matrix": matrix, "should_transform_children": False})
results.append({"product": obj, "matrix": matrix, "is_si": self.settings["is_si"], "should_transform_children": False})
results.extend(self.get_children_settings(referenced_placement))
return results
@@ -0,0 +1,338 @@
import numpy
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.placement
class TestEditObjectPlacement(test.bootstrap.IFC4):
def test_attemping_to_edit_the_placement_of_an_invalid_element(self):
project = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
result = ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=project)
assert result is None
def test_setting_an_object_placement(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix)
def test_setting_an_object_placement_using_si_units(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
matrix_millimeters = numpy.array(
(
(1.0, 0.0, 0.0, 1000.0),
(0.0, 1.0, 0.0, 2000.0),
(0.0, 0.0, 1.0, 3000.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=element, matrix=matrix, is_si=True)
assert numpy.array_equal(
ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix_millimeters
)
def test_changing_an_object_placement(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
matrix1 = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
matrix2 = numpy.array(
(
(1.0, 0.0, 0.0, 4.0),
(0.0, 1.0, 0.0, 5.0),
(0.0, 0.0, 1.0, 6.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix1.copy(), is_si=False
)
created_element_ids = [e.id() for e in self.file.traverse(element.ObjectPlacement)]
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix2.copy(), is_si=False
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix2)
for element_id in created_element_ids:
with pytest.raises(RuntimeError):
self.file.by_id(element_id)
def test_changing_placements_relative_to_a_spatial_container(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 1.0),
(0.0, 0.0, 1.0, 1.0),
(0.0, 0.0, 0.0, 1.0),
)
)
submatrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run("spatial.assign_container", self.file, product=subelement, relating_structure=element)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), submatrix)
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement
def test_changing_placements_relative_to_an_aggregate(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBeam")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 1.0),
(0.0, 0.0, 1.0, 1.0),
(0.0, 0.0, 0.0, 1.0),
)
)
submatrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), submatrix)
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement
def test_changing_placements_relative_to_a_voided_element(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcOpeningElement")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 1.0),
(0.0, 0.0, 1.0, 1.0),
(0.0, 0.0, 0.0, 1.0),
)
)
submatrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run("void.add_opening", self.file, opening=subelement, element=element)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), submatrix)
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement
def test_changing_placements_relative_to_an_opening(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcOpeningElement")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcDoor")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 1.0),
(0.0, 0.0, 1.0, 1.0),
(0.0, 0.0, 0.0, 1.0),
)
)
submatrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run("void.add_filling", self.file, element=subelement, opening=element)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), submatrix)
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement
def test_changing_placements_relative_to_a_projected_element(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProjectionElement")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 1.0),
(0.0, 0.0, 1.0, 1.0),
(0.0, 0.0, 0.0, 1.0),
)
)
submatrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
self.file.create_entity(
"IfcRelProjectsElement",
**{
"GlobalId": ifcopenshell.guid.new(),
"RelatingElement": element,
"RelatedFeatureElement": subelement,
}
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), matrix)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), submatrix)
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement
def test_changing_placements_without_affecting_children(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 1.0),
(0.0, 0.0, 1.0, 1.0),
(0.0, 0.0, 0.0, 1.0),
)
)
submatrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run("spatial.assign_container", self.file, product=subelement, relating_structure=element)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=submatrix.copy(), is_si=False
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), submatrix)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), submatrix)
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement
def test_changing_placements_with_affecting_children(self):
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
ifcopenshell.api.run("unit.assign_unit", self.file)
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
matrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 1.0),
(0.0, 0.0, 1.0, 1.0),
(0.0, 0.0, 0.0, 1.0),
)
)
submatrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 2.0),
(0.0, 0.0, 1.0, 3.0),
(0.0, 0.0, 0.0, 1.0),
)
)
shifted_submatrix = numpy.array(
(
(1.0, 0.0, 0.0, 1.0),
(0.0, 1.0, 0.0, 3.0),
(0.0, 0.0, 1.0, 5.0),
(0.0, 0.0, 0.0, 1.0),
)
)
ifcopenshell.api.run("spatial.assign_container", self.file, product=subelement, relating_structure=element)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement", self.file, product=subelement, matrix=submatrix.copy(), is_si=False
)
ifcopenshell.api.run(
"geometry.edit_object_placement",
self.file,
product=element,
matrix=submatrix.copy(),
is_si=False,
should_transform_children=True,
)
assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement), submatrix)
assert numpy.array_equal(
ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), shifted_submatrix
)
assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement