mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-06 07:51:47 +00:00
Fix #1551. Support mixed geolocation workarounds of both object placement and cartesian point.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user