mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-19 19:54:07 +00:00
Fix covering geometry not persisting to IFC, same root cause as #7055
The covering tool used bmesh as an intermediate and relied on
type.assign_type post-listeners (removed in 44a52863a) to generate
the IfcExtrudedAreaSolid body. With those listeners gone, coverings
had no body representation and assign_swept_area_outer_curve crashed.
Build covering representations from scratch using ShapeBuilder, reading
the extrusion depth from the type's IfcMaterialLayerSet. Also replace
bpy.ops.bim.assign_class with bonsai.core.root.assign_class using
should_add_representation=False, consistent with the space fix.
Refactored shared coordinate-conversion and extrusion-building logic
into get_2d_vertices_from_polygon and set_extrusion_representation_from_polygon,
used by both space and covering code paths. Removed all bmesh-dependent
dead code from the spatial tool.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,21 +51,11 @@ def add_instance_flooring_covering_from_cursor(
|
|||||||
if isinstance(space_polygon, str):
|
if isinstance(space_polygon, str):
|
||||||
return
|
return
|
||||||
|
|
||||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=0, polygon_is_si=True)
|
obj = spatial.create_object("Covering")
|
||||||
name = "Covering"
|
|
||||||
mesh = spatial.get_named_mesh_from_bmesh(name=name, bmesh=bm)
|
|
||||||
|
|
||||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
|
||||||
|
|
||||||
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
||||||
spatial.translate_obj_to_z_location(obj, z)
|
spatial.translate_obj_to_z_location(obj, z)
|
||||||
points = spatial.get_2d_vertices_from_obj(obj)
|
|
||||||
points = spatial.get_scaled_2d_vertices(points)
|
|
||||||
spatial.assign_type_to_obj(obj)
|
spatial.assign_type_to_obj(obj)
|
||||||
|
spatial.set_covering_representation_from_polygon(obj, space_polygon, polygon_is_si=True)
|
||||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices=points)
|
|
||||||
body = spatial.get_body_representation(obj)
|
|
||||||
spatial.regen_obj_representation(obj, body)
|
|
||||||
|
|
||||||
|
|
||||||
def add_instance_ceiling_covering_from_cursor(
|
def add_instance_ceiling_covering_from_cursor(
|
||||||
@@ -95,21 +85,11 @@ def add_instance_ceiling_covering_from_cursor(
|
|||||||
if isinstance(space_polygon, str):
|
if isinstance(space_polygon, str):
|
||||||
return
|
return
|
||||||
|
|
||||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=0, polygon_is_si=True)
|
obj = spatial.create_object("Covering")
|
||||||
name = "Covering"
|
|
||||||
mesh = spatial.get_named_mesh_from_bmesh(name=name, bmesh=bm)
|
|
||||||
|
|
||||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
|
||||||
|
|
||||||
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
spatial.set_obj_origin_to_cursor_position_and_zero_elevation(obj)
|
||||||
spatial.translate_obj_to_z_location(obj, z + ceiling_height)
|
spatial.translate_obj_to_z_location(obj, z + ceiling_height)
|
||||||
points = spatial.get_2d_vertices_from_obj(obj)
|
|
||||||
points = spatial.get_scaled_2d_vertices(points)
|
|
||||||
spatial.assign_type_to_obj(obj)
|
spatial.assign_type_to_obj(obj)
|
||||||
|
spatial.set_covering_representation_from_polygon(obj, space_polygon, polygon_is_si=True)
|
||||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices=points)
|
|
||||||
body = spatial.get_body_representation(obj)
|
|
||||||
spatial.regen_obj_representation(obj, body)
|
|
||||||
|
|
||||||
|
|
||||||
def regen_selected_covering_object(root: type[tool.Root], spatial: type[tool.Spatial]) -> None:
|
def regen_selected_covering_object(root: type[tool.Root], spatial: type[tool.Spatial]) -> None:
|
||||||
@@ -127,19 +107,7 @@ def regen_selected_covering_object(root: type[tool.Root], spatial: type[tool.Spa
|
|||||||
if isinstance(space_polygon, str):
|
if isinstance(space_polygon, str):
|
||||||
return
|
return
|
||||||
|
|
||||||
bm = spatial.get_bmesh_from_polygon(space_polygon, h=0, polygon_is_si=True)
|
spatial.set_covering_representation_from_polygon(active_obj, space_polygon, polygon_is_si=True)
|
||||||
|
|
||||||
name = "Aux"
|
|
||||||
mesh = spatial.get_named_mesh_from_bmesh(name=name, bmesh=bm)
|
|
||||||
mesh = spatial.get_transformed_mesh_from_local_to_global(mesh)
|
|
||||||
obj = spatial.get_named_obj_from_mesh(name, mesh)
|
|
||||||
|
|
||||||
points = spatial.get_2d_vertices_from_obj(obj)
|
|
||||||
points = spatial.get_scaled_2d_vertices(points)
|
|
||||||
|
|
||||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(active_obj, vertices=points)
|
|
||||||
body = spatial.get_body_representation(active_obj)
|
|
||||||
spatial.regen_obj_representation(active_obj, body)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO CHECK IF IT IS POSSIBLE TO CREATE ONLY ONE CORE FUNCTION FOR _FROM_WALLS
|
# TODO CHECK IF IT IS POSSIBLE TO CREATE ONLY ONE CORE FUNCTION FOR _FROM_WALLS
|
||||||
@@ -151,22 +119,13 @@ def add_instance_flooring_coverings_from_walls(root: type[tool.Root], spatial: t
|
|||||||
union = spatial.get_union_shape_from_selected_objects()
|
union = spatial.get_union_shape_from_selected_objects()
|
||||||
for i, linear_ring in enumerate(union.interiors):
|
for i, linear_ring in enumerate(union.interiors):
|
||||||
poly = spatial.get_buffered_poly_from_linear_ring(linear_ring)
|
poly = spatial.get_buffered_poly_from_linear_ring(linear_ring)
|
||||||
bm = spatial.get_bmesh_from_polygon(poly, h=0, polygon_is_si=False)
|
|
||||||
|
|
||||||
name = "Covering" + str(i)
|
name = "Covering" + str(i)
|
||||||
obj = spatial.get_named_obj_from_bmesh(name, bmesh=bm)
|
obj = spatial.create_object(name)
|
||||||
|
spatial.set_obj_origin_to_polygon_center(obj, poly, polygon_is_si=False)
|
||||||
spatial.set_obj_origin_to_bboxcenter(obj)
|
|
||||||
spatial.translate_obj_to_z_location(obj, z)
|
spatial.translate_obj_to_z_location(obj, z)
|
||||||
|
|
||||||
points = spatial.get_2d_vertices_from_obj(obj)
|
|
||||||
points = spatial.get_scaled_2d_vertices(points)
|
|
||||||
|
|
||||||
spatial.assign_type_to_obj(obj)
|
spatial.assign_type_to_obj(obj)
|
||||||
|
spatial.set_covering_representation_from_polygon(obj, poly, polygon_is_si=False)
|
||||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices=points)
|
|
||||||
body = spatial.get_body_representation(obj)
|
|
||||||
spatial.regen_obj_representation(obj, body)
|
|
||||||
|
|
||||||
|
|
||||||
def add_instance_ceiling_coverings_from_walls(
|
def add_instance_ceiling_coverings_from_walls(
|
||||||
@@ -179,22 +138,13 @@ def add_instance_ceiling_coverings_from_walls(
|
|||||||
union = spatial.get_union_shape_from_selected_objects()
|
union = spatial.get_union_shape_from_selected_objects()
|
||||||
for i, linear_ring in enumerate(union.interiors):
|
for i, linear_ring in enumerate(union.interiors):
|
||||||
poly = spatial.get_buffered_poly_from_linear_ring(linear_ring)
|
poly = spatial.get_buffered_poly_from_linear_ring(linear_ring)
|
||||||
bm = spatial.get_bmesh_from_polygon(poly, h=0, polygon_is_si=False)
|
|
||||||
|
|
||||||
name = "Covering" + str(i)
|
name = "Covering" + str(i)
|
||||||
obj = spatial.get_named_obj_from_bmesh(name, bmesh=bm)
|
obj = spatial.create_object(name)
|
||||||
|
spatial.set_obj_origin_to_polygon_center(obj, poly, polygon_is_si=False)
|
||||||
spatial.set_obj_origin_to_bboxcenter(obj)
|
|
||||||
spatial.translate_obj_to_z_location(obj, z)
|
spatial.translate_obj_to_z_location(obj, z)
|
||||||
|
|
||||||
points = spatial.get_2d_vertices_from_obj(obj)
|
|
||||||
points = spatial.get_scaled_2d_vertices(points)
|
|
||||||
|
|
||||||
spatial.assign_type_to_obj(obj)
|
spatial.assign_type_to_obj(obj)
|
||||||
|
spatial.set_covering_representation_from_polygon(obj, poly, polygon_is_si=False)
|
||||||
spatial.assign_swept_area_outer_curve_from_2d_vertices(obj, vertices=points)
|
|
||||||
body = spatial.get_body_representation(obj)
|
|
||||||
spatial.regen_obj_representation(obj, body)
|
|
||||||
|
|
||||||
|
|
||||||
class NoDefaultContainer(Exception):
|
class NoDefaultContainer(Exception):
|
||||||
|
|||||||
@@ -999,15 +999,12 @@ class Spatial:
|
|||||||
def get_purged_inner_holes_poly(cls, union_geom, min_area): pass
|
def get_purged_inner_holes_poly(cls, union_geom, min_area): pass
|
||||||
def get_poly_valid_interior_list(cls, poly, min_area, interiors_list): pass
|
def get_poly_valid_interior_list(cls, poly, min_area, interiors_list): pass
|
||||||
def get_buffered_poly_from_linear_ring(cls, linear_ring): pass
|
def get_buffered_poly_from_linear_ring(cls, linear_ring): pass
|
||||||
def get_bmesh_from_polygon(cls, poly, h, polygon_is_si=False): pass
|
def get_2d_vertices_from_polygon(cls, poly, obj, polygon_is_si=True): pass
|
||||||
def get_named_obj_from_bmesh(cls, name, bmesh): pass
|
def set_extrusion_representation_from_polygon(cls, obj, element, poly, depth_ifc, polygon_is_si=True): pass
|
||||||
def get_named_obj_from_mesh(cls, name, mesh): pass
|
|
||||||
def get_named_mesh_from_bmesh(cls, name, bmesh): pass
|
|
||||||
def get_transformed_mesh_from_local_to_global(cls, mesh): pass
|
|
||||||
def set_space_representation_from_polygon(cls, obj, element, poly, h, polygon_is_si=True): pass
|
def set_space_representation_from_polygon(cls, obj, element, poly, h, polygon_is_si=True): pass
|
||||||
|
def set_covering_representation_from_polygon(cls, obj, poly, polygon_is_si=True): pass
|
||||||
def create_object(cls, name): pass
|
def create_object(cls, name): pass
|
||||||
def set_obj_origin_to_polygon_center(cls, obj, poly, polygon_is_si=True): pass
|
def set_obj_origin_to_polygon_center(cls, obj, poly, polygon_is_si=True): pass
|
||||||
def set_obj_origin_to_bboxcenter(cls, obj): pass
|
|
||||||
def set_obj_origin_to_cursor_position_and_zero_elevation(cls, obj): pass
|
def set_obj_origin_to_cursor_position_and_zero_elevation(cls, obj): pass
|
||||||
def get_selected_objects(cls): pass
|
def get_selected_objects(cls): pass
|
||||||
def get_active_obj(cls): pass
|
def get_active_obj(cls): pass
|
||||||
@@ -1015,14 +1012,9 @@ class Spatial:
|
|||||||
def get_active_obj_height(cls): pass
|
def get_active_obj_height(cls): pass
|
||||||
def get_relating_type_id(cls): pass
|
def get_relating_type_id(cls): pass
|
||||||
def translate_obj_to_z_location(cls, obj, z): pass
|
def translate_obj_to_z_location(cls, obj, z): pass
|
||||||
def get_2d_vertices_from_obj(cls, obj): pass
|
|
||||||
def get_scaled_2d_vertices(cls, points): pass
|
|
||||||
def assign_swept_area_outer_curve_from_2d_vertices(cls, obj, vertices): pass
|
|
||||||
def get_body_representation(cls, obj): pass
|
|
||||||
def assign_ifcspace_class_to_obj(cls, obj): pass
|
def assign_ifcspace_class_to_obj(cls, obj): pass
|
||||||
def assign_type_to_obj(cls, obj): pass
|
def assign_type_to_obj(cls, obj): pass
|
||||||
def assign_relating_type_to_element(cls, ifc, type, element, relating_type): pass
|
def assign_relating_type_to_element(cls, ifc, type, element, relating_type): pass
|
||||||
def regen_obj_representation(cls, obj, body): pass
|
|
||||||
def toggle_spaces_visibility_wired_and_textured(cls, spaces): pass
|
def toggle_spaces_visibility_wired_and_textured(cls, spaces): pass
|
||||||
def toggle_hide_spaces(cls, spaces): pass
|
def toggle_hide_spaces(cls, spaces): pass
|
||||||
def set_default_container(cls, container): pass
|
def set_default_container(cls, container): pass
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections.abc import Generator, Iterable
|
from collections.abc import Generator, Iterable
|
||||||
from math import pi
|
|
||||||
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
||||||
|
|
||||||
import bmesh
|
import bmesh
|
||||||
@@ -991,49 +990,6 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
)
|
)
|
||||||
return poly
|
return poly
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_bmesh_from_polygon(cls, poly: Polygon, h: float, polygon_is_si: bool = False) -> bmesh.types.BMesh:
|
|
||||||
"""
|
|
||||||
:param h: Height, in meters.
|
|
||||||
:param polygon_is_si: Should be True if `poly` is defined in meters.
|
|
||||||
"""
|
|
||||||
mat = Matrix()
|
|
||||||
bm = bmesh.new()
|
|
||||||
bm.verts.index_update()
|
|
||||||
bm.edges.index_update()
|
|
||||||
|
|
||||||
mat_invert = mat.inverted()
|
|
||||||
si_conversion = 1.0 if polygon_is_si else ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
|
||||||
new_verts = [
|
|
||||||
# bm.verts.new(mat_invert @ (Vector([v[0], v[1], 0]) * si_conversion)) for v in poly.exterior.coords[0:-1]
|
|
||||||
bm.verts.new(mat_invert @ (Vector([v[0], v[1], 0]) * si_conversion))
|
|
||||||
for v in shapely.get_exterior_ring(poly).coords[0:-1]
|
|
||||||
]
|
|
||||||
[bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
|
||||||
bm.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
|
||||||
|
|
||||||
bm.verts.index_update()
|
|
||||||
bm.edges.index_update()
|
|
||||||
|
|
||||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=1e-5)
|
|
||||||
bmesh.ops.triangle_fill(bm, edges=bm.edges)
|
|
||||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 5, verts=bm.verts, edges=bm.edges)
|
|
||||||
|
|
||||||
if h != 0:
|
|
||||||
extrusion = bmesh.ops.extrude_face_region(bm, geom=bm.faces)
|
|
||||||
extruded_verts = [g for g in extrusion["geom"] if isinstance(g, bmesh.types.BMVert)]
|
|
||||||
bmesh.ops.translate(bm, vec=[0.0, 0.0, h], verts=extruded_verts)
|
|
||||||
|
|
||||||
bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
|
|
||||||
|
|
||||||
return bm
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_named_obj_from_bmesh(cls, name: str, bmesh: bmesh.types.BMesh) -> bpy.types.Object:
|
|
||||||
mesh = cls.get_named_mesh_from_bmesh(name, bmesh)
|
|
||||||
obj = cls.get_named_obj_from_mesh(name, mesh)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_object(cls, name: str) -> bpy.types.Object:
|
def create_object(cls, name: str) -> bpy.types.Object:
|
||||||
mesh = bpy.data.meshes.new(name=name)
|
mesh = bpy.data.meshes.new(name=name)
|
||||||
@@ -1051,47 +1007,23 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
else:
|
else:
|
||||||
obj.location = Vector((centroid.x * unit_scale, centroid.y * unit_scale, 0))
|
obj.location = Vector((centroid.x * unit_scale, centroid.y * unit_scale, 0))
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_named_obj_from_mesh(cls, name: str, mesh: bpy.types.Mesh) -> bpy.types.Object:
|
|
||||||
obj = bpy.data.objects.new(name, mesh)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_named_mesh_from_bmesh(cls, name: str, bmesh: bmesh.types.BMesh) -> bpy.types.Mesh:
|
def get_2d_vertices_from_polygon(
|
||||||
mesh = bpy.data.meshes.new(name=name)
|
|
||||||
bmesh.to_mesh(mesh)
|
|
||||||
bmesh.free()
|
|
||||||
return mesh
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_transformed_mesh_from_local_to_global(cls, mesh: bpy.types.Mesh) -> bpy.types.Mesh:
|
|
||||||
active_obj = cls.get_active_obj()
|
|
||||||
mat = active_obj.matrix_world
|
|
||||||
mesh.transform(mat.inverted())
|
|
||||||
mesh.update()
|
|
||||||
return mesh
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set_space_representation_from_polygon(
|
|
||||||
cls,
|
cls,
|
||||||
obj: bpy.types.Object,
|
|
||||||
element: ifcopenshell.entity_instance,
|
|
||||||
poly: Polygon,
|
poly: Polygon,
|
||||||
h: float,
|
obj: bpy.types.Object,
|
||||||
polygon_is_si: bool = True,
|
polygon_is_si: bool = True,
|
||||||
) -> None:
|
) -> list[list[float]]:
|
||||||
"""Create or replace the IFC body representation of a space from a polygon.
|
"""Convert a world-space shapely polygon to 2D vertices in obj's local space, in IFC file units.
|
||||||
|
|
||||||
:param obj: The Blender object for the space.
|
:param poly: The polygon in world space.
|
||||||
:param element: The IfcSpace entity.
|
:param obj: The Blender object whose local space is used.
|
||||||
:param poly: The space polygon in world space.
|
|
||||||
:param h: The height in SI (meters).
|
|
||||||
:param polygon_is_si: True if polygon coords are in SI, False if in IFC file units.
|
:param polygon_is_si: True if polygon coords are in SI, False if in IFC file units.
|
||||||
|
:return: List of [x, y] coordinates (not closed).
|
||||||
"""
|
"""
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||||
builder = ifcopenshell.util.shape_builder.ShapeBuilder(ifc_file)
|
|
||||||
|
|
||||||
bpy.context.view_layer.update()
|
bpy.context.view_layer.update()
|
||||||
mat_inv = obj.matrix_world.inverted()
|
mat_inv = obj.matrix_world.inverted()
|
||||||
coords_2d = []
|
coords_2d = []
|
||||||
@@ -1100,10 +1032,33 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
if not polygon_is_si:
|
if not polygon_is_si:
|
||||||
world_si = world_si * unit_scale
|
world_si = world_si * unit_scale
|
||||||
local_si = mat_inv @ world_si
|
local_si = mat_inv @ world_si
|
||||||
coords_2d.append(Vector((local_si.x, local_si.y)) / unit_scale)
|
coords_2d.append([local_si.x / unit_scale, local_si.y / unit_scale])
|
||||||
|
return coords_2d
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_extrusion_representation_from_polygon(
|
||||||
|
cls,
|
||||||
|
obj: bpy.types.Object,
|
||||||
|
element: ifcopenshell.entity_instance,
|
||||||
|
poly: Polygon,
|
||||||
|
depth_ifc: float,
|
||||||
|
polygon_is_si: bool = True,
|
||||||
|
) -> None:
|
||||||
|
"""Create or replace the IFC body representation from a polygon extrusion.
|
||||||
|
|
||||||
|
:param obj: The Blender object.
|
||||||
|
:param element: The IFC product entity.
|
||||||
|
:param poly: The polygon in world space.
|
||||||
|
:param depth_ifc: The extrusion depth in IFC file units.
|
||||||
|
:param polygon_is_si: True if polygon coords are in SI, False if in IFC file units.
|
||||||
|
"""
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
|
builder = ifcopenshell.util.shape_builder.ShapeBuilder(ifc_file)
|
||||||
|
|
||||||
|
coords_2d = cls.get_2d_vertices_from_polygon(poly, obj, polygon_is_si)
|
||||||
|
|
||||||
curve = builder.polyline(coords_2d, closed=True)
|
curve = builder.polyline(coords_2d, closed=True)
|
||||||
item = builder.extrude(curve, magnitude=h / unit_scale)
|
item = builder.extrude(curve, magnitude=depth_ifc)
|
||||||
|
|
||||||
old_body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
old_body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||||
if old_body:
|
if old_body:
|
||||||
@@ -1123,20 +1078,20 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_obj_origin_to_bboxcenter(cls, obj: bpy.types.Object) -> None:
|
def set_space_representation_from_polygon(
|
||||||
mat = obj.matrix_world
|
cls,
|
||||||
inverted = mat.inverted()
|
obj: bpy.types.Object,
|
||||||
local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector())
|
element: ifcopenshell.entity_instance,
|
||||||
global_bbox_center = mat @ local_bbox_center
|
poly: Polygon,
|
||||||
|
h: float,
|
||||||
|
polygon_is_si: bool = True,
|
||||||
|
) -> None:
|
||||||
|
"""Create or replace the IFC body representation of a space from a polygon.
|
||||||
|
|
||||||
oldLoc = obj.location
|
:param h: The height in SI (meters).
|
||||||
newLoc = global_bbox_center
|
"""
|
||||||
diff = newLoc - oldLoc
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
for vert in obj.data.vertices:
|
cls.set_extrusion_representation_from_polygon(obj, element, poly, h / unit_scale, polygon_is_si)
|
||||||
aux_vector = mat @ vert.co
|
|
||||||
aux_vector = aux_vector - diff
|
|
||||||
vert.co = inverted @ aux_vector
|
|
||||||
obj.location = newLoc
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_obj_origin_to_cursor_position_and_zero_elevation(cls, obj: bpy.types.Object) -> None:
|
def set_obj_origin_to_cursor_position_and_zero_elevation(cls, obj: bpy.types.Object) -> None:
|
||||||
@@ -1183,47 +1138,6 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
if z != 0:
|
if z != 0:
|
||||||
obj.location = obj.location + Vector((0, 0, z))
|
obj.location = obj.location + Vector((0, 0, z))
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_2d_vertices_from_obj(cls, obj: bpy.types.Object) -> list[tuple]:
|
|
||||||
points = []
|
|
||||||
vectors = [v.co for v in obj.data.vertices.values()]
|
|
||||||
for vector in vectors:
|
|
||||||
points.append(vector.xy)
|
|
||||||
|
|
||||||
points.append(vectors[0].xy)
|
|
||||||
return points
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_scaled_2d_vertices(cls, points: list[Vector]) -> list[tuple[float, float]]:
|
|
||||||
model = tool.Ifc.get()
|
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(model)
|
|
||||||
_points = []
|
|
||||||
for p in points:
|
|
||||||
_p = list(p)
|
|
||||||
_p[0] /= unit_scale
|
|
||||||
_p[1] /= unit_scale
|
|
||||||
_points.append(_p)
|
|
||||||
return _points
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def assign_swept_area_outer_curve_from_2d_vertices(cls, obj: bpy.types.Object, vertices: list[Vector]) -> None:
|
|
||||||
body = cls.get_body_representation(obj)
|
|
||||||
model = tool.Ifc.get()
|
|
||||||
extrusion = tool.Model.get_extrusion(body)
|
|
||||||
area = extrusion.SweptArea
|
|
||||||
old_area = area.OuterCurve
|
|
||||||
|
|
||||||
builder = ifcopenshell.util.shape_builder.ShapeBuilder(model)
|
|
||||||
outer_curve = builder.polyline(vertices, closed=True)
|
|
||||||
|
|
||||||
area.OuterCurve = outer_curve
|
|
||||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_area)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_body_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
|
||||||
element = tool.Ifc.get_entity(obj)
|
|
||||||
return ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def assign_ifcspace_class_to_obj(cls, obj: bpy.types.Object) -> None:
|
def assign_ifcspace_class_to_obj(cls, obj: bpy.types.Object) -> None:
|
||||||
bonsai.core.root.assign_class(
|
bonsai.core.root.assign_class(
|
||||||
@@ -1237,18 +1151,40 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def assign_type_to_obj(cls, obj: bpy.types.Object) -> None:
|
def assign_type_to_obj(cls, obj: bpy.types.Object) -> None:
|
||||||
# TODO this code looks in the wrong spot and suspicious
|
|
||||||
props = tool.Model.get_model_props()
|
props = tool.Model.get_model_props()
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
relating_type_id = props.relating_type_id
|
relating_type_id = props.relating_type_id
|
||||||
relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
relating_type = ifc_file.by_id(int(relating_type_id))
|
||||||
ifc_class = relating_type.is_a()
|
ifc_class = relating_type.is_a()
|
||||||
instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, ifc_file.schema)[0]
|
instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, ifc_file.schema)[0]
|
||||||
bpy.ops.bim.assign_class(obj=obj.name, ifc_class=instance_class)
|
bonsai.core.root.assign_class(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Collector,
|
||||||
|
tool.Root,
|
||||||
|
obj=obj,
|
||||||
|
ifc_class=instance_class,
|
||||||
|
should_add_representation=False,
|
||||||
|
)
|
||||||
element = tool.Ifc.get_entity(obj)
|
element = tool.Ifc.get_entity(obj)
|
||||||
assert element
|
assert element
|
||||||
ifcopenshell.api.type.assign_type(ifc_file, related_objects=[element], relating_type=relating_type)
|
ifcopenshell.api.type.assign_type(ifc_file, related_objects=[element], relating_type=relating_type)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_covering_representation_from_polygon(
|
||||||
|
cls,
|
||||||
|
obj: bpy.types.Object,
|
||||||
|
poly: Polygon,
|
||||||
|
polygon_is_si: bool = True,
|
||||||
|
) -> None:
|
||||||
|
"""Create the covering body representation from a polygon, extruded by the type's material layer thickness."""
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
relating_type = ifcopenshell.util.element.get_type(element)
|
||||||
|
material = ifcopenshell.util.element.get_material(relating_type, should_skip_usage=True)
|
||||||
|
depth = 0.0
|
||||||
|
if material and material.is_a("IfcMaterialLayerSet"):
|
||||||
|
depth = sum(layer.LayerThickness for layer in material.MaterialLayers)
|
||||||
|
cls.set_extrusion_representation_from_polygon(obj, element, poly, depth, polygon_is_si)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def assign_relating_type_to_element(
|
def assign_relating_type_to_element(
|
||||||
cls,
|
cls,
|
||||||
@@ -1259,14 +1195,6 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
) -> None:
|
) -> None:
|
||||||
bonsai.core.type.assign_type(ifc, tool.Model, type, element=element, type=relating_type)
|
bonsai.core.type.assign_type(ifc, tool.Model, type, element=element, type=relating_type)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def regen_obj_representation(cls, obj: bpy.types.Object, body: ifcopenshell.entity_instance) -> None:
|
|
||||||
bonsai.core.geometry.switch_representation(
|
|
||||||
tool.Ifc,
|
|
||||||
tool.Geometry,
|
|
||||||
obj=obj,
|
|
||||||
representation=body,
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_space_visibility(cls, is_visible: bool) -> None:
|
def set_space_visibility(cls, is_visible: bool) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user