mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-29 16:22:53 +00:00
Added "IFC Stair" to object modifiers
1) Added "IFC Stair" to object modifiers - it works in similar way as "IFC Array". It creates a stair from current object (if it's IfcStairFlight) and writes it's parameters to property set "BBIM_Stair" and also to IfcStairFlight parameters. Then you can edit it - modifying stair parameters and immediately see the changes, then you can save those changes or discard them. 2) "Clever Stair" operator (in Shift+A menu) works a bit different now - it creates new IfcStairFlight with IFC Stair modifier activated.
This commit is contained in:
@@ -73,8 +73,10 @@ classes = (
|
|||||||
prop.ConstrBrowserState,
|
prop.ConstrBrowserState,
|
||||||
prop.BIMModelProperties,
|
prop.BIMModelProperties,
|
||||||
prop.BIMArrayProperties,
|
prop.BIMArrayProperties,
|
||||||
|
prop.BIMStairProperties,
|
||||||
ui.BIM_PT_authoring,
|
ui.BIM_PT_authoring,
|
||||||
ui.BIM_PT_array,
|
ui.BIM_PT_array,
|
||||||
|
ui.BIM_PT_stair,
|
||||||
ui.DisplayConstrTypesUI,
|
ui.DisplayConstrTypesUI,
|
||||||
ui.LaunchTypeManager,
|
ui.LaunchTypeManager,
|
||||||
ui.HelpConstrTypes,
|
ui.HelpConstrTypes,
|
||||||
@@ -82,6 +84,11 @@ classes = (
|
|||||||
grid.BIM_OT_add_object,
|
grid.BIM_OT_add_object,
|
||||||
stair.BIM_OT_add_object,
|
stair.BIM_OT_add_object,
|
||||||
stair.BIM_OT_add_clever_stair,
|
stair.BIM_OT_add_clever_stair,
|
||||||
|
stair.AddStair,
|
||||||
|
stair.CancelEditingStair,
|
||||||
|
stair.FinishEditingStair,
|
||||||
|
stair.EnableEditingStair,
|
||||||
|
stair.RemoveStair,
|
||||||
pie.OpenPieClass,
|
pie.OpenPieClass,
|
||||||
pie.PieUpdateContainer,
|
pie.PieUpdateContainer,
|
||||||
pie.PieAddOpening,
|
pie.PieAddOpening,
|
||||||
@@ -97,6 +104,7 @@ def register():
|
|||||||
bpy.utils.register_tool(workspace.BimTool, after={"builtin.scale_cage"}, separator=True, group=True)
|
bpy.utils.register_tool(workspace.BimTool, after={"builtin.scale_cage"}, separator=True, group=True)
|
||||||
bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties)
|
bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties)
|
||||||
bpy.types.Object.BIMArrayProperties = bpy.props.PointerProperty(type=prop.BIMArrayProperties)
|
bpy.types.Object.BIMArrayProperties = bpy.props.PointerProperty(type=prop.BIMArrayProperties)
|
||||||
|
bpy.types.Object.BIMStairProperties = bpy.props.PointerProperty(type=prop.BIMStairProperties)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.append(grid.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.append(grid.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.append(stair.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.append(stair.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_add.append(ui.add_menu)
|
bpy.types.VIEW3D_MT_add.append(ui.add_menu)
|
||||||
@@ -114,6 +122,7 @@ def unregister():
|
|||||||
bpy.utils.unregister_tool(workspace.BimTool)
|
bpy.utils.unregister_tool(workspace.BimTool)
|
||||||
del bpy.types.Scene.BIMModelProperties
|
del bpy.types.Scene.BIMModelProperties
|
||||||
del bpy.types.Object.BIMArrayProperties
|
del bpy.types.Object.BIMArrayProperties
|
||||||
|
del bpy.types.Object.BIMStairProperties
|
||||||
bpy.app.handlers.load_post.remove(handler.load_post)
|
bpy.app.handlers.load_post.remove(handler.load_post)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.remove(grid.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.remove(grid.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button)
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ import blenderbim.tool as tool
|
|||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound
|
from blenderbim.bim.module.model.root import ConstrTypeEntityNotFound
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
AuthoringData.is_loaded = False
|
AuthoringData.is_loaded = False
|
||||||
ArrayData.is_loaded = False
|
ArrayData.is_loaded = False
|
||||||
|
StairData.is_loaded = False
|
||||||
|
|
||||||
|
|
||||||
class AuthoringData:
|
class AuthoringData:
|
||||||
@@ -352,3 +354,23 @@ class ArrayData:
|
|||||||
except:
|
except:
|
||||||
parameters["has_parent"] = False
|
parameters["has_parent"] = False
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
|
class StairData:
|
||||||
|
data = {}
|
||||||
|
is_loaded = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls):
|
||||||
|
cls.is_loaded = True
|
||||||
|
cls.data = {"parameters": cls.parameters()}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parameters(cls):
|
||||||
|
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||||
|
if element:
|
||||||
|
psets = ifcopenshell.util.element.get_psets(element)
|
||||||
|
parameters = psets.get("BBIM_Stair", None)
|
||||||
|
if parameters:
|
||||||
|
parameters["data"] = json.loads(parameters.get("Data", "[]") or "[]")
|
||||||
|
return parameters
|
||||||
|
|||||||
@@ -228,8 +228,8 @@ class BIMModelProperties(PropertyGroup):
|
|||||||
x: bpy.props.FloatProperty(name="X", default=0.5)
|
x: bpy.props.FloatProperty(name="X", default=0.5)
|
||||||
y: bpy.props.FloatProperty(name="Y", default=0.5)
|
y: bpy.props.FloatProperty(name="Y", default=0.5)
|
||||||
z: bpy.props.FloatProperty(name="Z", default=0.5)
|
z: bpy.props.FloatProperty(name="Z", default=0.5)
|
||||||
rl1: bpy.props.FloatProperty(name="RL", default=1) # Used for things like walls, doors, flooring, skirting, etc
|
rl1: bpy.props.FloatProperty(name="RL", default=1) # Used for things like walls, doors, flooring, skirting, etc
|
||||||
rl2: bpy.props.FloatProperty(name="RL", default=1) # Used for things like windows, other hosted furniture
|
rl2: bpy.props.FloatProperty(name="RL", default=1) # Used for things like windows, other hosted furniture
|
||||||
x_angle: bpy.props.FloatProperty(name="X Angle", default=0)
|
x_angle: bpy.props.FloatProperty(name="X Angle", default=0)
|
||||||
type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page)
|
type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page)
|
||||||
type_template: bpy.props.EnumProperty(
|
type_template: bpy.props.EnumProperty(
|
||||||
@@ -253,3 +253,28 @@ class BIMArrayProperties(PropertyGroup):
|
|||||||
x: bpy.props.FloatProperty(name="X", default=0)
|
x: bpy.props.FloatProperty(name="X", default=0)
|
||||||
y: bpy.props.FloatProperty(name="Y", default=0)
|
y: bpy.props.FloatProperty(name="Y", default=0)
|
||||||
z: bpy.props.FloatProperty(name="Z", default=0)
|
z: bpy.props.FloatProperty(name="Z", default=0)
|
||||||
|
|
||||||
|
|
||||||
|
class BIMStairProperties(PropertyGroup):
|
||||||
|
is_editing: bpy.props.IntProperty(default=-1)
|
||||||
|
width: bpy.props.FloatProperty(name="Width", default=1.2, soft_min=0.01)
|
||||||
|
height: bpy.props.FloatProperty(name="Height", default=1.0, soft_min=0.01)
|
||||||
|
number_of_treads: bpy.props.IntProperty(name="Number of Treads (Goings)", default=6, soft_min=1)
|
||||||
|
tread_depth: bpy.props.FloatProperty(name="Tread Depth", default=0.25, soft_min=0.01)
|
||||||
|
tread_run: bpy.props.FloatProperty(name="Tread Run", default=0.3, soft_min=0.01)
|
||||||
|
base_slab_depth: bpy.props.FloatProperty(name="Base slab depth", default=0.25, soft_min=0)
|
||||||
|
top_slab_depth: bpy.props.FloatProperty(name="Top slab depth", default=0.25, soft_min=0)
|
||||||
|
has_top_nib: bpy.props.BoolProperty(name="Has top nib", default=True)
|
||||||
|
|
||||||
|
def get_props_kwargs(self):
|
||||||
|
kwargs = {
|
||||||
|
"width": self.width,
|
||||||
|
"height": self.height,
|
||||||
|
"number_of_treads": self.number_of_treads,
|
||||||
|
"tread_depth": self.tread_depth,
|
||||||
|
"tread_run": self.tread_run,
|
||||||
|
"base_slab_depth": self.base_slab_depth,
|
||||||
|
"top_slab_depth": self.top_slab_depth,
|
||||||
|
"has_top_nib": self.has_top_nib,
|
||||||
|
}
|
||||||
|
return kwargs
|
||||||
|
|||||||
@@ -20,18 +20,22 @@ import bpy
|
|||||||
from bpy.types import Operator
|
from bpy.types import Operator
|
||||||
from bpy.props import FloatProperty, IntProperty, BoolProperty
|
from bpy.props import FloatProperty, IntProperty, BoolProperty
|
||||||
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
||||||
from mathutils import Vector
|
|
||||||
|
|
||||||
import bmesh
|
import bmesh
|
||||||
from bmesh.types import BMVert
|
from bmesh.types import BMVert
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
import blenderbim
|
||||||
|
import blenderbim.tool as tool
|
||||||
|
from blenderbim.bim.module.model.prop import BIMStairProperties
|
||||||
|
|
||||||
|
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
def add_dumb_stair_object(self, context):
|
def add_dumb_stair_object(self, context):
|
||||||
if self.number_of_treads <= 0:
|
|
||||||
self.number_of_treads = 1
|
|
||||||
verts = [
|
verts = [
|
||||||
Vector((0, 0, 0)),
|
Vector((0, 0, 0)),
|
||||||
Vector((0, self.tread_length, 0)),
|
Vector((0, self.tread_length, 0)),
|
||||||
@@ -69,7 +73,7 @@ class BIM_OT_add_object(Operator, AddObjectHelper):
|
|||||||
width: FloatProperty(name="Width", default=1.1)
|
width: FloatProperty(name="Width", default=1.1)
|
||||||
height: FloatProperty(name="Height", default=1)
|
height: FloatProperty(name="Height", default=1)
|
||||||
tread_depth: FloatProperty(name="Tread Depth", default=0.2)
|
tread_depth: FloatProperty(name="Tread Depth", default=0.2)
|
||||||
number_of_treads: IntProperty(name="Number of Treads (Goings)", default=6)
|
number_of_treads: IntProperty(name="Number of Treads (Goings)", default=6, soft_min=1)
|
||||||
tread_length: FloatProperty(name="Tread Length (Going)", default=0.25)
|
tread_length: FloatProperty(name="Tread Length (Going)", default=0.25)
|
||||||
riser_height: FloatProperty(name="*Calculated* Riser Height")
|
riser_height: FloatProperty(name="*Calculated* Riser Height")
|
||||||
length: FloatProperty(name="*Calculated* Length")
|
length: FloatProperty(name="*Calculated* Length")
|
||||||
@@ -79,30 +83,25 @@ class BIM_OT_add_object(Operator, AddObjectHelper):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
def create_stair(
|
def generate_stair_2d_profile(
|
||||||
operator,
|
number_of_treads,
|
||||||
context,
|
height,
|
||||||
width=1.2,
|
width,
|
||||||
height=1.0,
|
tread_run,
|
||||||
number_of_treads=6,
|
tread_depth,
|
||||||
tread_depth=0.25,
|
has_top_nib,
|
||||||
tread_run=0.3,
|
top_slab_depth,
|
||||||
# tread_rise = None, # 0.167
|
base_slab_depth,
|
||||||
base_slab_depth=0.25,
|
|
||||||
top_slab_depth=0.25,
|
|
||||||
has_top_nib=False,
|
|
||||||
stair_type="CONCRETE",
|
stair_type="CONCRETE",
|
||||||
extrude=True,
|
|
||||||
):
|
):
|
||||||
|
|
||||||
vertices = []
|
vertices = []
|
||||||
edges = []
|
edges = []
|
||||||
|
|
||||||
# 2d profile generation
|
number_of_risers = number_of_treads + 1
|
||||||
tread_rise = height / number_of_treads
|
tread_rise = height / number_of_risers
|
||||||
length = tread_run * number_of_treads
|
length = tread_run * number_of_risers
|
||||||
|
|
||||||
for i in range(number_of_treads):
|
for i in range(number_of_risers):
|
||||||
vertices.extend([
|
vertices.extend([
|
||||||
Vector((tread_run*i, 0, tread_rise*i)),
|
Vector((tread_run*i, 0, tread_rise*i)),
|
||||||
Vector((tread_run*i, 0, tread_rise*(i+1)))
|
Vector((tread_run*i, 0, tread_rise*(i+1)))
|
||||||
@@ -112,8 +111,8 @@ def create_stair(
|
|||||||
edges.append((cur_vertex - 1, cur_vertex))
|
edges.append((cur_vertex - 1, cur_vertex))
|
||||||
edges.append((cur_vertex, cur_vertex + 1))
|
edges.append((cur_vertex, cur_vertex + 1))
|
||||||
|
|
||||||
vertices.append(Vector((tread_run * number_of_treads, 0, tread_rise * number_of_treads)))
|
vertices.append(Vector((tread_run * number_of_risers, 0, tread_rise * number_of_risers)))
|
||||||
edges.append((number_of_treads * 2, number_of_treads * 2 - 1))
|
edges.append((number_of_risers * 2, number_of_risers * 2 - 1))
|
||||||
|
|
||||||
td_vector = Vector((vertices[2][2], 0, -vertices[2][0])).normalized() * tread_depth
|
td_vector = Vector((vertices[2][2], 0, -vertices[2][0])).normalized() * tread_depth
|
||||||
|
|
||||||
@@ -127,15 +126,15 @@ def create_stair(
|
|||||||
|
|
||||||
# top nib
|
# top nib
|
||||||
if has_top_nib:
|
if has_top_nib:
|
||||||
vertices.append( vertices[number_of_treads * 2] + Vector((0, 0, -top_slab_depth)) )
|
vertices.append( vertices[number_of_risers * 2] + Vector((0, 0, -top_slab_depth)) )
|
||||||
vertices.append( vertices[number_of_treads * 2] + Vector(((-top_slab_depth - b) / k, 0, -top_slab_depth)) )
|
vertices.append( vertices[number_of_risers * 2] + Vector(((-top_slab_depth - b) / k, 0, -top_slab_depth)) )
|
||||||
last_vertex_i = len(vertices) - 1
|
last_vertex_i = len(vertices) - 1
|
||||||
edges.append( (number_of_treads * 2, last_vertex_i - 1) )
|
edges.append( (number_of_risers * 2, last_vertex_i - 1) )
|
||||||
edges.append( (last_vertex_i - 1, last_vertex_i) )
|
edges.append( (last_vertex_i - 1, last_vertex_i) )
|
||||||
else:
|
else:
|
||||||
vertices.append(vertices[number_of_treads * 2] + depth_vector)
|
vertices.append(vertices[number_of_risers * 2] + depth_vector)
|
||||||
last_vertex_i = len(vertices) - 1
|
last_vertex_i = len(vertices) - 1
|
||||||
edges.append((number_of_treads * 2, last_vertex_i))
|
edges.append((number_of_risers * 2, last_vertex_i))
|
||||||
|
|
||||||
top_nib_end = len(vertices) - 1
|
top_nib_end = len(vertices) - 1
|
||||||
|
|
||||||
@@ -154,7 +153,17 @@ def create_stair(
|
|||||||
|
|
||||||
edges.append( (bottom_nib_end, top_nib_end) )
|
edges.append( (bottom_nib_end, top_nib_end) )
|
||||||
faces = [list(range(len(vertices)))]
|
faces = [list(range(len(vertices)))]
|
||||||
# profile generation finished
|
|
||||||
|
return (vertices, edges, faces)
|
||||||
|
|
||||||
|
|
||||||
|
def update_stair_modifier(context):
|
||||||
|
obj = context.active_object
|
||||||
|
props = obj.BIMStairProperties
|
||||||
|
|
||||||
|
props_kwargs = props.get_props_kwargs()
|
||||||
|
props_kwargs["stair_type"] = "CONCRETE"
|
||||||
|
vertices, edges, faces = generate_stair_2d_profile(**props_kwargs)
|
||||||
|
|
||||||
obj = context.object
|
obj = context.object
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
@@ -168,49 +177,19 @@ def create_stair(
|
|||||||
|
|
||||||
bmesh.ops.contextual_create(bm, geom=new_edges)
|
bmesh.ops.contextual_create(bm, geom=new_edges)
|
||||||
|
|
||||||
if extrude:
|
bm.faces.ensure_lookup_table()
|
||||||
bm.faces.ensure_lookup_table()
|
faces = [bm.faces[0]]
|
||||||
faces = [bm.faces[0]]
|
extruded = bmesh.ops.extrude_face_region(bm, geom=faces)
|
||||||
extruded = bmesh.ops.extrude_face_region(bm, geom=faces)
|
extrusion_vector = Vector((0, 1, 0)) * props.width
|
||||||
extrusion_vector = Vector((0, 1, 0)) * width
|
translate_verts = [v for v in extruded["geom"] if isinstance(v, BMVert)]
|
||||||
translate_verts = [v for v in extruded["geom"] if isinstance(v, BMVert)]
|
bmesh.ops.translate(bm, vec=extrusion_vector, verts=translate_verts)
|
||||||
bmesh.ops.translate(bm, vec=extrusion_vector, verts=translate_verts)
|
|
||||||
|
|
||||||
# set origin
|
|
||||||
prev_cursor_location = bpy.context.scene.cursor.location.copy()
|
|
||||||
bpy.context.scene.cursor.location = vertices[0]
|
|
||||||
bpy.ops.object.origin_set(type="ORIGIN_CURSOR", center="MEDIAN")
|
|
||||||
bpy.context.scene.cursor.location = prev_cursor_location
|
|
||||||
|
|
||||||
if context.object.mode == "EDIT":
|
if context.object.mode == "EDIT":
|
||||||
bmesh.update_edit_mesh(obj.data)
|
bmesh.update_edit_mesh(obj.data)
|
||||||
else:
|
else:
|
||||||
bm.to_mesh(obj.data)
|
bm.to_mesh(obj.data)
|
||||||
bm.free()
|
bm.free()
|
||||||
|
obj.data.update()
|
||||||
return (obj, tread_rise, length)
|
|
||||||
|
|
||||||
|
|
||||||
def add_object_stair(self, context):
|
|
||||||
if context.object is None:
|
|
||||||
self.report({"ERROR"}, "Select object that will be transformed to a stair.")
|
|
||||||
return
|
|
||||||
|
|
||||||
mesh, tread_rise, length = create_stair(
|
|
||||||
operator=self,
|
|
||||||
context=context,
|
|
||||||
stair_type="CONCRETE",
|
|
||||||
width=self.width,
|
|
||||||
height=self.height,
|
|
||||||
number_of_treads=self.number_of_treads,
|
|
||||||
tread_depth=self.tread_depth,
|
|
||||||
tread_run=self.tread_run,
|
|
||||||
base_slab_depth=self.base_slab_depth,
|
|
||||||
top_slab_depth=self.top_slab_depth,
|
|
||||||
has_top_nib=self.has_top_nib,
|
|
||||||
)
|
|
||||||
self.tread_rise = tread_rise
|
|
||||||
self.length = length
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_OT_add_clever_stair(Operator):
|
class BIM_OT_add_clever_stair(Operator):
|
||||||
@@ -218,20 +197,164 @@ class BIM_OT_add_clever_stair(Operator):
|
|||||||
bl_label = "Clever Stair"
|
bl_label = "Clever Stair"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
width: FloatProperty(name="Width", default=1.2, soft_min=0.01)
|
|
||||||
height: FloatProperty(name="Height", default=1.0, soft_min=0.01)
|
|
||||||
number_of_treads: IntProperty(name="Number of Treads (Goings)", default=6, soft_min=1)
|
|
||||||
tread_depth: FloatProperty(name="Tread Depth", default=0.25, soft_min=0.01)
|
|
||||||
tread_run: FloatProperty(name="Tread Run", default=0.3, soft_min=0.01)
|
|
||||||
base_slab_depth: FloatProperty(name="Base slab depth", default=0.25, soft_min=0)
|
|
||||||
top_slab_depth: FloatProperty(name="Top slab depth", default=0.25, soft_min=0)
|
|
||||||
has_top_nib: BoolProperty(name="Has top nib", default=True)
|
|
||||||
|
|
||||||
tread_rise: FloatProperty(name="*Calculated* Tread Rise")
|
|
||||||
length: FloatProperty(name="*Calculated* Length")
|
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
add_object_stair(self, context)
|
ifcfile = tool.Ifc.get()
|
||||||
|
if not ifcfile:
|
||||||
|
self.report({"ERROR"}, "You need to start IFC project first to create a stair.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
if context.object is not None:
|
||||||
|
spawn_location = context.object.location.copy()
|
||||||
|
context.object.select_set(False)
|
||||||
|
else:
|
||||||
|
spawn_location = bpy.context.scene.cursor.location.copy()
|
||||||
|
|
||||||
|
mesh = bpy.data.meshes.new("StairFlight")
|
||||||
|
obj = bpy.data.objects.new("StairFlight", mesh)
|
||||||
|
bpy.context.scene.collection.objects.link(obj)
|
||||||
|
obj.location = spawn_location
|
||||||
|
body_context = ifcopenshell.util.representation.get_context(ifcfile, "Model", "Body", "MODEL_VIEW")
|
||||||
|
element = blenderbim.core.root.assign_class(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Collector,
|
||||||
|
tool.Root,
|
||||||
|
obj=obj,
|
||||||
|
ifc_class="IfcStairFlight",
|
||||||
|
should_add_representation=True,
|
||||||
|
context=body_context,
|
||||||
|
)
|
||||||
|
bpy.context.view_layer.objects.active = None
|
||||||
|
bpy.context.view_layer.objects.active = obj
|
||||||
|
obj.select_set(True)
|
||||||
|
bpy.ops.bim.add_stair()
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
# UI operators
|
||||||
|
class AddStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.add_stair"
|
||||||
|
bl_label = "Add Stair"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
obj = context.active_object
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
|
||||||
|
if not element.is_a("IfcStairFlight"):
|
||||||
|
self.report({"ERROR"}, "Object has to be IfcStairFlight type to add a stair.")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
props = obj.BIMStairProperties
|
||||||
|
|
||||||
|
stair_data = props.get_props_kwargs()
|
||||||
|
psets = ifcopenshell.util.element.get_psets(element)
|
||||||
|
pset = psets.get("BBIM_Stair", None)
|
||||||
|
|
||||||
|
if pset:
|
||||||
|
pset = tool.Ifc.get().by_id(pset["id"])
|
||||||
|
else:
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Stair")
|
||||||
|
|
||||||
|
ifcopenshell.api.run(
|
||||||
|
"pset.edit_pset",
|
||||||
|
tool.Ifc.get(),
|
||||||
|
pset=pset,
|
||||||
|
properties={"Data": json.dumps(stair_data)},
|
||||||
|
)
|
||||||
|
update_stair_modifier(context)
|
||||||
|
|
||||||
|
# update IfcStairFlight properties
|
||||||
|
element.NumberOfRisers = props.number_of_treads + 1
|
||||||
|
element.NumberOfTreads = props.number_of_treads
|
||||||
|
element.RiserHeight = props.height / element.NumberOfRisers
|
||||||
|
element.TreadLength = props.tread_depth
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class CancelEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.cancel_editing_stair"
|
||||||
|
bl_label = "Cancel editing Stair"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
obj = context.active_object
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
psets = ifcopenshell.util.element.get_psets(element)
|
||||||
|
data = json.loads(psets["BBIM_Stair"]["Data"])
|
||||||
|
props = obj.BIMStairProperties
|
||||||
|
# restore previous settings since editing was canceled
|
||||||
|
for prop_name in data:
|
||||||
|
setattr(props, prop_name, data[prop_name])
|
||||||
|
update_stair_modifier(context)
|
||||||
|
|
||||||
|
props.is_editing = -1
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class FinishEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.finish_editing_stair"
|
||||||
|
bl_label = "Finish editing stair"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
obj = context.active_object
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
props = obj.BIMStairProperties
|
||||||
|
|
||||||
|
psets = ifcopenshell.util.element.get_psets(element)
|
||||||
|
pset = psets["BBIM_Stair"]
|
||||||
|
data = props.get_props_kwargs()
|
||||||
|
|
||||||
|
props.is_editing = -1
|
||||||
|
|
||||||
|
update_stair_modifier(context)
|
||||||
|
|
||||||
|
pset = tool.Ifc.get().by_id(pset["id"])
|
||||||
|
data = json.dumps(data)
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": data})
|
||||||
|
|
||||||
|
# update IfcStairFlight properties
|
||||||
|
if element.is_a("IfcStairFlight"):
|
||||||
|
element.NumberOfRisers = props.number_of_treads + 1
|
||||||
|
element.NumberOfTreads = props.number_of_treads
|
||||||
|
element.RiserHeight = props.height / element.NumberOfRisers
|
||||||
|
element.TreadLength = props.tread_depth
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class EnableEditingStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.enable_editing_stair"
|
||||||
|
bl_label = "Enable Editing Stair"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
obj = context.active_object
|
||||||
|
props = obj.BIMStairProperties
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
pset = ifcopenshell.util.element.get_psets(element)
|
||||||
|
data = json.loads(pset["BBIM_Stair"]["Data"])
|
||||||
|
# required since we could load pset from .ifc and BIMStairProperties won't be set
|
||||||
|
for prop_name in data:
|
||||||
|
setattr(props, prop_name, data[prop_name])
|
||||||
|
|
||||||
|
props.is_editing = 1
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveStair(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.remove_stair"
|
||||||
|
bl_label = "Remove Stair"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
obj = context.active_object
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
obj.BIMStairProperties.is_editing = -1
|
||||||
|
|
||||||
|
pset = ifcopenshell.util.element.get_psets(element)
|
||||||
|
pset = tool.Ifc.get().by_id(pset["BBIM_Stair"]["id"])
|
||||||
|
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,10 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from bpy.types import Panel, Operator, Menu
|
from bpy.types import Panel, Operator, Menu
|
||||||
from blenderbim.bim.module.model.data import AuthoringData, ArrayData
|
from blenderbim.bim.module.model.data import AuthoringData, ArrayData, StairData
|
||||||
from blenderbim.bim.module.model.prop import store_cursor_position
|
from blenderbim.bim.module.model.prop import store_cursor_position
|
||||||
|
from blenderbim.bim.module.model.stair import update_stair_modifier
|
||||||
|
|
||||||
from blenderbim.bim.helper import prop_with_search
|
from blenderbim.bim.helper import prop_with_search
|
||||||
|
|
||||||
|
|
||||||
@@ -291,6 +293,7 @@ class BIM_PT_array(bpy.types.Panel):
|
|||||||
row.prop(props, "x")
|
row.prop(props, "x")
|
||||||
row.prop(props, "y")
|
row.prop(props, "y")
|
||||||
row.prop(props, "z")
|
row.prop(props, "z")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
row = box.row(align=True)
|
row = box.row(align=True)
|
||||||
row.label(text=f"{array['count']} Items", icon="MOD_ARRAY")
|
row.label(text=f"{array['count']} Items", icon="MOD_ARRAY")
|
||||||
@@ -306,6 +309,60 @@ class BIM_PT_array(bpy.types.Panel):
|
|||||||
row.operator("bim.add_array", icon="ADD", text="")
|
row.operator("bim.add_array", icon="ADD", text="")
|
||||||
|
|
||||||
|
|
||||||
|
class BIM_PT_stair(bpy.types.Panel):
|
||||||
|
bl_label = "IFC Stair"
|
||||||
|
bl_idname = "BIM_PT_stair"
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
bl_context = "modifier"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
# always display modifier if it's IFC object
|
||||||
|
return tool.Ifc.get() and tool.Ifc.get_entity(context.active_object)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
if not StairData.is_loaded:
|
||||||
|
StairData.load()
|
||||||
|
|
||||||
|
props = context.active_object.BIMStairProperties
|
||||||
|
|
||||||
|
if StairData.data["parameters"]:
|
||||||
|
row = self.layout.row(align=True)
|
||||||
|
row.label(text="Stair", icon="IPO_CONSTANT")
|
||||||
|
|
||||||
|
stair_data = StairData.data["parameters"]["data"]
|
||||||
|
box = self.layout.box()
|
||||||
|
if props.is_editing != -1:
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.operator("bim.finish_editing_stair", icon="CHECKMARK", text="Finish editing")
|
||||||
|
row.operator("bim.cancel_editing_stair", icon="CANCEL", text="")
|
||||||
|
row = box.row(align=True)
|
||||||
|
for prop_name in props.get_props_kwargs():
|
||||||
|
box.prop(props, prop_name)
|
||||||
|
update_stair_modifier(context)
|
||||||
|
else:
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.label(text=f"Parameters:", icon="MOD_ARRAY")
|
||||||
|
row.operator("bim.enable_editing_stair", icon="GREASEPENCIL", text="")
|
||||||
|
row.operator("bim.remove_stair", icon="X", text="")
|
||||||
|
row = box.row(align=True)
|
||||||
|
for prop in props.get_props_kwargs():
|
||||||
|
prop_value = stair_data[prop]
|
||||||
|
prop_value = round(prop_value, 5) if type(prop_value) is float else prop_value
|
||||||
|
box.label(text=f"{props.bl_rna.properties[prop].name}: {prop_value}")
|
||||||
|
|
||||||
|
# calculated properties
|
||||||
|
number_of_rises = props.number_of_treads + 1
|
||||||
|
box.label(text=f"Number of risers: {number_of_rises}")
|
||||||
|
box.label(text=f"Tread rise: {round(props.height / number_of_rises, 5)}")
|
||||||
|
box.label(text=f"Length: {round(props.tread_run * number_of_rises, 5)}")
|
||||||
|
else:
|
||||||
|
row = self.layout.row()
|
||||||
|
row.label(text="No Stair Found")
|
||||||
|
row.operator("bim.add_stair", icon="ADD", text="")
|
||||||
|
|
||||||
|
|
||||||
class BIM_MT_model(Menu):
|
class BIM_MT_model(Menu):
|
||||||
bl_idname = "BIM_MT_model"
|
bl_idname = "BIM_MT_model"
|
||||||
bl_label = "IFC Objects"
|
bl_label = "IFC Objects"
|
||||||
|
|||||||
Reference in New Issue
Block a user