mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 22:33:33 +00:00
Experimental operator to generate hipped roofs from a footprint.
This commit is contained in:
@@ -48,6 +48,7 @@ classes = (
|
|||||||
product.AlignProduct,
|
product.AlignProduct,
|
||||||
product.ChangeTypePage,
|
product.ChangeTypePage,
|
||||||
product.DisplayConstrTypes,
|
product.DisplayConstrTypes,
|
||||||
|
product.GenerateHippedRoof,
|
||||||
product.GenerateSpace,
|
product.GenerateSpace,
|
||||||
product.LoadTypeThumbnails,
|
product.LoadTypeThumbnails,
|
||||||
product.ReinvokeOperator,
|
product.ReinvokeOperator,
|
||||||
|
|||||||
@@ -583,7 +583,6 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_idname = "bim.generate_space"
|
bl_idname = "bim.generate_space"
|
||||||
bl_label = "Generate Space"
|
bl_label = "Generate Space"
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
page: bpy.props.IntProperty()
|
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
# Note: this experimental operator relies on Shapely v2 (not v1.8 which
|
# Note: this experimental operator relies on Shapely v2 (not v1.8 which
|
||||||
@@ -629,7 +628,7 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
space_polygon = None
|
space_polygon = None
|
||||||
for polygon in closed_polygons.geoms:
|
for polygon in closed_polygons.geoms:
|
||||||
if shapely.contains_xy(polygon, x, y):
|
if shapely.contains_xy(polygon, x, y):
|
||||||
space_polygon = polygon
|
space_polygon = shapely.force_3d(polygon)
|
||||||
|
|
||||||
if not space_polygon:
|
if not space_polygon:
|
||||||
return
|
return
|
||||||
@@ -638,16 +637,15 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bm.verts.index_update()
|
bm.verts.index_update()
|
||||||
bm.edges.index_update()
|
bm.edges.index_update()
|
||||||
|
|
||||||
new_verts = [bm.verts.new(v) for v in shapely.force_3d(space_polygon).exterior.coords[0:-1]]
|
new_verts = [bm.verts.new(v) for v in space_polygon.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[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.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
||||||
|
|
||||||
for interior in space_polygon.interiors:
|
for interior in space_polygon.interiors:
|
||||||
new_verts = [bm.verts.new(v) for v in shapely.force_3d(interior).coords[0:-1]]
|
new_verts = [bm.verts.new(v) for v in interior.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[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.edges.new((new_verts[len(new_verts) - 1], new_verts[0]))
|
||||||
|
|
||||||
|
|
||||||
bm.verts.index_update()
|
bm.verts.index_update()
|
||||||
bm.edges.index_update()
|
bm.edges.index_update()
|
||||||
|
|
||||||
@@ -683,3 +681,93 @@ class GenerateSpace(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
(obj.matrix_world @ Vector((max_x, max_y, 0.0))).to_2d(),
|
(obj.matrix_world @ Vector((max_x, max_y, 0.0))).to_2d(),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateHippedRoof(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.generate_hipped_roof"
|
||||||
|
bl_label = "Generate Hipped Roof"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
mode: bpy.props.StringProperty(default="ANGLE")
|
||||||
|
height: bpy.props.FloatProperty(default=1)
|
||||||
|
angle: bpy.props.FloatProperty(default=10)
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
import bmesh
|
||||||
|
import shapely
|
||||||
|
from math import tan, radians
|
||||||
|
from bpypolyskel import bpypolyskel
|
||||||
|
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
|
||||||
|
boundary_lines = []
|
||||||
|
|
||||||
|
for edge in obj.data.edges:
|
||||||
|
boundary_lines.append(
|
||||||
|
shapely.LineString([obj.data.vertices[edge.vertices[0]].co, obj.data.vertices[edge.vertices[1]].co])
|
||||||
|
)
|
||||||
|
|
||||||
|
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
|
||||||
|
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
|
||||||
|
|
||||||
|
roof_polygon = None
|
||||||
|
biggest_area = 0
|
||||||
|
for polygon in closed_polygons.geoms:
|
||||||
|
area = polygon.area
|
||||||
|
if area > biggest_area:
|
||||||
|
roof_polygon = polygon
|
||||||
|
biggest_area = area
|
||||||
|
|
||||||
|
roof_polygon = shapely.force_3d(roof_polygon)
|
||||||
|
|
||||||
|
if not shapely.is_ccw(roof_polygon):
|
||||||
|
roof_polygon = roof_polygon.reverse()
|
||||||
|
|
||||||
|
# Define vertices for the base footprint of the building at height 0.0
|
||||||
|
# counterclockwise order
|
||||||
|
verts = [Vector(v) for v in roof_polygon.exterior.coords[0:-1]]
|
||||||
|
total_exterior_verts = len(verts)
|
||||||
|
next_index = total_exterior_verts
|
||||||
|
|
||||||
|
inner_loops = None
|
||||||
|
for interior in roof_polygon.interiors:
|
||||||
|
if inner_loops is None:
|
||||||
|
inner_loops = []
|
||||||
|
loop = interior.coords[0:-1]
|
||||||
|
total_verts = len(loop)
|
||||||
|
verts.extend([Vector(v) for v in loop])
|
||||||
|
inner_loops.append((next_index, total_verts))
|
||||||
|
next_index += total_verts
|
||||||
|
|
||||||
|
unit_vectors = None # we have no unit vectors, let them computed by polygonize()
|
||||||
|
start_exterior_index = 0
|
||||||
|
|
||||||
|
faces = []
|
||||||
|
|
||||||
|
if self.mode == "HEIGHT":
|
||||||
|
height = self.height
|
||||||
|
angle = 0.0
|
||||||
|
else:
|
||||||
|
angle = tan(radians(round(self.angle, 4)))
|
||||||
|
height = 0.0
|
||||||
|
|
||||||
|
faces = bpypolyskel.polygonize(
|
||||||
|
verts, start_exterior_index, total_exterior_verts, inner_loops, height, angle, faces, unit_vectors
|
||||||
|
)
|
||||||
|
|
||||||
|
edges = []
|
||||||
|
mesh = bpy.data.meshes.new(name="Building_with_Roof")
|
||||||
|
mesh.from_pydata(verts, edges, faces)
|
||||||
|
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(mesh)
|
||||||
|
|
||||||
|
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, 0.1], verts=extruded_verts)
|
||||||
|
|
||||||
|
bm.to_mesh(mesh)
|
||||||
|
bm.free()
|
||||||
|
|
||||||
|
obj.data = mesh
|
||||||
|
|||||||
Reference in New Issue
Block a user