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:
Dion Moult
2021-11-11 17:31:16 +11:00
parent f5f88f4b97
commit 6f55cd5169
8 changed files with 46 additions and 27 deletions
+14 -17
View File
@@ -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]
+11 -6
View File
@@ -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):
+2 -3
View File
@@ -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(
+1
View File
@@ -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)
+9 -1
View File
@@ -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")