mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Optimise object placement change checking, so medium sized project exports are 5 seconds faster at minimum, but can be much higher since false positives are culled.
This commit is contained in:
@@ -132,6 +132,8 @@ class IfcExporter:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
def sync_object_placement(self, obj):
|
def sync_object_placement(self, obj):
|
||||||
|
if not self.has_object_moved(obj):
|
||||||
|
return
|
||||||
blender_matrix = np.array(obj.matrix_world)
|
blender_matrix = np.array(obj.matrix_world)
|
||||||
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
if (obj.scale - Vector((1.0, 1.0, 1.0))).length > 1e-4:
|
if (obj.scale - Vector((1.0, 1.0, 1.0))).length > 1e-4:
|
||||||
@@ -141,24 +143,19 @@ class IfcExporter:
|
|||||||
return self.sync_grid_axis_object_placement(obj, element)
|
return self.sync_grid_axis_object_placement(obj, element)
|
||||||
if not hasattr(element, "ObjectPlacement"):
|
if not hasattr(element, "ObjectPlacement"):
|
||||||
return
|
return
|
||||||
ifc_matrix = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
|
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||||
ifc_matrix[0][3] *= self.unit_scale
|
return element
|
||||||
ifc_matrix[1][3] *= self.unit_scale
|
|
||||||
ifc_matrix[2][3] *= self.unit_scale
|
|
||||||
|
|
||||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
def has_object_moved(self, obj):
|
||||||
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "OBJECT_PLACEMENT":
|
if not obj.BIMObjectProperties.location_checksum:
|
||||||
ifc_matrix = ifcopenshell.util.geolocation.global2local(
|
return True # Let's be conservative
|
||||||
ifc_matrix,
|
loc_check = np.frombuffer(eval(obj.BIMObjectProperties.location_checksum))
|
||||||
float(props.blender_eastings) * self.unit_scale,
|
rot_check = np.frombuffer(eval(obj.BIMObjectProperties.rotation_checksum))
|
||||||
float(props.blender_northings) * self.unit_scale,
|
loc_real = np.array(obj.matrix_world.translation).flatten()
|
||||||
float(props.blender_orthogonal_height) * self.unit_scale,
|
rot_real = np.array(obj.matrix_world.to_3x3()).flatten()
|
||||||
float(props.blender_x_axis_abscissa),
|
if np.allclose(loc_check, loc_real, atol=1e-4) and np.allclose(rot_check, rot_real, atol=1e-2):
|
||||||
float(props.blender_x_axis_ordinate),
|
return False
|
||||||
)
|
return True
|
||||||
if not np.allclose(ifc_matrix, blender_matrix, atol=0.0001):
|
|
||||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
|
||||||
return element
|
|
||||||
|
|
||||||
def sync_grid_axis_object_placement(self, obj, element):
|
def sync_grid_axis_object_placement(self, obj, element):
|
||||||
grid = (element.PartOfU or element.PartOfV or element.PartOfW)[0]
|
grid = (element.PartOfU or element.PartOfV or element.PartOfW)[0]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import ifcopenshell.util.unit
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
import ifcopenshell.util.geolocation
|
import ifcopenshell.util.geolocation
|
||||||
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.drawing.prop import get_diagram_scales
|
from blenderbim.bim.module.drawing.prop import get_diagram_scales
|
||||||
|
|
||||||
@@ -509,7 +510,7 @@ class IfcImporter:
|
|||||||
mesh = self.create_mesh(axis, shape)
|
mesh = self.create_mesh(axis, shape)
|
||||||
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
|
||||||
self.link_element(axis, obj)
|
self.link_element(axis, obj)
|
||||||
obj.matrix_world = grid_obj.matrix_world
|
self.set_matrix_world(obj, grid_obj.matrix_world)
|
||||||
grid_collection.objects.link(obj)
|
grid_collection.objects.link(obj)
|
||||||
|
|
||||||
def create_type_products(self):
|
def create_type_products(self):
|
||||||
@@ -700,7 +701,7 @@ class IfcImporter:
|
|||||||
mesh.from_pydata([mathutils.Vector(vertex) * self.unit_scale], [], [])
|
mesh.from_pydata([mathutils.Vector(vertex) * self.unit_scale], [], [])
|
||||||
|
|
||||||
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
|
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
|
||||||
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, placement_matrix)
|
self.set_matrix_world(obj, self.apply_blender_offset_to_matrix_world(obj, placement_matrix))
|
||||||
self.link_element(product, obj)
|
self.link_element(product, obj)
|
||||||
|
|
||||||
def get_pointcloud_representation(self, product):
|
def get_pointcloud_representation(self, product):
|
||||||
@@ -759,7 +760,7 @@ class IfcImporter:
|
|||||||
mesh.from_pydata(vertex_list, [], [])
|
mesh.from_pydata(vertex_list, [], [])
|
||||||
|
|
||||||
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
|
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
|
||||||
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, placement_matrix)
|
self.set_matrix_world(obj, self.apply_blender_offset_to_matrix_world(obj, placement_matrix))
|
||||||
self.link_element(product, obj)
|
self.link_element(product, obj)
|
||||||
return product
|
return product
|
||||||
|
|
||||||
@@ -826,13 +827,13 @@ class IfcImporter:
|
|||||||
mat = np.array(
|
mat = np.array(
|
||||||
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
([m[0], m[3], m[6], m[9]], [m[1], m[4], m[7], m[10]], [m[2], m[5], m[8], m[11]], [0, 0, 0, 1])
|
||||||
)
|
)
|
||||||
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, mat)
|
self.set_matrix_world(obj, self.apply_blender_offset_to_matrix_world(obj, mat))
|
||||||
self.material_creator.create(element, obj, mesh)
|
self.material_creator.create(element, obj, mesh)
|
||||||
elif mesh:
|
elif mesh:
|
||||||
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))
|
self.set_matrix_world(obj, self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element)))
|
||||||
self.material_creator.create(element, obj, mesh)
|
self.material_creator.create(element, obj, mesh)
|
||||||
elif hasattr(element, "ObjectPlacement"):
|
elif hasattr(element, "ObjectPlacement"):
|
||||||
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))
|
self.set_matrix_world(obj, self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element)))
|
||||||
|
|
||||||
self.add_opening_relation(element, obj)
|
self.add_opening_relation(element, obj)
|
||||||
|
|
||||||
@@ -1518,6 +1519,10 @@ class IfcImporter:
|
|||||||
else:
|
else:
|
||||||
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id)
|
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id)
|
||||||
|
|
||||||
|
def set_matrix_world(self, obj, matrix_world):
|
||||||
|
obj.matrix_world = matrix_world
|
||||||
|
tool.Geometry.record_object_position(obj)
|
||||||
|
|
||||||
|
|
||||||
class IfcImportSettings:
|
class IfcImportSettings:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|||||||
@@ -318,9 +318,8 @@ class BIMObjectProperties(PropertyGroup):
|
|||||||
default="NONE",
|
default="NONE",
|
||||||
)
|
)
|
||||||
is_reassigning_class: BoolProperty(name="Is Reassigning Class")
|
is_reassigning_class: BoolProperty(name="Is Reassigning Class")
|
||||||
global_ids: CollectionProperty(name="GlobalIds", type=GlobalId)
|
location_checksum: StringProperty(name="Location Checksum")
|
||||||
psets: CollectionProperty(name="Psets", type=PsetQto)
|
rotation_checksum: StringProperty(name="Rotation Checksum")
|
||||||
qtos: CollectionProperty(name="Qtos", type=PsetQto)
|
|
||||||
|
|
||||||
|
|
||||||
class BIMMaterialProperties(PropertyGroup):
|
class BIMMaterialProperties(PropertyGroup):
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ def edit_object_placement(ifc, geometry, surveyor, obj=None):
|
|||||||
geometry.clear_cache(element)
|
geometry.clear_cache(element)
|
||||||
geometry.clear_scale(obj)
|
geometry.clear_scale(obj)
|
||||||
ifc.run("geometry.edit_object_placement", product=element, matrix=surveyor.get_absolute_matrix(obj))
|
ifc.run("geometry.edit_object_placement", product=element, matrix=surveyor.get_absolute_matrix(obj))
|
||||||
|
geometry.record_object_position(obj)
|
||||||
|
|
||||||
|
|
||||||
def add_representation(
|
def add_representation(
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ class Geometry:
|
|||||||
def is_mapped_representation(cls, representation): pass
|
def is_mapped_representation(cls, representation): pass
|
||||||
def is_type_product(cls, element): pass
|
def is_type_product(cls, element): pass
|
||||||
def link(cls, element, obj): pass
|
def link(cls, element, obj): pass
|
||||||
|
def record_object_position(cls, obj): pass
|
||||||
def rename_object(cls, obj, name): pass
|
def rename_object(cls, obj, name): pass
|
||||||
def replace_object_with_empty(cls, obj): pass
|
def replace_object_with_empty(cls, obj): pass
|
||||||
def resolve_mapped_representation(cls, representation): pass
|
def resolve_mapped_representation(cls, representation): pass
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import logging
|
import logging
|
||||||
|
import numpy as np
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.core.style
|
import blenderbim.core.style
|
||||||
@@ -223,6 +224,12 @@ class Geometry(blenderbim.core.tool.Geometry):
|
|||||||
def link(cls, element, obj):
|
def link(cls, element, obj):
|
||||||
obj.BIMMeshProperties.ifc_definition_id = element.id()
|
obj.BIMMeshProperties.ifc_definition_id = element.id()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def record_object_position(cls, obj):
|
||||||
|
# These are recorded separately because they have different numerical tolerances
|
||||||
|
obj.BIMObjectProperties.location_checksum = repr(np.array(obj.matrix_world.translation).tobytes())
|
||||||
|
obj.BIMObjectProperties.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3()).tobytes())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def rename_object(cls, obj, name):
|
def rename_object(cls, obj, name):
|
||||||
obj.name = name
|
obj.name = name
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class TestEditObjectPlacement:
|
|||||||
geometry.clear_scale("obj").should_be_called()
|
geometry.clear_scale("obj").should_be_called()
|
||||||
surveyor.get_absolute_matrix("obj").should_be_called().will_return("matrix")
|
surveyor.get_absolute_matrix("obj").should_be_called().will_return("matrix")
|
||||||
ifc.run("geometry.edit_object_placement", product="element", matrix="matrix").should_be_called()
|
ifc.run("geometry.edit_object_placement", product="element", matrix="matrix").should_be_called()
|
||||||
|
geometry.record_object_position("obj").should_be_called()
|
||||||
|
|
||||||
def test_run(self, ifc, geometry, surveyor):
|
def test_run(self, ifc, geometry, surveyor):
|
||||||
self.predict(ifc, geometry, surveyor)
|
self.predict(ifc, geometry, surveyor)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import math
|
import math
|
||||||
import numpy
|
import numpy as np
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
@@ -352,6 +352,14 @@ class TestLink(NewFile):
|
|||||||
assert obj.BIMMeshProperties.ifc_definition_id == element.id()
|
assert obj.BIMMeshProperties.ifc_definition_id == element.id()
|
||||||
|
|
||||||
|
|
||||||
|
class TestRecordObjectPosition(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
obj = bpy.data.objects.new("Object", None)
|
||||||
|
subject.record_object_position(obj)
|
||||||
|
assert obj.BIMObjectProperties.location_checksum == repr(np.array(obj.matrix_world.translation).tobytes())
|
||||||
|
assert obj.BIMObjectProperties.rotation_checksum == repr(np.array(obj.matrix_world.to_3x3()).tobytes())
|
||||||
|
|
||||||
|
|
||||||
class TestRenameObject(NewFile):
|
class TestRenameObject(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
obj = bpy.data.meshes.new("Mesh")
|
obj = bpy.data.meshes.new("Mesh")
|
||||||
|
|||||||
Reference in New Issue
Block a user