mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 02:31:09 +00:00
Rewrite coordinate offset to use np.array not Vector() for precision
This commit is contained in:
@@ -1368,7 +1368,10 @@ class IfcImporter:
|
|||||||
|
|
||||||
mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name_from_shape(geometry))
|
mesh = bpy.data.meshes.new(tool.Loader.get_mesh_name_from_shape(geometry))
|
||||||
|
|
||||||
if cartesian_point_offset:
|
if cartesian_point_offset is False:
|
||||||
|
verts = geometry.verts
|
||||||
|
mesh["has_cartesian_point_offset"] = False
|
||||||
|
elif cartesian_point_offset is not None:
|
||||||
verts_array = np.array(geometry.verts)
|
verts_array = np.array(geometry.verts)
|
||||||
offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]])
|
offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]])
|
||||||
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||||
@@ -1378,9 +1381,6 @@ class IfcImporter:
|
|||||||
mesh["cartesian_point_offset"] = (
|
mesh["cartesian_point_offset"] = (
|
||||||
f"{cartesian_point_offset[0]},{cartesian_point_offset[1]},{cartesian_point_offset[2]}"
|
f"{cartesian_point_offset[0]},{cartesian_point_offset[1]},{cartesian_point_offset[2]}"
|
||||||
)
|
)
|
||||||
elif cartesian_point_offset is False:
|
|
||||||
verts = geometry.verts
|
|
||||||
mesh["has_cartesian_point_offset"] = False
|
|
||||||
elif geometry.verts and tool.Loader.is_point_far_away(
|
elif geometry.verts and tool.Loader.is_point_far_away(
|
||||||
(geometry.verts[0], geometry.verts[1], geometry.verts[2]), is_meters=True
|
(geometry.verts[0], geometry.verts[1], geometry.verts[2]), is_meters=True
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -1831,8 +1831,8 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
|
||||||
rep_obj = bpy.context.scene.BIMGeometryProperties.representation_obj
|
rep_obj = bpy.context.scene.BIMGeometryProperties.representation_obj
|
||||||
if coordinate_offset := tool.Geometry.get_cartesian_point_offset(rep_obj):
|
if (coordinate_offset := tool.Geometry.get_cartesian_point_offset(rep_obj)) is not None:
|
||||||
verts = [(v.co + coordinate_offset) / unit_scale for v in obj.data.vertices]
|
verts = [((np.array(v.co) + coordinate_offset) / unit_scale).tolist() for v in obj.data.vertices]
|
||||||
else:
|
else:
|
||||||
verts = [v.co / unit_scale for v in obj.data.vertices]
|
verts = [v.co / unit_scale for v in obj.data.vertices]
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import struct
|
|||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import numpy.typing as npt
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
@@ -460,12 +461,12 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
return ifcopenshell.util.representation.get_representation(element, context)
|
return ifcopenshell.util.representation.get_representation(element, context)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_cartesian_point_offset(cls, obj: bpy.types.Object) -> Vector | None:
|
def get_cartesian_point_offset(cls, obj: bpy.types.Object) -> npt.NDArray[np.float64] | None:
|
||||||
if (
|
if (
|
||||||
obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT"
|
obj.BIMObjectProperties.blender_offset_type == "CARTESIAN_POINT"
|
||||||
and obj.BIMObjectProperties.cartesian_point_offset
|
and obj.BIMObjectProperties.cartesian_point_offset
|
||||||
):
|
):
|
||||||
return Vector(tuple(map(float, obj.BIMObjectProperties.cartesian_point_offset.split(","))))
|
return np.array(tuple(map(float, obj.BIMObjectProperties.cartesian_point_offset.split(","))))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
def get_element_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
@@ -693,8 +694,11 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
if element.is_a("IfcAnnotation") and ifc_importer.is_curve_annotation(element):
|
if element.is_a("IfcAnnotation") and ifc_importer.is_curve_annotation(element):
|
||||||
mesh = ifc_importer.create_curve(element, shape)
|
mesh = ifc_importer.create_curve(element, shape)
|
||||||
elif shape:
|
elif shape:
|
||||||
|
cartesian_point_offset = cls.get_cartesian_point_offset(obj)
|
||||||
|
if cartesian_point_offset is None:
|
||||||
|
cartesian_point_offset = False
|
||||||
mesh = ifc_importer.create_mesh(
|
mesh = ifc_importer.create_mesh(
|
||||||
element, shape, cartesian_point_offset=cls.get_cartesian_point_offset(obj) or False
|
element, shape, cartesian_point_offset=cartesian_point_offset
|
||||||
)
|
)
|
||||||
ifc_importer.material_creator.load_existing_materials()
|
ifc_importer.material_creator.load_existing_materials()
|
||||||
shape_has_openings = cls.does_shape_has_openings(shape)
|
shape_has_openings = cls.does_shape_has_openings(shape)
|
||||||
@@ -1435,10 +1439,10 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
rep_obj = props.representation_obj
|
rep_obj = props.representation_obj
|
||||||
|
|
||||||
coordinate_offset = cls.get_cartesian_point_offset(rep_obj)
|
coordinate_offset = cls.get_cartesian_point_offset(rep_obj)
|
||||||
rep_matrix = rep_obj.matrix_world.copy()
|
rep_matrix = np.array(rep_obj.matrix_world.copy())
|
||||||
if coordinate_offset:
|
if coordinate_offset is not None:
|
||||||
rep_matrix.translation -= coordinate_offset
|
rep_matrix[:, 3][0:3] -= coordinate_offset
|
||||||
rep_matrix_i = rep_matrix.inverted()
|
rep_matrix_i = np.linalg.inv(rep_matrix)
|
||||||
|
|
||||||
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
|
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
|
||||||
has_changed = False
|
has_changed = False
|
||||||
@@ -1459,9 +1463,9 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
|
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
position = rep_matrix_i @ obj.matrix_world
|
position = rep_matrix_i @ np.array(obj.matrix_world)
|
||||||
position.translation /= unit_scale
|
position[:, 3][0:3] /= unit_scale
|
||||||
item.Position = builder.create_axis2_placement_3d_from_matrix(np.array(position))
|
item.Position = builder.create_axis2_placement_3d_from_matrix(position)
|
||||||
if old_position:
|
if old_position:
|
||||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
|
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_position)
|
||||||
|
|
||||||
@@ -1482,31 +1486,36 @@ class Geometry(bonsai.core.tool.Geometry):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def import_item(cls, obj: bpy.types.Object) -> None:
|
def import_item(cls, obj: bpy.types.Object) -> None:
|
||||||
props = bpy.context.scene.BIMGeometryProperties
|
props = bpy.context.scene.BIMGeometryProperties
|
||||||
|
rep_obj = props.representation_obj
|
||||||
tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get())
|
tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get())
|
||||||
tool.Loader.settings.context_settings = tool.Loader.create_settings()
|
tool.Loader.settings.context_settings = tool.Loader.create_settings()
|
||||||
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
|
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
|
||||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||||
geometry = tool.Loader.create_generic_shape(item)
|
|
||||||
obj.data.clear_geometry()
|
obj.data.clear_geometry()
|
||||||
tool.Loader.convert_geometry_to_mesh(geometry, obj.data)
|
|
||||||
|
|
||||||
rep_obj = props.representation_obj
|
geometry = tool.Loader.create_generic_shape(item)
|
||||||
|
if (cartesian_point_offset := cls.get_cartesian_point_offset(rep_obj)) is not None:
|
||||||
|
verts_array = np.array(geometry.verts)
|
||||||
|
offset = np.array([-cartesian_point_offset[0], -cartesian_point_offset[1], -cartesian_point_offset[2]])
|
||||||
|
offset_verts = verts_array + np.tile(offset, len(verts_array) // 3)
|
||||||
|
verts = offset_verts.tolist()
|
||||||
|
else:
|
||||||
|
verts = geometry.verts
|
||||||
|
tool.Loader.convert_geometry_to_mesh(geometry, obj.data, verts=verts)
|
||||||
|
|
||||||
obj.matrix_world = rep_obj.matrix_world.copy()
|
obj.matrix_world = rep_obj.matrix_world.copy()
|
||||||
if coordinate_offset := cls.get_cartesian_point_offset(rep_obj):
|
|
||||||
for vert in obj.data.vertices:
|
|
||||||
vert.co -= coordinate_offset
|
|
||||||
|
|
||||||
if (is_swept_area := item.is_a("IfcSweptAreaSolid")) or item.is_a("IfcConic"):
|
if (is_swept_area := item.is_a("IfcSweptAreaSolid")) or item.is_a("IfcConic"):
|
||||||
position = item.Position
|
position = item.Position
|
||||||
# Positional is optionaly only for SweptAreaSolid.
|
# Positional is optionaly only for SweptAreaSolid.
|
||||||
if position or not is_swept_area:
|
if position or not is_swept_area:
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
position = Matrix(ifcopenshell.util.placement.get_axis2placement(position).tolist())
|
position = ifcopenshell.util.placement.get_axis2placement(position)
|
||||||
position.translation *= unit_scale
|
position[:, 3][0:3] *= unit_scale
|
||||||
item_matrix = rep_obj.matrix_world.copy()
|
item_matrix = np.array(rep_obj.matrix_world.copy())
|
||||||
if coordinate_offset:
|
if cartesian_point_offset is not None:
|
||||||
item_matrix.translation -= coordinate_offset
|
item_matrix[:, 3][0:3] -= cartesian_point_offset
|
||||||
item_matrix = item_matrix @ position
|
item_matrix = Matrix(item_matrix @ position)
|
||||||
|
|
||||||
transformation = obj.matrix_world.inverted() @ item_matrix
|
transformation = obj.matrix_world.inverted() @ item_matrix
|
||||||
transformation_i = transformation.inverted()
|
transformation_i = transformation.inverted()
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ def add_representation(
|
|||||||
"context": context,
|
"context": context,
|
||||||
"blender_object": blender_object,
|
"blender_object": blender_object,
|
||||||
"geometry": geometry,
|
"geometry": geometry,
|
||||||
"coordinate_offset": coordinate_offset,
|
"coordinate_offset": Vector(coordinate_offset) if coordinate_offset is not None else None,
|
||||||
"total_items": total_items,
|
"total_items": total_items,
|
||||||
"unit_scale": unit_scale,
|
"unit_scale": unit_scale,
|
||||||
"should_force_faceted_brep": should_force_faceted_brep,
|
"should_force_faceted_brep": should_force_faceted_brep,
|
||||||
|
|||||||
@@ -1086,12 +1086,12 @@ class ShapeBuilder:
|
|||||||
)
|
)
|
||||||
return points, segments, transition_arc
|
return points, segments, transition_arc
|
||||||
|
|
||||||
def mesh(self, points: list[Vector], faces: list[list[int]]) -> ifcopenshell.entity_instance:
|
def mesh(self, points: list[list[float]], faces: list[list[int]]) -> ifcopenshell.entity_instance:
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
return self.faceted_brep(points, faces)
|
return self.faceted_brep(points, faces)
|
||||||
return self.polygonal_face_set(points, faces)
|
return self.polygonal_face_set(points, faces)
|
||||||
|
|
||||||
def faceted_brep(self, points: list[Vector], faces: list[list[int]]) -> ifcopenshell.entity_instance:
|
def faceted_brep(self, points: list[list[float]], faces: list[list[int]]) -> ifcopenshell.entity_instance:
|
||||||
"""Generate an IfcFacetedBrep with a closed shell
|
"""Generate an IfcFacetedBrep with a closed shell
|
||||||
|
|
||||||
Note that :func:`polygonal_face_set` is recommended in IFC4.
|
Note that :func:`polygonal_face_set` is recommended in IFC4.
|
||||||
@@ -1109,7 +1109,7 @@ class ShapeBuilder:
|
|||||||
]
|
]
|
||||||
return self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(faces))
|
return self.file.createIfcFacetedBrep(self.file.createIfcClosedShell(faces))
|
||||||
|
|
||||||
def polygonal_face_set(self, points: list[Vector], faces: list[list[int]]) -> ifcopenshell.entity_instance:
|
def polygonal_face_set(self, points: list[list[float]], faces: list[list[int]]) -> ifcopenshell.entity_instance:
|
||||||
"""
|
"""
|
||||||
Generate an IfcPolygonalFaceSet
|
Generate an IfcPolygonalFaceSet
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user