Now it's possible to create spaces automatically from selected walls

This commit is contained in:
Massimo Fabbro
2023-04-22 22:27:25 +02:00
parent 0d2ab2317a
commit 2ff5ffcb24
3 changed files with 141 additions and 0 deletions
@@ -96,6 +96,7 @@ classes = (
slab.ResetVertex,
slab.SetArcIndex,
space.GenerateSpace,
space.GenerateSpacesFromWalls,
prop.BIMModelProperties,
prop.BIMArrayProperties,
prop.BIMStairProperties,
@@ -26,6 +26,7 @@ import blenderbim.tool as tool
import blenderbim.core.type
from math import pi
from mathutils import Vector, Matrix
from shapely import Polygon
class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
@@ -166,3 +167,140 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
(obj.matrix_world @ Vector((max_x, max_y, 0.0))).to_2d(),
],
}
class GenerateSpacesFromWalls(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.generate_spaces_from_walls"
bl_label = "Generate Spaces From Walls"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Generate spaces from selected walls. The active object must be a wall."
@classmethod
def poll(cls, context):
return context.selected_objects
def _execute(self, context):
props = context.scene.BIMModelProperties
active_obj = bpy.context.active_object
if not active_obj:
self.report({'ERROR'}, "No active object. Please select a wall")
return
element = None
element = tool.Ifc.get_entity(active_obj)
if not element.is_a("IfcWall"):
self.report({'ERROR'}, "The active object is not a wall. Please select a wall.")
return
collection = active_obj.users_collection[0]
collection_obj = bpy.data.objects.get(collection.name)
if not collection_obj:
self.report({'ERROR'}, "No collection found. Please insert one.")
return
spatial_element = tool.Ifc.get_entity(collection_obj)
if not spatial_element:
self.report({'ERROR'}, "The collection hasn't an ifc space entity. Please provide one.")
return
if not bpy.context.selected_objects:
self.report({'ERROR'}, "No selected objects found. Please select walls.")
return
x, y, z = active_obj.matrix_world.translation.xyz
mat = active_obj.matrix_world
h = active_obj.dimensions.z
boundary_elements = []
for obj in bpy.context.selected_objects:
subelement = tool.Ifc.get_entity(obj)
if subelement.is_a("IfcWall"):
boundary_elements.append(subelement)
polys = []
for boundary_element in boundary_elements:
obj = tool.Ifc.get_object(boundary_element)
if not obj:
continue
points = []
base = self.get_obj_base_points(obj)
for index in ["low_left", "low_right", "high_right", "high_left"]:
point = base[index]
points.append(point)
polys.append(Polygon(points))
model = tool.Ifc.get()
project_unit = ifcopenshell.util.unit.get_project_unit(model, "LENGTHUNIT")
prefix=getattr(project_unit, "Prefix", None)
tolerance = 0.03
converted_tolerance = ifcopenshell.util.unit.convert(
value = tolerance,
from_prefix = None,
from_unit = "METRE",
to_prefix = prefix,
to_unit = project_unit.Name,
)
union = shapely.ops.unary_union(polys).buffer(converted_tolerance, cap_style = 2, join_style = 2)
i=0
for linear_ring in union.interiors:
poly = Polygon(linear_ring)
poly = poly.buffer(converted_tolerance, single_sided=True, cap_style = 2, join_style = 2)
bm = bmesh.new()
bm.verts.index_update()
bm.edges.index_update()
mat_invert = mat.inverted()
new_verts = [bm.verts.new(mat_invert @ Vector([v[0], v[1], 0])) for v in poly.exterior.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)
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)
name = "Space" + str(i)
mesh = bpy.data.meshes.new(name = name)
bm.to_mesh(mesh)
bm.free()
obj = bpy.data.objects.new(name, mesh)
obj.matrix_world = mat
collection.objects.link(obj)
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace")
i+=1
return {"FINISHED"}
def get_obj_base_points(self, obj):
x_values = [(obj.matrix_world @ Vector(v)).x for v in obj.bound_box]
y_values = [(obj.matrix_world @ Vector(v)).y for v in obj.bound_box]
return {
"low_left": (x_values[0], y_values[0]),
"high_left": (x_values[3], y_values[3]),
"low_right": (x_values[4], y_values[4]),
"high_right": (x_values[7], y_values[7]),
}
@@ -135,6 +135,8 @@ class BIM_PT_authoring(Panel):
row.operator("bim.align_wall", icon="ANCHOR_TOP", text="Ext.").align_type = "EXTERIOR"
row.operator("bim.align_wall", icon="ANCHOR_CENTER", text="C/L").align_type = "CENTERLINE"
row.operator("bim.align_wall", icon="ANCHOR_BOTTOM", text="Int.").align_type = "INTERIOR"
row = self.layout.row(align=True)
row.operator("bim.generate_spaces_from_walls")
class BIM_PT_array(bpy.types.Panel):