Fix #1551. Support mixed geolocation workarounds of both object placement and cartesian point.

This commit is contained in:
Dion Moult
2021-07-06 12:20:04 +10:00
parent cca28231ee
commit 75d67cd719
9 changed files with 59 additions and 68 deletions
@@ -84,7 +84,6 @@ if bpy is not None:
prop.GlobalId,
prop.BIMObjectProperties,
prop.BIMMaterialProperties,
prop.ItemSlotMap,
prop.BIMMeshProperties,
ui.BIM_PT_section_plane,
ui.BIM_UL_generic,
+1 -1
View File
@@ -103,7 +103,7 @@ class IfcExporter:
ifc_matrix[2][3] *= self.unit_scale
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "OBJECT_PLACEMENT":
ifc_matrix = ifcopenshell.util.geolocation.global2local(
ifc_matrix,
float(props.blender_eastings) * self.unit_scale,
+41 -37
View File
@@ -229,18 +229,19 @@ class IfcImporter:
self.set_default_context()
self.profile_code("Setting default context")
def is_element_far_away(self, element):
def is_element_far_away(self, element, is_meters=True):
try:
return self.is_point_far_away(element.ObjectPlacement.RelativePlacement.Location)
return self.is_point_far_away(element.ObjectPlacement.RelativePlacement.Location, is_meters=is_meters)
except:
pass
def is_point_far_away(self, point):
# Arbitrary threshold based on experience
def is_point_far_away(self, point, is_meters=True):
# Locations greater than 1km are not considered "small sites" according to the georeferencing guide
limit = 1000 if is_meters else (1000 / self.unit_scale)
coords = point
if hasattr(point, "Coordinates"):
coords = point.Coordinates
return abs(coords[0]) > 1000000 or abs(coords[1]) > 1000000 or abs(coords[2]) > 1000000
return abs(coords[0]) > limit or abs(coords[1]) > limit or abs(coords[2]) > limit
def process_element_filter(self):
if not self.ifc_import_settings.ifc_selector:
@@ -340,7 +341,6 @@ class IfcImporter:
props.blender_x_axis_abscissa = str(placement.RefDirection.DirectionRatios[0])
props.blender_x_axis_ordinate = str(placement.RefDirection.DirectionRatios[1])
props.has_blender_offset = True
props.blender_offset_type = "OBJECT_PLACEMENT"
def guess_absolute_coordinate(self):
# Civil BIM applications like to work in absolute coordinates, where the ObjectPlacement is 0,0,0 but each
@@ -353,7 +353,6 @@ class IfcImporter:
props.blender_northings = str(offset_point[1])
props.blender_orthogonal_height = str(offset_point[2])
props.has_blender_offset = True
props.blender_offset_type = "CARTESIAN_POINT"
def get_offset_point(self):
offset_point = None
@@ -370,7 +369,7 @@ class IfcImporter:
if elements_checked > element_checking_threshold:
return
for i, point in enumerate(point_list.CoordList):
if len(point) == 3 and self.is_point_far_away(point):
if len(point) == 3 and self.is_point_far_away(point, is_meters=False):
return point
for point in self.file.by_type("IfcCartesianPoint"):
@@ -384,22 +383,26 @@ class IfcImporter:
elements_checked += 1
if elements_checked > element_checking_threshold:
return
if len(point.Coordinates) == 3 and self.is_point_far_away(point):
if len(point.Coordinates) == 3 and self.is_point_far_away(point, is_meters=False):
return point.Coordinates
def apply_blender_offset_to_matrix(self, matrix):
def apply_blender_offset_to_matrix_world(self, obj, matrix):
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
return mathutils.Matrix(
ifcopenshell.util.geolocation.global2local(
matrix,
float(props.blender_eastings) * self.unit_scale,
float(props.blender_northings) * self.unit_scale,
float(props.blender_orthogonal_height) * self.unit_scale,
float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate),
).tolist()
)
if props.has_blender_offset:
if self.is_point_far_away((matrix[0, 3], matrix[1, 3], matrix[2, 3])):
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
return mathutils.Matrix(
ifcopenshell.util.geolocation.global2local(
matrix,
float(props.blender_eastings) * self.unit_scale,
float(props.blender_northings) * self.unit_scale,
float(props.blender_orthogonal_height) * self.unit_scale,
float(props.blender_x_axis_abscissa),
float(props.blender_x_axis_ordinate),
).tolist()
)
else:
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
return mathutils.Matrix(matrix.tolist())
def find_decomposed_ifc_class(self, element, ifc_class):
@@ -426,29 +429,25 @@ class IfcImporter:
shape = ifcopenshell.geom.create_shape(self.settings_2d, grid)
grid_obj = self.create_product(grid, shape)
collection = bpy.data.collections.new(self.get_name(grid))
element_matrix = self.get_local_placement(grid.ObjectPlacement)
element_matrix[0][3] *= self.unit_scale
element_matrix[1][3] *= self.unit_scale
element_matrix[2][3] *= self.unit_scale
u_axes = bpy.data.collections.new("UAxes")
collection.children.link(u_axes)
v_axes = bpy.data.collections.new("VAxes")
collection.children.link(v_axes)
self.create_grid_axes(grid.UAxes, u_axes, element_matrix)
self.create_grid_axes(grid.VAxes, v_axes, element_matrix)
self.create_grid_axes(grid.UAxes, u_axes, grid_obj)
self.create_grid_axes(grid.VAxes, v_axes, grid_obj)
if grid.WAxes:
w_axes = bpy.data.collections.new("WAxes")
collection.children.link(w_axes)
self.create_grid_axes(grid.WAxes, w_axes, element_matrix)
self.create_grid_axes(grid.WAxes, w_axes, grid_obj)
def create_grid_axes(self, axes, grid, matrix_world):
def create_grid_axes(self, axes, grid_collection, grid_obj):
for axis in axes:
shape = ifcopenshell.geom.create_shape(self.settings_2d, axis.AxisCurve)
mesh = self.create_mesh(axis, shape)
obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh)
obj.BIMObjectProperties.ifc_definition_id = axis.id()
obj.matrix_world = matrix_world
grid.objects.link(obj)
obj.matrix_world = grid_obj.matrix_world
grid_collection.objects.link(obj)
def create_type_products(self):
type_products = self.file.by_type("IfcTypeProduct")
@@ -617,7 +616,7 @@ class IfcImporter:
mesh.from_pydata([mathutils.Vector(vertex) * self.unit_scale], [], [])
obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh)
obj.matrix_world = mathutils.Matrix(placement_matrix.tolist())
obj.matrix_world = self.apply_blender_offset_to_matrix_world(placement_matrix)
self.link_element(product, obj)
def create_curve_products(self, products):
@@ -676,13 +675,13 @@ class IfcImporter:
mat = np.matrix(
([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(mat)
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, mat)
self.material_creator.create(element, obj, mesh)
elif mesh:
obj.matrix_world = self.apply_blender_offset_to_matrix(self.get_element_matrix(element))
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))
self.material_creator.create(element, obj, mesh)
elif hasattr(element, "ObjectPlacement"):
obj.matrix_world = self.apply_blender_offset_to_matrix(self.get_element_matrix(element))
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))
self.add_opening_relation(element, obj)
@@ -982,6 +981,7 @@ class IfcImporter:
def create_aggregate(self, rel_aggregate):
element = rel_aggregate.RelatingObject
obj = bpy.data.objects.new("{}/{}".format(element.is_a(), element.Name), None)
obj.matrix_world = self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))
self.link_element(element, obj)
collection = bpy.data.collections.new(obj.name)
collection.objects.link(obj)
@@ -1131,7 +1131,7 @@ class IfcImporter:
self.ifc_import_settings.logger.warning("Warning: this object is outside the spatial hierarchy %s", element)
bpy.context.scene.collection.objects.link(obj)
def get_element_matrix(self, element, mesh_name=None):
def get_element_matrix(self, element):
result = ifcopenshell.util.placement.get_local_placement(element.ObjectPlacement)
result[0][3] *= self.unit_scale
result[1][3] *= self.unit_scale
@@ -1265,7 +1265,11 @@ class IfcImporter:
mesh = bpy.data.meshes.new(self.get_mesh_name(geometry))
props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset and props.blender_offset_type == "CARTESIAN_POINT":
if (
props.has_blender_offset
and geometry.verts
and self.is_point_far_away((geometry.verts[0], geometry.verts[1], geometry.verts[2]))
):
ordinate_index = 0
verts = [None] * len(geometry.verts)
offset_point = (
@@ -32,7 +32,7 @@ class EditObjectPlacement(bpy.types.Operator):
if not obj.BIMObjectProperties.ifc_definition_id:
continue
matrix = np.array(obj.matrix_world)
if props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "OBJECT_PLACEMENT":
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
# TODO: np.array? Why not matrix?
matrix = np.array(
@@ -84,7 +84,7 @@ class AddRepresentation(bpy.types.Operator):
gprop = context.scene.BIMGeoreferenceProperties
coordinate_offset = None
if gprop.has_blender_offset and gprop.blender_offset_type == "CARTESIAN_POINT":
if gprop.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT":
coordinate_offset = Vector(
(
float(gprop.blender_eastings),
@@ -302,7 +302,7 @@ class UpdateRepresentation(bpy.types.Operator):
gprop = context.scene.BIMGeoreferenceProperties
coordinate_offset = None
if gprop.has_blender_offset and gprop.blender_offset_type == "CARTESIAN_POINT":
if gprop.has_blender_offset and obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT":
coordinate_offset = Vector(
(
float(gprop.blender_eastings),
@@ -109,6 +109,10 @@ class BIM_PT_mesh(Panel):
def BIM_PT_transform(self, context):
if context.active_object and context.active_object.BIMObjectProperties.ifc_definition_id:
row = self.layout.row(align=True)
row.label(text="Blender Offset")
row.label(text=context.active_object.BIMObjectProperties.blender_offset_type)
row = self.layout.row()
row.operator("bim.edit_object_placement")
@@ -244,11 +244,7 @@ class ConvertLocalToGlobal(bpy.types.Operator):
props = context.scene.BIMGeoreferenceProperties
x, y, z = [float(co) for co in props.coordinate_input.split(",")]
if props.has_blender_offset and props.blender_offset_type == "CARTESIAN_POINT":
x -= float(props.blender_eastings)
y -= float(props.blender_northings)
z -= float(props.blender_orthogonal_height)
elif props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
if props.has_blender_offset:
results = ifcopenshell.util.geolocation.xyz2enh(
x,
y,
@@ -310,11 +306,7 @@ class ConvertGlobalToLocal(bpy.types.Operator):
else:
results = (x, y, z)
if props.has_blender_offset and props.blender_offset_type == "CARTESIAN_POINT":
results[0] += float(props.blender_eastings)
results[1] += float(props.blender_northings)
results[2] += float(props.blender_orthogonal_height)
elif props.has_blender_offset and props.blender_offset_type == "OBJECT_PLACEMENT":
if props.has_blender_offset:
results = ifcopenshell.util.geolocation.enh2xyz(
results[0],
results[1],
@@ -36,11 +36,6 @@ class BIMGeoreferenceProperties(PropertyGroup):
coordinate_input: StringProperty(name="Coordinate Input")
coordinate_output: StringProperty(name="Coordinate Output")
has_blender_offset: BoolProperty(name="Has Blender Offset")
blender_offset_type: EnumProperty(
items=[(o, o, "") for o in ["OBJECT_PLACEMENT", "CARTESIAN_POINT"]],
name="Blender Offset",
default="OBJECT_PLACEMENT",
)
blender_eastings: StringProperty(name="Blender Eastings", default="0")
blender_northings: StringProperty(name="Blender Northings", default="0")
blender_orthogonal_height: StringProperty(name="Blender Orthogonal Height", default="0")
@@ -90,18 +90,16 @@ class BIM_PT_gis(Panel):
def draw_ui(self, context):
props = context.scene.BIMGeoreferenceProperties
if not Data.projected_crs and IfcStore.get_file().schema != "IFC2X3":
if not Data.projected_crs:
row = self.layout.row(align=True)
row.label(text="Not Georeferenced")
row.operator("bim.add_georeferencing", icon="ADD", text="")
if IfcStore.get_file().schema != "IFC2X3":
row.operator("bim.add_georeferencing", icon="ADD", text="")
if props.has_blender_offset:
row = self.layout.row()
row.label(text="Blender Offset", icon="TRACKING_REFINE_FORWARDS")
row = self.layout.row(align=True)
row.label(text="Type")
row.label(text=props.blender_offset_type)
row = self.layout.row(align=True)
row.label(text="Eastings")
row.label(text=props.blender_eastings)
+5 -6
View File
@@ -259,6 +259,11 @@ class GlobalId(PropertyGroup):
class BIMObjectProperties(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
blender_offset_type: EnumProperty(
items=[(o, o, "") for o in ["NONE", "OBJECT_PLACEMENT", "CARTESIAN_POINT"]],
name="Blender Offset",
default="NONE",
)
is_reassigning_class: BoolProperty(name="Is Reassigning Class")
global_ids: CollectionProperty(name="GlobalIds", type=GlobalId)
relating_object: PointerProperty(name="Aggregate", type=bpy.types.Object)
@@ -279,11 +284,6 @@ class BIMMaterialProperties(PropertyGroup):
ifc_style_id: IntProperty(name="IFC Style ID")
class ItemSlotMap(PropertyGroup):
name: StringProperty(name="Item Element ID")
slot_index: IntProperty(name="Material Slot Index")
class BIMMeshProperties(PropertyGroup):
ifc_definition_id: IntProperty(name="IFC Definition ID")
is_native: BoolProperty(name="Is Native", default=False)
@@ -291,4 +291,3 @@ class BIMMeshProperties(PropertyGroup):
is_parametric: BoolProperty(name="Is Parametric", default=False)
ifc_definition: StringProperty(name="IFC Definition")
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
ifc_item_ids: CollectionProperty(name="IFC Item IDs", type=ItemSlotMap)