stair profile generation code refactor

hopefully it's now a bit more readable
This commit is contained in:
Andrej730
2023-11-02 16:00:04 +05:00
parent 3ec513c56a
commit 617c8b9fd5
3 changed files with 69 additions and 61 deletions
@@ -201,13 +201,13 @@ class BIMStairProperties(PropertyGroup):
is_editing: bpy.props.BoolProperty(default=False)
width: bpy.props.FloatProperty(name="Width", default=1.2, soft_min=0.01, subtype="DISTANCE")
height: bpy.props.FloatProperty(name="Height", default=1.0, soft_min=0.01, subtype="DISTANCE")
number_of_treads: bpy.props.IntProperty(name="Number of treads", default=6, soft_min=1)
number_of_treads: bpy.props.IntProperty(name="Number of Treads", default=6, soft_min=1)
tread_depth: bpy.props.FloatProperty(name="Tread Depth", default=0.25, soft_min=0.01, subtype="DISTANCE")
tread_run: bpy.props.FloatProperty(name="Tread Run", default=0.3, soft_min=0.01, subtype="DISTANCE")
base_slab_depth: bpy.props.FloatProperty(name="Base slab depth", default=0.25, soft_min=0, subtype="DISTANCE")
top_slab_depth: bpy.props.FloatProperty(name="Top slab depth", default=0.25, soft_min=0, subtype="DISTANCE")
has_top_nib: bpy.props.BoolProperty(name="Has top nib", default=True)
stair_type: bpy.props.EnumProperty(name="Stair type", items=stair_types, default="CONCRETE")
base_slab_depth: bpy.props.FloatProperty(name="Base Slab Depth", default=0.25, soft_min=0, subtype="DISTANCE")
top_slab_depth: bpy.props.FloatProperty(name="Top Slab Depth", default=0.25, soft_min=0, subtype="DISTANCE")
has_top_nib: bpy.props.BoolProperty(name="Has Top Nib", default=True)
stair_type: bpy.props.EnumProperty(name="Stair Type", items=stair_types, default="CONCRETE")
def get_props_kwargs(self, convert_to_project_units=False, stair_type=None):
if not stair_type:
+61 -48
View File
@@ -924,8 +924,8 @@ class Model(blenderbim.core.tool.Model):
calculated_params = {}
number_of_rises = number_of_treads + 1
calculated_params["Number of risers"] = number_of_rises
calculated_params["Tread rise"] = round(height / number_of_rises, 5)
calculated_params["Number of Risers"] = number_of_rises
calculated_params["Tread Rise"] = round(height / number_of_rises, 5)
calculated_params["Length"] = round(tread_run * number_of_rises, 5)
return calculated_params
@@ -956,98 +956,111 @@ class Model(blenderbim.core.tool.Model):
if stair_type == "WOOD/STEEL":
builder = ShapeBuilder(None)
tread_shape = builder.get_rectangle_coords(
size=V(tread_run, 0, tread_depth), position=V(0, 0, -(tread_depth - tread_rise))
size=V(tread_run, tread_depth), position=V(0, -(tread_depth - tread_rise))
)
tread_offset = V(tread_run, 0, tread_rise)
tread_offset = V(tread_run, tread_rise)
# each tread is a separate shape
for i in range(number_of_risers):
cur_trade_shape = [v + tread_offset * i for v in tread_shape]
vertices.extend(cur_trade_shape)
cur_vertex = i * 4
edges.extend(
[
(cur_vertex, cur_vertex + 1),
(cur_vertex + 1, cur_vertex + 2),
(cur_vertex + 2, cur_vertex + 3),
(cur_vertex + 3, cur_vertex),
]
verts_to_add = (
(cur_vertex, cur_vertex + 1),
(cur_vertex + 1, cur_vertex + 2),
(cur_vertex + 2, cur_vertex + 3),
(cur_vertex + 3, cur_vertex),
)
faces.append(list(range(cur_vertex, cur_vertex + 1)))
return (vertices, edges, faces)
edges.extend(verts_to_add)
elif stair_type == "GENERIC":
vertices.append(Vector([0, 0, 0]))
vertices.append(Vector([0, 0]))
tread_verts = [Vector([0, 0, tread_rise]), Vector([tread_run, 0, tread_rise])]
tread_offset = Vector([tread_run, 0, tread_rise])
tread_verts = [Vector([0, tread_rise]), Vector([tread_run, tread_rise])]
tread_offset = Vector([tread_run, tread_rise])
# treads
for i in range(number_of_risers):
current_tread_verts = [v + tread_offset * i for v in tread_verts]
last_vert_i = len(vertices) - 1
last_vert_i = i * 2
edges.extend([(last_vert_i, last_vert_i + 1), (last_vert_i + 1, last_vert_i + 2)])
vertices.extend(current_tread_verts)
# close the shape
last_vert_i = len(vertices)
vertices.append(vertices[-1] * V(1, 0, 0))
vertices.append(vertices[-1] * V(1, 0))
edges.extend([(last_vert_i - 1, last_vert_i), (last_vert_i, 0)])
return (vertices, edges, faces)
elif stair_type == "CONCRETE":
# treads
for i in range(number_of_risers):
vertices.extend(
[Vector((tread_run * i, 0, tread_rise * i)), Vector((tread_run * i, 0, tread_rise * (i + 1)))]
)
vertices.append(Vector((tread_run * i, tread_rise * i)))
vertices.append(Vector((tread_run * i, tread_rise * (i + 1))))
cur_vertex = i * 2
if i != 0:
edges.append((cur_vertex - 1, cur_vertex))
edges.append((cur_vertex, cur_vertex + 1))
vertices.append(Vector((tread_run * number_of_risers, 0, tread_rise * number_of_risers)))
vertices.append(Vector((tread_run * number_of_risers, tread_rise * number_of_risers)))
edges.append((number_of_risers * 2, number_of_risers * 2 - 1))
td_vector = Vector((vertices[2][2], 0, -vertices[2][0])).normalized() * tread_depth
# add the nibs
tread_diagonal_dir = vertices[2].normalized()
# td_vector is clockwise orthogonal vector
td_vector = tread_diagonal_dir.yx * V(1, -1) * tread_depth
k = tread_rise / tread_run
# graph: https://www.desmos.com/calculator/bilmnti3cp
stair_tan = tread_rise / tread_run
s0 = vertices[0] + td_vector
b = s0.z - k * s0.x # comes from y = kx + b
# you could use td_vector as depth_vector
# but then stair won't be perpendicular to the X+
# b is kind of vertical tread_depth (along Z+)
depth_vector = Vector((0, 0, b))
# comes from y = stair_tan * x + b
b = s0.y - stair_tan * s0.x
# if we use td_vector as depth_vector
# then stair won't be perpendicular to the X+
# to make it perpendicular to X+ we calculate `b`
# `b` is basically Z-offset where stair starts
depth_vector = V(0, b)
# top nib
last_vert = vertices[-1]
last_vertex_i = len(vertices) - 1
# NOTE: has_top_nib = False and top_slab_depth are different things
if has_top_nib:
vertices.append(vertices[number_of_risers * 2] + Vector((0, 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
edges.append((number_of_risers * 2, last_vertex_i - 1))
edges.append((last_vertex_i - 1, last_vertex_i))
vertices.append(last_vert + Vector((0, -top_slab_depth)))
slab_overlaps_stair = abs(b) - top_slab_depth
# run distance by X until stair will meet the slab
run_slab_distance = slab_overlaps_stair / stair_tan
vertices.append(last_vert + Vector((run_slab_distance, -top_slab_depth)))
edges.append((last_vertex_i, last_vertex_i + 1))
edges.append((last_vertex_i + 1, last_vertex_i + 2))
else:
vertices.append(vertices[number_of_risers * 2] + depth_vector)
last_vertex_i = len(vertices) - 1
edges.append((number_of_risers * 2, last_vertex_i))
vertices.append(last_vert + depth_vector)
edges.append((last_vertex_i, last_vertex_i + 1))
top_nib_end = len(vertices) - 1
# bottom nib
if abs(b) <= base_slab_depth:
vertices.append(vertices[0] + depth_vector)
start_vert = vertices[0]
slab_overlaps_stair = max(abs(b) - base_slab_depth, 0)
if slab_overlaps_stair == 0:
# stair doesn't meet the slab
vertices.append(start_vert + depth_vector)
edges.append((0, len(vertices) - 1))
bottom_nib_end = len(vertices) - 1
else:
vertices.append(vertices[0] + Vector(((-base_slab_depth - b) / k, 0, -base_slab_depth)))
vertices.append(vertices[0] + Vector((0, 0, -base_slab_depth)))
# run distance by X until stair will meet the slab
run_slab_distance = slab_overlaps_stair / stair_tan
vertices.append(start_vert + Vector((run_slab_distance, -base_slab_depth)))
vertices.append(start_vert + Vector((0, -base_slab_depth)))
last_vertex_i = len(vertices) - 1
edges.append((0, last_vertex_i))
edges.append((last_vertex_i - 1, last_vertex_i))
bottom_nib_end = len(vertices) - 2
# close the shape
edges.append((bottom_nib_end, top_nib_end))
faces = [list(range(len(vertices)))]
else:
raise Exception(f"Unsupported stair type: {stair_type}")
return (vertices, edges, faces)
vertices = (v.to_3d().xzy for v in vertices)
return (vertices, edges, faces)
+3 -8
View File
@@ -158,7 +158,7 @@ class TestGenerateStair2DProfile(NewFile):
(10, 11),
(10, 9),
)
faces_data = ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),)
faces_data = ()
expected_profile = (verts_data, edges_data, faces_data)
generated_profile = subject.generate_stair_2d_profile(**kwargs)
self.compare_data(generated_profile, expected_profile)
@@ -207,7 +207,7 @@ class TestGenerateStair2DProfile(NewFile):
(11, 10),
)
faces_data = ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),)
faces_data = ()
expected_profile = (verts_data, edges_data, faces_data)
generated_profile = subject.generate_stair_2d_profile(**kwargs)
self.compare_data(generated_profile, expected_profile)
@@ -259,12 +259,7 @@ class TestGenerateStair2DProfile(NewFile):
(15, 12),
)
faces_data = (
(0,),
(4,),
(8,),
(12,),
)
faces_data = ()
expected_profile = (verts_data, edges_data, faces_data)
generated_profile = subject.generate_stair_2d_profile(**kwargs)