3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110- 1301, USA.
19 # ##### END GPL LICENSE BLOCK #####
23 # ----------------------------------------------------------
24 # Author: Stephen Leger (s-leger)
26 # ----------------------------------------------------------
27 # noinspection PyUnresolvedReferences
29 # noinspection PyUnresolvedReferences
30 from bpy
.types
import Operator
, PropertyGroup
, Mesh
, Panel
31 from bpy
.props
import (
32 FloatProperty
, BoolProperty
, IntProperty
, CollectionProperty
,
33 StringProperty
, EnumProperty
, FloatVectorProperty
35 from .bmesh_utils
import BmeshEdit
as bmed
36 from .panel
import Panel
as Lofter
37 from mathutils
import Vector
, Matrix
38 from math
import sin
, cos
, pi
, floor
, acos
39 from .archipack_manipulator
import Manipulable
, archipack_manipulator
40 from .archipack_2d
import Line
, Arc
41 from .archipack_preset
import ArchipackPreset
, PresetMenuOperator
42 from .archipack_object
import ArchipackCreateTool
, ArchipackObject
46 def __init__(self
, left_offset
, right_offset
, steps_type
, nose_type
, z_mode
, nose_z
, bottom_z
):
47 self
.steps_type
= steps_type
48 self
.nose_type
= nose_type
51 self
.next_type
= 'NONE'
52 self
.last_type
= 'NONE'
56 # size under the step on bottom
57 self
.bottom_z
= bottom_z
58 self
.left_offset
= left_offset
59 self
.right_offset
= right_offset
62 def set_matids(self
, matids
):
63 self
.idmat_top
, self
.idmat_step_front
, self
.idmat_raise
, \
64 self
.idmat_side
, self
.idmat_bottom
, self
.idmat_step_side
= matids
66 def set_height(self
, step_height
, z0
):
67 self
.step_height
= step_height
72 return self
.n_step
* self
.step_height
76 return self
.t_step
/ self
.step_depth
80 return self
.z0
+ self
.height
83 def left_length(self
):
84 return self
.get_length("LEFT")
87 def right_length(self
):
88 return self
.get_length("RIGHT")
90 def step_size(self
, step_depth
):
91 t_step
, n_step
= self
.steps(step_depth
)
94 self
.step_depth
= step_depth
97 def p3d_left(self
, verts
, p2d
, i
, t
, landing
=False):
99 nose_z
= min(self
.step_height
, self
.nose_z
)
100 zl
= self
.z0
+ t
* self
.height
101 zs
= self
.z0
+ i
* self
.step_height
102 if self
.z_mode
== 'LINEAR':
104 z1
= z0
- self
.bottom_z
105 verts
.extend([(x
, y
, z0
), (x
, y
, z1
)])
107 if "FULL" in self
.steps_type
:
110 z0
= max(0, zl
- nose_z
- self
.bottom_z
)
111 z3
= zs
+ max(0, self
.step_height
- nose_z
)
112 z4
= zs
+ self
.step_height
114 if "FULL" in self
.steps_type
:
118 z2
= max(0, min(z3
, z3
- self
.bottom_z
))
121 z1
= min(z3
, max(z0
, zl
- nose_z
))
122 z2
= min(z3
, max(z1
, zl
))
123 verts
.extend([(x
, y
, z0
),
129 def p3d_right(self
, verts
, p2d
, i
, t
, landing
=False):
131 nose_z
= min(self
.step_height
, self
.nose_z
)
132 zl
= self
.z0
+ t
* self
.height
133 zs
= self
.z0
+ i
* self
.step_height
134 if self
.z_mode
== 'LINEAR':
136 z1
= z0
- self
.bottom_z
137 verts
.extend([(x
, y
, z1
), (x
, y
, z0
)])
139 if "FULL" in self
.steps_type
:
142 z0
= max(0, zl
- nose_z
- self
.bottom_z
)
143 z3
= zs
+ max(0, self
.step_height
- nose_z
)
144 z4
= zs
+ self
.step_height
146 if "FULL" in self
.steps_type
:
150 z2
= max(0, min(z3
, z3
- self
.bottom_z
))
153 z1
= min(z3
, max(z0
, zl
- nose_z
))
154 z2
= min(z3
, max(z1
, zl
))
155 verts
.extend([(x
, y
, z4
),
161 def p3d_cstep_left(self
, verts
, p2d
, i
, t
):
163 nose_z
= min(self
.step_height
, self
.nose_z
)
164 zs
= self
.z0
+ i
* self
.step_height
165 z3
= zs
+ max(0, self
.step_height
- nose_z
)
166 z1
= min(z3
, zs
- nose_z
)
167 verts
.append((x
, y
, z1
))
168 verts
.append((x
, y
, z3
))
170 def p3d_cstep_right(self
, verts
, p2d
, i
, t
):
172 nose_z
= min(self
.step_height
, self
.nose_z
)
173 zs
= self
.z0
+ i
* self
.step_height
174 z3
= zs
+ max(0, self
.step_height
- nose_z
)
175 z1
= min(z3
, zs
- nose_z
)
176 verts
.append((x
, y
, z3
))
177 verts
.append((x
, y
, z1
))
179 def straight_stair(self
, length
):
180 self
.next_type
= 'STAIR'
181 s
= self
.straight(length
)
182 return StraightStair(s
.p
, s
.v
, self
.left_offset
, self
.right_offset
, self
.steps_type
,
183 self
.nose_type
, self
.z_mode
, self
.nose_z
, self
.bottom_z
)
185 def straight_landing(self
, length
, last_type
='STAIR'):
186 self
.next_type
= 'LANDING'
187 s
= self
.straight(length
)
188 return StraightLanding(s
.p
, s
.v
, self
.left_offset
, self
.right_offset
, self
.steps_type
,
189 self
.nose_type
, self
.z_mode
, self
.nose_z
, self
.bottom_z
, last_type
=last_type
)
191 def curved_stair(self
, da
, radius
, left_shape
, right_shape
, double_limit
=pi
):
192 self
.next_type
= 'STAIR'
194 n
.v
= radius
* n
.v
.normalized()
199 return CurvedStair(c
, radius
, a0
, da
, self
.left_offset
, self
.right_offset
,
200 self
.steps_type
, self
.nose_type
, self
.z_mode
, self
.nose_z
, self
.bottom_z
,
201 left_shape
, right_shape
, double_limit
=double_limit
)
203 def curved_landing(self
, da
, radius
, left_shape
, right_shape
, double_limit
=pi
, last_type
='STAIR'):
204 self
.next_type
= 'LANDING'
206 n
.v
= radius
* n
.v
.normalized()
211 return CurvedLanding(c
, radius
, a0
, da
, self
.left_offset
, self
.right_offset
,
212 self
.steps_type
, self
.nose_type
, self
.z_mode
, self
.nose_z
, self
.bottom_z
,
213 left_shape
, right_shape
, double_limit
=double_limit
, last_type
=last_type
)
215 def get_z(self
, t
, mode
):
217 return self
.z0
+ t
* self
.height
219 step
= 1 + floor(t
/ self
.t_step
)
220 return self
.z0
+ step
* self
.step_height
222 def make_profile(self
, t
, side
, profile
, verts
, faces
, matids
, next
=None, tnext
=0):
223 z0
= self
.get_z(t
, 'LINEAR')
225 t
, part
, dz0
, shape
= self
.get_part(t
, side
)
227 tnext
, next
, dz1
, shape1
= next
.get_part(tnext
, side
)
228 xy
, s
= part
.proj_xy(t
, next
)
229 v_xy
= s
* xy
.to_3d()
230 z
, s
= part
.proj_z(t
, dz0
, next
, dz1
)
231 v_z
= s
* Vector((-xy
.y
* z
.x
, xy
.x
* z
.x
, z
.y
))
233 verts
+= [Vector((x
, y
, z0
)) + v
.x
* v_xy
+ v
.y
* v_z
for v
in profile
]
235 def project_uv(self
, rM
, uvs
, verts
, indexes
, up_axis
='Z'):
237 uvs
.append([(rM
@ Vector(verts
[i
])).to_2d() for i
in indexes
])
239 uvs
.append([(x
, z
) for x
, y
, z
in [(rM
@ Vector(verts
[i
])) for i
in indexes
]])
241 uvs
.append([(y
, z
) for x
, y
, z
in [(rM
@ Vector(verts
[i
])) for i
in indexes
]])
243 def get_proj_matrix(self
, part
, t
, nose_y
):
244 # a matrix to project verts
245 # into uv space for horizontal parts of this step
246 # so uv = (rM @ vertex).to_2d()
247 tl
= t
- nose_y
/ self
.get_length("LEFT")
248 tr
= t
- nose_y
/ self
.get_length("RIGHT")
249 t2
, part
, dz
, shape
= self
.get_part(tl
, "LEFT")
251 t2
, part
, dz
, shape
= self
.get_part(tr
, "RIGHT")
253 v
= (p1
- p0
).normalized()
255 [-v
.y
, v
.x
, 0, p0
.x
],
261 def _make_nose(self
, i
, s
, verts
, faces
, matids
, uvs
, nose_y
):
265 # a matrix to project verts
266 # into uv space for horizontal parts of this step
267 # so uv = (rM @ vertex).to_2d()
268 rM
= self
.get_proj_matrix(self
, t
, nose_y
)
270 if self
.z_mode
== 'LINEAR':
275 tl
= t
- nose_y
/ self
.get_length("LEFT")
276 tr
= t
- nose_y
/ self
.get_length("RIGHT")
278 t2
, part
, dz
, shape
= self
.get_part(tl
, "LEFT")
280 self
.p3d_left(verts
, p0
, s
, t2
)
282 t2
, part
, dz
, shape
= self
.get_part(tr
, "RIGHT")
284 self
.p3d_right(verts
, p1
, s
, t2
)
291 matids
.extend([self
.idmat_step_side
,
293 self
.idmat_step_side
])
295 faces
+= [(f
+ j
, f
+ j
+ 1, f
+ j
+ offset
+ 1, f
+ j
+ offset
) for j
in range(start
, end
)]
299 w
= verts
[f
+ 2][2] - verts
[f
+ 3][2]
300 s
= int((end
- start
) / 2)
302 uvs
+= [[(u
, verts
[f
+ j
][2]), (u
, verts
[f
+ j
+ 1][2]),
303 (0, verts
[f
+ j
+ 1][2]), (0, verts
[f
+ j
][2])] for j
in range(start
, start
+ s
)]
305 uvs
.append([(0, 0), (0, v
), (u
, v
), (u
, 0)])
307 uvs
+= [[(u
, verts
[f
+ j
][2]), (u
, verts
[f
+ j
+ 1][2]),
308 (0, verts
[f
+ j
+ 1][2]), (0, verts
[f
+ j
][2])] for j
in range(start
+ s
+ 1, end
)]
310 if 'STRAIGHT' in self
.nose_type
or 'OPEN' in self
.steps_type
:
312 matids
.append(self
.idmat_bottom
)
313 faces
.append((f
+ end
, f
+ start
, f
+ offset
+ start
, f
+ offset
+ end
))
314 uvs
.append([(u
, v
), (u
, 0), (0, 0), (0, v
)])
316 if self
.steps_type
!= 'OPEN':
317 if 'STRAIGHT' in self
.nose_type
:
318 # front face bottom straight
319 matids
.append(self
.idmat_raise
)
320 faces
.append((f
+ 12, f
+ 17, f
+ 16, f
+ 13))
321 uvs
.append([(0, w
), (v
, w
), (v
, 0), (0, 0)])
323 elif 'OBLIQUE' in self
.nose_type
:
324 # front face bottom oblique
325 matids
.append(self
.idmat_raise
)
326 faces
.append((f
+ 12, f
+ 17, f
+ 6, f
+ 3))
328 uvs
.append([(0, w
), (v
, w
), (v
, 0), (0, 0)])
330 matids
.append(self
.idmat_side
)
331 faces
.append((f
+ 3, f
+ 13, f
+ 12))
332 uvs
.append([(0, 0), (u
, 0), (u
, w
)])
334 matids
.append(self
.idmat_side
)
335 faces
.append((f
+ 6, f
+ 17, f
+ 16))
336 uvs
.append([(0, 0), (u
, w
), (u
, 0)])
339 w
= verts
[f
+ 3][2] - verts
[f
+ 4][2]
340 matids
.append(self
.idmat_step_front
)
341 faces
.append((f
+ 4, f
+ 3, f
+ 6, f
+ 5))
342 uvs
.append([(0, 0), (0, w
), (v
, w
), (v
, 0)])
345 def make_faces(self
, f
, rM
, verts
, faces
, matids
, uvs
):
347 if self
.z_mode
== 'LINEAR':
351 matids
.extend([self
.idmat_side
,
355 elif "OPEN" in self
.steps_type
:
356 # faces dessus-dessous-lateral marches fermees
360 matids
.extend([self
.idmat_step_side
,
362 self
.idmat_step_side
,
365 # faces dessus-dessous-lateral marches fermees
369 matids
.extend([self
.idmat_side
,
372 self
.idmat_step_side
,
374 self
.idmat_step_side
,
381 u_l1
= self
.t_step
* self
.left_length
383 u_r1
= self
.t_step
* self
.right_length
385 s
= int((end
- start
) / 2)
386 uvs
+= [[(u_l0
, verts
[f
+ j
][2]), (u_l0
, verts
[f
+ j
+ 1][2]),
387 (u_l1
, verts
[f
+ j
+ offset
+ 1][2]), (u_l1
, verts
[f
+ j
+ offset
][2])] for j
in range(start
, start
+ s
)]
389 self
.project_uv(rM
, uvs
, verts
, [f
+ start
+ s
, f
+ start
+ s
+ 1,
390 f
+ start
+ s
+ offset
+ 1, f
+ start
+ s
+ offset
])
392 uvs
+= [[(u_r0
, verts
[f
+ j
][2]), (u_r0
, verts
[f
+ j
+ 1][2]),
393 (u_r1
, verts
[f
+ j
+ offset
+ 1][2]), (u_r1
, verts
[f
+ j
+ offset
][2])] for j
in range(start
+ s
+ 1, end
)]
395 self
.project_uv(rM
, uvs
, verts
, [f
+ end
, f
+ start
, f
+ offset
+ start
, f
+ offset
+ end
])
397 faces
+= [(f
+ j
, f
+ j
+ 1, f
+ j
+ offset
+ 1, f
+ j
+ offset
) for j
in range(start
, end
)]
398 faces
.append((f
+ end
, f
+ start
, f
+ offset
+ start
, f
+ offset
+ end
))
401 class StraightStair(Stair
, Line
):
402 def __init__(self
, p
, v
, left_offset
, right_offset
, steps_type
, nose_type
, z_mode
, nose_z
, bottom_z
):
403 Stair
.__init
__(self
, left_offset
, right_offset
, steps_type
, nose_type
, z_mode
, nose_z
, bottom_z
)
404 Line
.__init
__(self
, p
, v
)
405 self
.l_line
= self
.offset(-left_offset
)
406 self
.r_line
= self
.offset(right_offset
)
408 def make_step(self
, i
, verts
, faces
, matids
, uvs
, nose_y
=0):
410 rM
= self
._make
_nose
(i
, i
, verts
, faces
, matids
, uvs
, nose_y
)
416 p
= self
.l_line
.lerp(t0
)
417 self
.p3d_left(verts
, p
, i
, t0
)
418 p
= self
.r_line
.lerp(t0
)
419 self
.p3d_right(verts
, p
, i
, t0
)
421 t1
= t0
+ self
.t_step
423 p
= self
.l_line
.lerp(t1
)
424 self
.p3d_left(verts
, p
, i
, t1
)
425 p
= self
.r_line
.lerp(t1
)
426 self
.p3d_right(verts
, p
, i
, t1
)
428 self
.make_faces(f
, rM
, verts
, faces
, matids
, uvs
)
430 if "OPEN" in self
.steps_type
:
431 faces
.append((f
+ 13, f
+ 14, f
+ 15, f
+ 16))
432 matids
.append(self
.idmat_step_front
)
433 uvs
.append([(0, 0), (0, 1), (1, 1), (1, 0)])
435 def get_length(self
, side
):
438 def get_lerp_vect(self
, posts
, side
, i
, t_step
, respect_edges
, z_offset
=0, t0_abs
=None):
439 if t0_abs
is not None:
443 t
, part
, dz
, shape
= self
.get_part(t0
, side
)
446 z0
= self
.get_z(t0
, 'STEP')
447 z1
= self
.get_z(t0
, 'LINEAR')
448 posts
.append((n
, dz
, z0
, z1
+ t0
* z_offset
))
451 def n_posts(self
, post_spacing
, side
, respect_edges
):
452 return self
.steps(post_spacing
)
454 def get_part(self
, t
, side
):
459 return t
, part
, self
.height
, 'LINE'
462 class CurvedStair(Stair
, Arc
):
463 def __init__(self
, c
, radius
, a0
, da
, left_offset
, right_offset
, steps_type
, nose_type
,
464 z_mode
, nose_z
, bottom_z
, left_shape
, right_shape
, double_limit
=pi
):
466 Stair
.__init
__(self
, left_offset
, right_offset
, steps_type
, nose_type
, z_mode
, nose_z
, bottom_z
)
467 Arc
.__init
__(self
, c
, radius
, a0
, da
)
468 self
.l_shape
= left_shape
469 self
.r_shape
= right_shape
470 self
.edges_multiples
= round(abs(da
), 6) > double_limit
471 # left arc, tangent at start and end
472 self
.l_arc
, self
.l_t0
, self
.l_t1
, self
.l_tc
= self
.set_offset(-left_offset
, left_shape
)
473 self
.r_arc
, self
.r_t0
, self
.r_t1
, self
.r_tc
= self
.set_offset(right_offset
, right_shape
)
475 def set_offset(self
, offset
, shape
):
476 arc
= self
.offset(offset
)
477 t0
= arc
.tangeant(0, 1)
478 t1
= arc
.tangeant(1, 1)
479 tc
= arc
.tangeant(0.5, 1)
480 if self
.edges_multiples
:
481 i
, p
, t
= t0
.intersect(tc
)
484 i
, p
, t2
= tc
.intersect(t1
)
486 i
, p
, t
= t0
.intersect(t1
)
490 return arc
, t0
, t1
, tc
492 def get_length(self
, side
):
501 if shape
== 'CIRCLE':
504 if self
.edges_multiples
:
510 def _make_step(self
, t_step
, i
, s
, verts
, landing
=False):
516 t
, part
, dz
, shape
= self
.get_part(tb
, "LEFT")
518 self
.p3d_left(verts
, p
, s
, tb
, landing
)
520 t
, part
, dz
, shape
= self
.get_part(tb
, "RIGHT")
522 self
.p3d_right(verts
, p
, s
, tb
, landing
)
525 def _make_edge(self
, t_step
, i
, j
, f
, rM
, verts
, faces
, matids
, uvs
):
527 # make edges verts after regular ones
528 if self
.l_shape
!= 'CIRCLE' or self
.r_shape
!= 'CIRCLE':
529 if self
.edges_multiples
:
531 if tb
< 0.25 and tb
+ t_step
> 0.25:
534 if self
.l_shape
== 'CIRCLE':
535 self
.p3d_left(verts
, self
.l_arc
.lerp(0.25), j
, 0.25)
537 self
.p3d_left(verts
, self
.l_tc
.p
, j
, 0.25)
538 if self
.r_shape
== 'CIRCLE':
539 self
.p3d_right(verts
, self
.r_arc
.lerp(0.25), j
, 0.25)
541 self
.p3d_right(verts
, self
.r_tc
.p
, j
, 0.25)
542 self
.make_faces(f0
, rM
, verts
, faces
, matids
, uvs
)
544 if tb
< 0.75 and tb
+ t_step
> 0.75:
547 if self
.l_shape
== 'CIRCLE':
548 self
.p3d_left(verts
, self
.l_arc
.lerp(0.75), j
, 0.75)
550 self
.p3d_left(verts
, self
.l_t1
.p
, j
, 0.75)
551 if self
.r_shape
== 'CIRCLE':
552 self
.p3d_right(verts
, self
.r_arc
.lerp(0.75), j
, 0.75)
554 self
.p3d_right(verts
, self
.r_t1
.p
, j
, 0.75)
555 self
.make_faces(f0
, rM
, verts
, faces
, matids
, uvs
)
557 if tb
< 0.5 and tb
+ t_step
> 0.5:
560 # the step goes through the edge
561 if self
.l_shape
== 'CIRCLE':
562 self
.p3d_left(verts
, self
.l_arc
.lerp(0.5), j
, 0.5)
564 self
.p3d_left(verts
, self
.l_t1
.p
, j
, 0.5)
565 if self
.r_shape
== 'CIRCLE':
566 self
.p3d_right(verts
, self
.r_arc
.lerp(0.5), j
, 0.5)
568 self
.p3d_right(verts
, self
.r_t1
.p
, j
, 0.5)
569 self
.make_faces(f0
, rM
, verts
, faces
, matids
, uvs
)
572 def make_step(self
, i
, verts
, faces
, matids
, uvs
, nose_y
=0):
574 # open stair with closed face
577 rM
= self
._make
_nose
(i
, i
, verts
, faces
, matids
, uvs
, nose_y
)
579 if self
.l_shape
== 'CIRCLE' or self
.r_shape
== 'CIRCLE':
581 n_subs
= max(1, int(abs(self
.da
) / pi
* 30 / self
.n_step
))
582 t_step
= self
.t_step
/ n_subs
583 for j
in range(n_subs
):
585 f
= self
._make
_step
(t_step
, n_subs
* i
+ j
, i
, verts
)
587 self
.make_faces(f0
, rM
, verts
, faces
, matids
, uvs
)
588 f
= self
._make
_edge
(t_step
, n_subs
* i
+ j
, i
, f
, rM
, verts
, faces
, matids
, uvs
)
590 f
= self
._make
_step
(self
.t_step
, i
, i
, verts
)
591 f
= self
._make
_edge
(self
.t_step
, i
, i
, f
, rM
, verts
, faces
, matids
, uvs
)
593 self
._make
_step
(self
.t_step
, i
+ 1, i
, verts
)
594 self
.make_faces(f
, rM
, verts
, faces
, matids
, uvs
)
596 if "OPEN" in self
.steps_type
and self
.z_mode
!= 'LINEAR':
598 faces
.append((f
+ 13, f
+ 14, f
+ 15, f
+ 16))
599 matids
.append(self
.idmat_step_front
)
600 uvs
.append([(0, 0), (0, 1), (1, 1), (1, 0)])
602 def get_part(self
, t
, side
):
606 t0
, t1
, tc
= self
.r_t0
, self
.r_t1
, self
.r_tc
610 t0
, t1
, tc
= self
.l_t0
, self
.l_t1
, self
.l_tc
611 if shape
== 'CIRCLE':
612 return t
, arc
, self
.height
, shape
614 if self
.edges_multiples
:
617 return 4 * t
, t0
, 0.25 * self
.height
, shape
619 return 2 * (t
- 0.25), tc
, 0.5 * self
.height
, shape
621 return 4 * (t
- 0.75), t1
, 0.25 * self
.height
, shape
624 return 2 * t
, t0
, 0.5 * self
.height
, shape
626 return 2 * (t
- 0.5), t1
, 0.5 * self
.height
, shape
628 def get_lerp_vect(self
, posts
, side
, i
, t_step
, respect_edges
, z_offset
=0, t0_abs
=None):
629 if t0_abs
is not None:
635 zs
= self
.get_z(t0
, 'STEP')
636 zl
= self
.get_z(t0
, 'LINEAR')
639 t
, part
, dz
, shape
= self
.get_part(t0
, side
)
642 posts
.append((n
, dz
, zs
, zl
+ t0
* z_offset
))
644 if shape
!= 'CIRCLE' and respect_edges
:
645 if self
.edges_multiples
:
646 if t0
< 0.25 and t1
> 0.25:
647 zs
= self
.get_z(0.25, 'STEP')
648 zl
= self
.get_z(0.25, 'LINEAR')
649 t
, part
, dz
, shape
= self
.get_part(0.25, side
)
651 posts
.append((n
, dz
, zs
, zl
+ 0.25 * z_offset
))
653 if t0
< 0.75 and t1
> 0.75:
654 zs
= self
.get_z(0.75, 'STEP')
655 zl
= self
.get_z(0.75, 'LINEAR')
656 t
, part
, dz
, shape
= self
.get_part(0.75, side
)
658 posts
.append((n
, dz
, zs
, zl
+ 0.75 * z_offset
))
660 elif t0
< 0.5 and t1
> 0.5:
661 zs
= self
.get_z(0.5, 'STEP')
662 zl
= self
.get_z(0.5, 'LINEAR')
663 t
, part
, dz
, shape
= self
.get_part(0.5, side
)
665 posts
.append((n
, dz
, zs
, zl
+ 0.5 * z_offset
))
669 def n_posts(self
, post_spacing
, side
, respect_edges
):
671 arc
, t0
, shape
= self
.l_arc
, self
.l_t0
, self
.l_shape
673 arc
, t0
, shape
= self
.r_arc
, self
.r_t0
, self
.r_shape
675 if shape
== 'CIRCLE':
678 if self
.edges_multiples
:
681 length
= 4 * t0
.length
683 length
= 2 * t0
.length
684 steps
= step_factor
* max(1, round(length
/ post_spacing
, 0))
685 # print("respect_edges:%s t_step:%s n_step:%s" % (respect_edges, 1.0 / steps, int(steps)))
686 return 1.0 / steps
, int(steps
)
689 class StraightLanding(StraightStair
):
690 def __init__(self
, p
, v
, left_offset
, right_offset
, steps_type
,
691 nose_type
, z_mode
, nose_z
, bottom_z
, last_type
='STAIR'):
693 StraightStair
.__init
__(self
, p
, v
, left_offset
, right_offset
, steps_type
,
694 nose_type
, z_mode
, nose_z
, bottom_z
)
696 self
.last_type
= last_type
703 def top_offset(self
):
704 return self
.t_step
/ self
.v
.length
708 if self
.next_type
== 'LANDING':
711 return self
.z0
+ self
.step_height
713 def step_size(self
, step_depth
):
716 self
.step_depth
= step_depth
717 if self
.last_type
== 'LANDING':
722 def make_step(self
, i
, verts
, faces
, matids
, uvs
, nose_y
=0):
724 if i
== 0 and self
.last_type
!= 'LANDING':
725 rM
= self
._make
_nose
(i
, 0, verts
, faces
, matids
, uvs
, nose_y
)
727 rM
= self
.get_proj_matrix(self
.l_line
, self
.t_step
* i
, nose_y
)
733 p
= self
.l_line
.lerp(t0
)
734 self
.p3d_left(verts
, p
, j
, t0
)
736 p
= self
.r_line
.lerp(t0
)
737 self
.p3d_right(verts
, p
, j
, t0
)
739 t1
= t0
+ self
.t_step
740 p
= self
.l_line
.lerp(t1
)
741 self
.p3d_left(verts
, p
, j
, t1
, self
.next_type
!= 'LANDING')
743 p
= self
.r_line
.lerp(t1
)
744 self
.p3d_right(verts
, p
, j
, t1
, self
.next_type
!= 'LANDING')
746 self
.make_faces(f
, rM
, verts
, faces
, matids
, uvs
)
748 if "OPEN" in self
.steps_type
and self
.next_type
!= 'LANDING':
749 faces
.append((f
+ 13, f
+ 14, f
+ 15, f
+ 16))
750 matids
.append(self
.idmat_step_front
)
751 uvs
.append([(0, 0), (0, 1), (1, 1), (1, 0)])
753 def straight_landing(self
, length
):
754 return Stair
.straight_landing(self
, length
, last_type
='LANDING')
756 def curved_landing(self
, da
, radius
, left_shape
, right_shape
, double_limit
=pi
):
757 return Stair
.curved_landing(self
, da
, radius
, left_shape
,
758 right_shape
, double_limit
=double_limit
, last_type
='LANDING')
760 def get_z(self
, t
, mode
):
762 return self
.z0
+ self
.step_height
767 class CurvedLanding(CurvedStair
):
768 def __init__(self
, c
, radius
, a0
, da
, left_offset
, right_offset
, steps_type
,
769 nose_type
, z_mode
, nose_z
, bottom_z
, left_shape
, right_shape
, double_limit
=pi
, last_type
='STAIR'):
771 CurvedStair
.__init
__(self
, c
, radius
, a0
, da
, left_offset
, right_offset
, steps_type
,
772 nose_type
, z_mode
, nose_z
, bottom_z
, left_shape
, right_shape
, double_limit
=double_limit
)
774 self
.last_type
= last_type
777 def top_offset(self
):
778 if self
.l_shape
== 'CIRCLE' or self
.r_shape
== 'CIRCLE':
779 return self
.t_step
/ self
.step_depth
781 if self
.edges_multiples
:
782 return 0.5 / self
.length
784 return 1 / self
.length
792 if self
.next_type
== 'LANDING':
795 return self
.z0
+ self
.step_height
797 def step_size(self
, step_depth
):
798 if self
.l_shape
== 'CIRCLE' or self
.r_shape
== 'CIRCLE':
799 t_step
, n_step
= self
.steps(step_depth
)
801 if self
.edges_multiples
:
802 t_step
, n_step
= 0.5, 2
804 t_step
, n_step
= 1, 1
807 self
.step_depth
= step_depth
808 if self
.last_type
== 'LANDING':
813 def make_step(self
, i
, verts
, faces
, matids
, uvs
, nose_y
=0):
815 if i
== 0 and 'LANDING' not in self
.last_type
:
816 rM
= self
._make
_nose
(i
, 0, verts
, faces
, matids
, uvs
, nose_y
)
818 rM
= self
.get_proj_matrix(self
.l_arc
, self
.t_step
* i
, nose_y
)
822 if self
.l_shape
== 'CIRCLE' or self
.r_shape
== 'CIRCLE':
823 n_subs
= max(1, int(abs(self
.da
/ pi
* 30 / self
.n_step
)))
824 t_step
= self
.t_step
/ n_subs
825 for j
in range(n_subs
):
827 f
= self
._make
_step
(t_step
, n_subs
* i
+ j
, 0, verts
)
829 self
.make_faces(f0
, rM
, verts
, faces
, matids
, uvs
)
830 f
= self
._make
_edge
(t_step
, n_subs
* i
+ j
, 0, f
, rM
, verts
, faces
, matids
, uvs
)
832 f
= self
._make
_step
(self
.t_step
, i
, 0, verts
)
833 f
= self
._make
_edge
(self
.t_step
, i
, 0, f
, rM
, verts
, faces
, matids
, uvs
)
835 self
._make
_step
(self
.t_step
, i
+ 1, 0, verts
, i
== self
.n_step
- 1 and 'LANDING' not in self
.next_type
)
836 self
.make_faces(f
, rM
, verts
, faces
, matids
, uvs
)
838 if "OPEN" in self
.steps_type
and 'LANDING' not in self
.next_type
:
839 faces
.append((f
+ 13, f
+ 14, f
+ 15, f
+ 16))
840 matids
.append(self
.idmat_step_front
)
841 uvs
.append([(0, 0), (0, 1), (1, 1), (1, 0)])
843 def straight_landing(self
, length
):
844 return Stair
.straight_landing(self
, length
, last_type
='LANDING')
846 def curved_landing(self
, da
, radius
, left_shape
, right_shape
, double_limit
=pi
):
847 return Stair
.curved_landing(self
, da
, radius
, left_shape
,
848 right_shape
, double_limit
=double_limit
, last_type
='LANDING')
850 def get_z(self
, t
, mode
):
852 return self
.z0
+ self
.step_height
857 class StairGenerator():
858 def __init__(self
, parts
):
860 self
.last_type
= 'NONE'
862 self
.steps_type
= 'NONE'
864 self
.user_defined_post
= None
865 self
.user_defined_uvs
= None
866 self
.user_defined_mat
= None
868 def add_part(self
, type, steps_type
, nose_type
, z_mode
, nose_z
, bottom_z
, center
,
869 radius
, da
, width_left
, width_right
, length
, left_shape
, right_shape
):
871 self
.steps_type
= steps_type
872 if len(self
.stairs
) < 1:
882 if type == 'S_STAIR':
884 v
= Vector((0, length
))
885 s
= StraightStair(p
, v
, width_left
, width_right
, steps_type
, nose_type
, z_mode
, nose_z
, bottom_z
)
886 elif type == 'C_STAIR':
888 c
= Vector((radius
, 0))
890 c
= Vector((-radius
, 0))
891 s
= CurvedStair(c
, radius
, 0, da
, width_left
, width_right
, steps_type
,
892 nose_type
, z_mode
, nose_z
, bottom_z
, left_shape
, right_shape
)
893 elif type == 'D_STAIR':
895 c
= Vector((radius
, 0))
897 c
= Vector((-radius
, 0))
898 s
= CurvedStair(c
, radius
, 0, da
, width_left
, width_right
, steps_type
,
899 nose_type
, z_mode
, nose_z
, bottom_z
, left_shape
, right_shape
, double_limit
=0)
900 elif type == 'S_LANDING':
902 v
= Vector((0, length
))
903 s
= StraightLanding(p
, v
, width_left
, width_right
, steps_type
, nose_type
, z_mode
, nose_z
, bottom_z
)
904 elif type == 'C_LANDING':
906 c
= Vector((radius
, 0))
908 c
= Vector((-radius
, 0))
909 s
= CurvedLanding(c
, radius
, 0, da
, width_left
, width_right
, steps_type
,
910 nose_type
, z_mode
, nose_z
, bottom_z
, left_shape
, right_shape
)
911 elif type == 'D_LANDING':
913 c
= Vector((radius
, 0))
915 c
= Vector((-radius
, 0))
916 s
= CurvedLanding(c
, radius
, 0, da
, width_left
, width_right
, steps_type
,
917 nose_type
, z_mode
, nose_z
, bottom_z
, left_shape
, right_shape
, double_limit
=0)
919 if type == 'S_STAIR':
920 s
= s
.straight_stair(length
)
921 elif type == 'C_STAIR':
922 s
= s
.curved_stair(da
, radius
, left_shape
, right_shape
)
923 elif type == 'D_STAIR':
924 s
= s
.curved_stair(da
, radius
, left_shape
, right_shape
, double_limit
=0)
925 elif type == 'S_LANDING':
926 s
= s
.straight_landing(length
)
927 elif type == 'C_LANDING':
928 s
= s
.curved_landing(da
, radius
, left_shape
, right_shape
)
929 elif type == 'D_LANDING':
930 s
= s
.curved_landing(da
, radius
, left_shape
, right_shape
, double_limit
=0)
931 self
.stairs
.append(s
)
932 self
.last_type
= type
934 def n_steps(self
, step_depth
):
936 for stair
in self
.stairs
:
937 n_steps
+= stair
.step_size(step_depth
)
940 def set_height(self
, step_height
):
942 for stair
in self
.stairs
:
943 stair
.set_height(step_height
, z
)
946 def make_stair(self
, height
, step_depth
, verts
, faces
, matids
, uvs
, nose_y
=0):
947 n_steps
= self
.n_steps(step_depth
)
948 self
.set_height(height
/ n_steps
)
950 for s
, stair
in enumerate(self
.stairs
):
951 if s
< len(self
.parts
):
952 manipulator
= self
.parts
[s
].manipulators
[0]
953 # Store Gl Points for manipulators
954 if 'Curved' in type(stair
).__name
__:
956 p0
= (stair
.p0
- c
).to_3d()
957 p1
= (stair
.p1
- c
).to_3d()
958 manipulator
.set_pts([(c
.x
, c
.y
, stair
.top
), p0
, p1
])
959 manipulator
.type_key
= 'ARC_ANGLE_RADIUS'
960 manipulator
.prop1_name
= 'da'
961 manipulator
.prop2_name
= 'radius'
969 manipulator
.set_pts([(v0
.x
, v0
.y
, stair
.top
), (v1
.x
, v1
.y
, stair
.top
), (side
, 0, 0)])
970 manipulator
.type_key
= 'SIZE'
971 manipulator
.prop1_name
= 'length'
973 for i
in range(stair
.n_step
):
974 stair
.make_step(i
, verts
, faces
, matids
, uvs
, nose_y
=nose_y
)
975 if s
< len(self
.stairs
) - 1 and self
.steps_type
!= 'OPEN' and \
976 'Landing' in type(stair
).__name
__ and stair
.next_type
!= "LANDING":
978 faces
.append((f
, f
+ 1, f
+ 8, f
+ 9))
979 matids
.append(self
.stairs
[-1].idmat_bottom
)
980 u
= verts
[f
+ 1][2] - verts
[f
][2]
981 v
= (Vector(verts
[f
]) - Vector(verts
[f
+ 9])).length
982 uvs
.append([(0, 0), (0, u
), (v
, u
), (v
, 0)])
984 if self
.steps_type
!= 'OPEN' and len(self
.stairs
) > 0:
986 faces
.append((f
, f
+ 1, f
+ 2, f
+ 3, f
+ 4, f
+ 5, f
+ 6, f
+ 7, f
+ 8, f
+ 9))
987 matids
.append(self
.stairs
[-1].idmat_bottom
)
988 uvs
.append([(0, 0), (.1, 0), (.2, 0), (.3, 0), (.4, 0), (.4, 1), (.3, 1), (.2, 1), (.1, 1), (0, 1)])
990 def setup_user_defined_post(self
, o
, post_x
, post_y
, post_z
):
991 self
.user_defined_post
= o
992 x
= o
.bound_box
[6][0] - o
.bound_box
[0][0]
993 y
= o
.bound_box
[6][1] - o
.bound_box
[0][1]
994 z
= o
.bound_box
[6][2] - o
.bound_box
[0][2]
995 self
.user_defined_post_scale
= Vector((post_x
/ x
, post_y
/ -y
, post_z
/ z
))
997 # create vertex group lookup dictionary for names
998 vgroup_names
= {vgroup
.index
: vgroup
.name
for vgroup
in o
.vertex_groups
}
999 # create dictionary of vertex group assignments per vertex
1000 self
.vertex_groups
= [[vgroup_names
[g
.group
] for g
in v
.groups
] for v
in m
.vertices
]
1002 uv_act
= m
.uv_layers
.active
1003 if uv_act
is not None:
1004 uv_layer
= uv_act
.data
1005 self
.user_defined_uvs
= [[uv_layer
[li
].uv
for li
in p
.loop_indices
] for p
in m
.polygons
]
1007 self
.user_defined_uvs
= [[(0, 0) for i
in p
.vertices
] for p
in m
.polygons
]
1009 self
.user_defined_mat
= [p
.material_index
for p
in m
.polygons
]
1011 def get_user_defined_post(self
, tM
, z0
, z1
, z2
, slope
, post_z
, verts
, faces
, matids
, uvs
):
1013 m
= self
.user_defined_post
.data
1014 for i
, g
in enumerate(self
.vertex_groups
):
1015 co
= m
.vertices
[i
].co
.copy()
1016 co
.x
*= self
.user_defined_post_scale
.x
1017 co
.y
*= self
.user_defined_post_scale
.y
1018 co
.z
*= self
.user_defined_post_scale
.z
1026 co
.z
+= co
.y
* slope
1027 verts
.append(tM
@ co
)
1028 matids
+= self
.user_defined_mat
1029 faces
+= [tuple([i
+ f
for i
in p
.vertices
]) for p
in m
.polygons
]
1030 uvs
+= self
.user_defined_uvs
1032 def get_post(self
, post
, post_x
, post_y
, post_z
, post_alt
, sub_offset_x
,
1033 id_mat
, verts
, faces
, matids
, uvs
, bottom
="STEP"):
1035 n
, dz
, zs
, zl
= post
1038 if self
.user_defined_post
is not None:
1039 if bottom
== "STEP":
1045 x
, y
= -n
.v
.normalized()
1049 [0, 0, 1, z0
+ post_alt
],
1052 self
.get_user_defined_post(tM
, z0
, z1
, z2
, dz
, post_z
, verts
, faces
, matids
, uvs
)
1055 z3
= zl
+ post_z
+ post_alt
- slope
1056 z4
= zl
+ post_z
+ post_alt
+ slope
1057 if bottom
== "STEP":
1061 z0
= zl
+ post_alt
- slope
1062 z1
= zl
+ post_alt
+ slope
1063 vn
= n
.v
.normalized()
1065 dy
= post_y
* Vector((vn
.y
, -vn
.x
))
1066 oy
= sub_offset_x
* vn
1067 x0
, y0
= n
.p
- dx
+ dy
+ oy
1068 x1
, y1
= n
.p
- dx
- dy
+ oy
1069 x2
, y2
= n
.p
+ dx
- dy
+ oy
1070 x3
, y3
= n
.p
+ dx
+ dy
+ oy
1072 verts
.extend([(x0
, y0
, z0
), (x0
, y0
, z3
),
1073 (x1
, y1
, z1
), (x1
, y1
, z4
),
1074 (x2
, y2
, z1
), (x2
, y2
, z4
),
1075 (x3
, y3
, z0
), (x3
, y3
, z3
)])
1076 faces
.extend([(f
, f
+ 1, f
+ 3, f
+ 2),
1077 (f
+ 2, f
+ 3, f
+ 5, f
+ 4),
1078 (f
+ 4, f
+ 5, f
+ 7, f
+ 6),
1079 (f
+ 6, f
+ 7, f
+ 1, f
),
1080 (f
, f
+ 2, f
+ 4, f
+ 6),
1081 (f
+ 7, f
+ 5, f
+ 3, f
+ 1)])
1082 matids
.extend([id_mat
, id_mat
, id_mat
, id_mat
, id_mat
, id_mat
])
1083 x
= [(0, 0), (0, post_z
), (post_x
, post_z
), (post_x
, 0)]
1084 y
= [(0, 0), (0, post_z
), (post_y
, post_z
), (post_y
, 0)]
1085 z
= [(0, 0), (post_x
, 0), (post_x
, post_y
), (0, post_y
)]
1086 uvs
.extend([x
, y
, x
, y
, z
, z
])
1088 def get_panel(self
, subs
, altitude
, panel_x
, panel_z
, sub_offset_x
, idmat
, verts
, faces
, matids
, uvs
):
1093 x0
= sub_offset_x
- 0.5 * panel_x
1094 x1
= sub_offset_x
+ 0.5 * panel_x
1097 profile
= [Vector((x0
, z0
)), Vector((x1
, z0
)), Vector((x1
, z1
)), Vector((x0
, z1
))]
1099 n_sections
= n_subs
- 1
1100 n
, dz
, zs
, zl
= subs
[0]
1102 v0
= n
.v
.normalized()
1103 for s
, section
in enumerate(subs
):
1104 n
, dz
, zs
, zl
= section
1107 v1
= subs
[s
+ 1][0].v
.normalized()
1108 dir = (v0
+ v1
).normalized()
1109 scale
= 1 / cos(0.5 * acos(min(1, max(-1, v0
.dot(v1
)))))
1111 x
, y
= n
.p
+ scale
* p
.x
* dir
1112 z
= zl
+ p
.y
+ altitude
1113 verts
.append((x
, y
, z
))
1115 user_path_uv_v
.append((p1
- p0
).length
)
1119 # build faces using Panel
1121 # closed_shape, index, x, y, idmat
1123 [i
for i
in range(len(profile
))],
1124 [p
.x
for p
in profile
],
1125 [p
.y
for p
in profile
],
1126 [idmat
for i
in range(len(profile
))],
1128 user_path_uv_v
=user_path_uv_v
,
1129 user_path_verts
=n_subs
1131 faces
+= lofter
.faces(16, offset
=f
, path_type
='USER_DEFINED')
1132 matids
+= lofter
.mat(16, idmat
, idmat
, path_type
='USER_DEFINED')
1134 uvs
+= lofter
.uv(16, v
, v
, v
, v
, 0, v
, 0, 0, path_type
='USER_DEFINED')
1136 def reset_shapes(self
):
1137 for s
, stair
in enumerate(self
.stairs
):
1138 if 'Curved' in type(stair
).__name
__:
1139 stair
.l_shape
= self
.parts
[s
].left_shape
1140 stair
.r_shape
= self
.parts
[s
].right_shape
1142 def make_subs(self
, height
, step_depth
, x
, y
, z
, post_y
, altitude
, bottom
, side
, slice,
1143 post_spacing
, sub_spacing
, respect_edges
, move_x
, x_offset
, sub_offset_x
, mat
,
1144 verts
, faces
, matids
, uvs
):
1146 n_steps
= self
.n_steps(step_depth
)
1147 self
.set_height(height
/ n_steps
)
1148 n_stairs
= len(self
.stairs
) - 1
1152 offset
= move_x
- x_offset
1153 # offset_sub = offset - sub_offset_x
1155 offset
= move_x
+ x_offset
1156 # offset_sub = offset + sub_offset_x
1158 for s
, stair
in enumerate(self
.stairs
):
1159 if 'Curved' in type(stair
).__name
__:
1162 shape
= stair
.l_shape
1165 shape
= stair
.r_shape
1166 # Note: use left part as reference for post distances
1167 # use right part as reference for panels
1168 stair
.l_arc
, stair
.l_t0
, stair
.l_t1
, stair
.l_tc
= stair
.set_offset(offset
, shape
)
1169 stair
.r_arc
, stair
.r_t0
, stair
.r_t1
, stair
.r_tc
= stair
.set_offset(offset
, shape
)
1171 stair
.l_line
= stair
.offset(offset
)
1172 stair
.r_line
= stair
.offset(offset
)
1178 # interpolate z near end landing
1179 if 'Landing' in type(stair
).__name
__ and stair
.next_type
== 'STAIR':
1181 line
= stair
.normal(1).offset(self
.stairs
[s
+ 1].step_depth
)
1182 res
, p
, t_part
= part
.intersect(line
)
1183 # does perpendicular line intersects circle ?
1185 edge_size
= self
.stairs
[s
+ 1].step_depth
/ stair
.get_length(side
)
1186 edge_t
= 1 - edge_size
1188 # in this case, lerp z over one step
1189 lerp_z
= stair
.step_height
1191 t_step
, n_step
= stair
.n_posts(post_spacing
, side
, respect_edges
)
1193 # space between posts
1194 sp
= stair
.get_length(side
)
1196 t_post
= post_y
/ sp
1200 for i
in range(n_step
):
1201 res_t
= stair
.get_lerp_vect([], side
, i
, t_step
, respect_edges
)
1203 if s
< n_stairs
or i
< n_step
- 1:
1204 res_t
.append((i
+ 1) * t_step
)
1205 for j
in range(len(res_t
) - 1):
1206 t0
= res_t
[j
] + t_post
1207 t1
= res_t
[j
+ 1] - t_post
1209 n_subs
= int(sp
* dt
/ sub_spacing
)
1211 t_subs
= dt
/ n_subs
1212 for k
in range(1, n_subs
):
1214 stair
.get_lerp_vect(subs
, side
, 1, t0
+ k
* t_subs
, False)
1216 n
, dz
, z0
, z1
= subs
[-1]
1217 subs
[-1] = n
, dz
, z0
, z1
+ (t
- edge_t
) / edge_size
* stair
.step_height
1219 n
, dz
, z0
, z1
= subs
[-1]
1220 subs
[-1] = n
, dz
, z0
, z1
+ t
* stair
.step_height
1222 for i
, post
in enumerate(subs
):
1223 self
.get_post(post
, x
, y
, z
, altitude
, sub_offset_x
, mat
, verts
, faces
, matids
, uvs
, bottom
=bottom
)
1225 def make_post(self
, height
, step_depth
, x
, y
, z
, altitude
, side
, post_spacing
, respect_edges
, move_x
, x_offset
, mat
,
1226 verts
, faces
, matids
, uvs
):
1227 n_steps
= self
.n_steps(step_depth
)
1228 self
.set_height(height
/ n_steps
)
1230 n_stairs
= len(self
.stairs
) - 1
1232 for s
, stair
in enumerate(self
.stairs
):
1233 if type(stair
).__name
__ in ['CurvedStair', 'CurvedLanding']:
1234 stair
.l_arc
, stair
.l_t0
, stair
.l_t1
, stair
.l_tc
= stair
.set_offset(move_x
- x_offset
, stair
.l_shape
)
1235 stair
.r_arc
, stair
.r_t0
, stair
.r_t1
, stair
.r_tc
= stair
.set_offset(move_x
+ x_offset
, stair
.r_shape
)
1237 stair
.l_line
= stair
.offset(move_x
- x_offset
)
1238 stair
.r_line
= stair
.offset(move_x
+ x_offset
)
1240 t_step
, n_step
= stair
.n_posts(post_spacing
, side
, respect_edges
)
1244 for i
in range(n_step
):
1245 stair
.get_lerp_vect(l_posts
, side
, i
, t_step
, respect_edges
)
1247 if s
== n_stairs
and i
== n_step
- 1:
1248 n
, dz
, z0
, z1
= l_posts
[-1]
1249 l_posts
[-1] = (n
, dz
, z0
- stair
.step_height
, z1
)
1251 for i
, post
in enumerate(l_posts
):
1252 self
.get_post(post
, x
, y
, z
, altitude
, 0, mat
, verts
, faces
, matids
, uvs
)
1254 def make_panels(self
, height
, step_depth
, x
, z
, post_y
, altitude
, side
, post_spacing
,
1255 panel_dist
, respect_edges
, move_x
, x_offset
, sub_offset_x
, mat
, verts
, faces
, matids
, uvs
):
1257 n_steps
= self
.n_steps(step_depth
)
1258 self
.set_height(height
/ n_steps
)
1260 n_stairs
= len(self
.stairs
) - 1
1263 offset
= move_x
- x_offset
1265 offset
= move_x
+ x_offset
1267 for s
, stair
in enumerate(self
.stairs
):
1270 if 'Curved' in type(stair
).__name
__:
1272 is_circle
= stair
.l_shape
== "CIRCLE"
1273 shape
= stair
.l_shape
1275 is_circle
= stair
.r_shape
== "CIRCLE"
1276 shape
= stair
.r_shape
1277 stair
.l_arc
, stair
.l_t0
, stair
.l_t1
, stair
.l_tc
= stair
.set_offset(offset
, shape
)
1278 stair
.r_arc
, stair
.r_t0
, stair
.r_t1
, stair
.r_tc
= stair
.set_offset(offset
, shape
)
1280 stair
.l_line
= stair
.offset(offset
)
1281 stair
.r_line
= stair
.offset(offset
)
1283 # space between posts
1284 sp
= stair
.get_length(side
)
1286 t_step
, n_step
= stair
.n_posts(post_spacing
, side
, respect_edges
)
1288 if is_circle
and 'Curved' in type(stair
).__name
__:
1289 panel_da
= abs(stair
.da
) / pi
* 180 / n_step
1290 panel_step
= max(1, int(panel_da
/ 6))
1295 t_post
= (post_y
+ panel_dist
) / sp
1299 for i
in range(n_step
):
1300 res_t
= stair
.get_lerp_vect([], side
, i
, t_step
, respect_edges
)
1302 if s
< n_stairs
or i
< n_step
- 1:
1303 res_t
.append((i
+ 1) * t_step
)
1304 for j
in range(len(res_t
) - 1):
1305 t0
= res_t
[j
] + t_post
1306 t1
= res_t
[j
+ 1] - t_post
1308 t_curve
= dt
/ panel_step
1311 for k
in range(panel_step
):
1312 stair
.get_lerp_vect(panel
, side
, 1, t_curve
, True, t0_abs
=t0
+ k
* t_curve
)
1313 stair
.get_lerp_vect(panel
, side
, 1, t1
, False)
1316 self
.get_panel(sub
, altitude
, x
, z
, sub_offset_x
, mat
, verts
, faces
, matids
, uvs
)
1318 def make_part(self
, height
, step_depth
, part_x
, part_z
, x_move
, x_offset
,
1319 z_offset
, z_mode
, steps_type
, verts
, faces
, matids
, uvs
):
1321 params
= [(stair
.z_mode
, stair
.l_shape
, stair
.r_shape
,
1322 stair
.bottom_z
, stair
.steps_type
) for stair
in self
.stairs
]
1324 for stair
in self
.stairs
:
1326 stair
.l_shape
= stair
.r_shape
1328 stair
.r_shape
= stair
.l_shape
1329 stair
.steps_type
= steps_type
1330 stair
.z_mode
= "LINEAR"
1331 stair
.bottom_z
= part_z
1332 if 'Curved' in type(stair
).__name
__:
1333 stair
.l_arc
, stair
.l_t0
, stair
.l_t1
, stair
.l_tc
= \
1334 stair
.set_offset(x_move
+ x_offset
+ 0.5 * part_x
, stair
.l_shape
)
1335 stair
.r_arc
, stair
.r_t0
, stair
.r_t1
, stair
.r_tc
= \
1336 stair
.set_offset(x_move
+ x_offset
- 0.5 * part_x
, stair
.r_shape
)
1338 stair
.l_line
= stair
.offset(x_move
+ x_offset
+ 0.5 * part_x
)
1339 stair
.r_line
= stair
.offset(x_move
+ x_offset
- 0.5 * part_x
)
1340 n_steps
= self
.n_steps(step_depth
)
1341 self
.set_height(height
/ n_steps
)
1342 for j
, stair
in enumerate(self
.stairs
):
1343 stair
.z0
+= z_offset
+ part_z
1346 stair
.step_height
/= 2
1347 for i
in range(stair
.n_step
):
1348 stair
.make_step(i
, verts
, faces
, matids
, uvs
, nose_y
=0)
1351 stair
.step_height
*= 2
1352 stair
.z_mode
= params
[j
][0]
1353 stair
.l_shape
= params
[j
][1]
1354 stair
.r_shape
= params
[j
][2]
1355 stair
.bottom_z
= params
[j
][3]
1356 stair
.steps_type
= params
[j
][4]
1357 stair
.z0
-= z_offset
+ part_z
1359 def make_profile(self
, profile
, idmat
, side
, slice, height
, step_depth
,
1360 x_offset
, z_offset
, extend
, verts
, faces
, matids
, uvs
):
1362 for stair
in self
.stairs
:
1363 if 'Curved' in type(stair
).__name
__:
1364 stair
.l_arc
, stair
.l_t0
, stair
.l_t1
, stair
.l_tc
= stair
.set_offset(-x_offset
, stair
.l_shape
)
1365 stair
.r_arc
, stair
.r_t0
, stair
.r_t1
, stair
.r_tc
= stair
.set_offset(x_offset
, stair
.r_shape
)
1367 stair
.l_line
= stair
.offset(-x_offset
)
1368 stair
.r_line
= stair
.offset(x_offset
)
1370 n_steps
= self
.n_steps(step_depth
)
1371 self
.set_height(height
/ n_steps
)
1373 n_stairs
= len(self
.stairs
) - 1
1383 t
= -extend
/ self
.stairs
[0].length
1384 self
.stairs
[0].get_lerp_vect(sections
[-1], side
, 1, t
, True)
1386 for s
, stair
in enumerate(self
.stairs
):
1390 if 'Curved' in type(stair
).__name
__:
1393 is_circle
= stair
.l_shape
== "CIRCLE"
1396 is_circle
= stair
.r_shape
== "CIRCLE"
1404 n_step
= 3 * stair
.n_step
1411 # last section 1 step before stair
1412 if 'Landing' in type(stair
).__name
__ and stair
.next_type
== 'STAIR':
1414 line
= stair
.normal(1).offset(self
.stairs
[s
+ 1].step_depth
)
1415 res
, p
, t_part
= part
.intersect(line
)
1416 # does perpendicular line intersects circle ?
1418 last_t
= 1 - self
.stairs
[s
+ 1].step_depth
/ stair
.get_length(side
)
1422 # in this case, lerp z over one step
1424 lerp_z
= stair
.step_height
1429 for i
in range(n_step
):
1430 res_t
= stair
.get_lerp_vect(sections
[-1], side
, i
, t_step
, True, z_offset
=lerp_z
)
1431 # remove corner section
1433 if cur_t
> 0 and cur_t
> last_t
:
1434 sections
[-1] = sections
[-1][:-1]
1436 # last section 1 step before next stair start
1437 if 'Landing' in type(stair
).__name
__ and stair
.next_type
== 'STAIR':
1439 stair
.get_lerp_vect(sections
[-1], side
, 1, last_t
, False)
1443 t
= -extend
/ self
.stairs
[s
+ 1].length
1444 self
.stairs
[s
+ 1].get_lerp_vect(sections
[-1], side
, 1, t
, True)
1446 t
= 1 + extend
/ self
.stairs
[-1].length
1447 self
.stairs
[-1].get_lerp_vect(sections
[-1], side
, 1, t
, True)
1449 for cur_sect
in sections
:
1450 user_path_verts
= len(cur_sect
)
1452 if user_path_verts
> 0:
1454 n
, dz
, z0
, z1
= cur_sect
[-1]
1455 cur_sect
[-1] = (n
, dz
, z0
- stair
.step_height
, z1
)
1456 n_sections
= user_path_verts
- 1
1457 n
, dz
, zs
, zl
= cur_sect
[0]
1459 v0
= n
.v
.normalized()
1460 for s
, section
in enumerate(cur_sect
):
1461 n
, dz
, zs
, zl
= section
1464 v1
= cur_sect
[s
+ 1][0].v
.normalized()
1465 dir = (v0
+ v1
).normalized()
1466 scale
= 1 / cos(0.5 * acos(min(1, max(-1, v0
.dot(v1
)))))
1468 x
, y
= n
.p
+ scale
* p
.x
* dir
1469 z
= zl
+ p
.y
+ z_offset
1470 verts
.append((x
, y
, z
))
1472 user_path_uv_v
.append((p1
- p0
).length
)
1476 # build faces using Panel
1478 # closed_shape, index, x, y, idmat
1480 [i
for i
in range(len(profile
))],
1481 [p
.x
for p
in profile
],
1482 [p
.y
for p
in profile
],
1483 [idmat
for i
in range(len(profile
))],
1485 user_path_uv_v
=user_path_uv_v
,
1486 user_path_verts
=user_path_verts
1488 faces
+= lofter
.faces(16, offset
=f
, path_type
='USER_DEFINED')
1489 matids
+= lofter
.mat(16, idmat
, idmat
, path_type
='USER_DEFINED')
1491 uvs
+= lofter
.uv(16, v
, v
, v
, v
, 0, v
, 0, 0, path_type
='USER_DEFINED')
1493 def set_matids(self
, id_materials
):
1494 for stair
in self
.stairs
:
1495 stair
.set_matids(id_materials
)
1498 def update(self
, context
):
1499 self
.update(context
)
1502 def update_manipulators(self
, context
):
1503 self
.update(context
, manipulable_refresh
=True)
1506 def update_preset(self
, context
):
1507 auto_update
= self
.auto_update
1508 self
.auto_update
= False
1509 if self
.presets
== 'STAIR_I':
1512 self
.parts
[0].type = 'S_STAIR'
1513 elif self
.presets
== 'STAIR_L':
1516 self
.parts
[0].type = 'S_STAIR'
1517 self
.parts
[1].type = 'C_STAIR'
1518 self
.parts
[2].type = 'S_STAIR'
1520 elif self
.presets
== 'STAIR_U':
1523 self
.parts
[0].type = 'S_STAIR'
1524 self
.parts
[1].type = 'D_STAIR'
1525 self
.parts
[2].type = 'S_STAIR'
1527 elif self
.presets
== 'STAIR_O':
1530 self
.parts
[0].type = 'D_STAIR'
1531 self
.parts
[1].type = 'D_STAIR'
1533 # keep auto_update state same
1534 # prevent unwanted load_preset update
1535 self
.auto_update
= auto_update
1539 ('0', 'Ceiling', '', 0),
1540 ('1', 'White', '', 1),
1541 ('2', 'Concrete', '', 2),
1542 ('3', 'Wood', '', 3),
1543 ('4', 'Metal', '', 4),
1544 ('5', 'Glass', '', 5)
1548 class archipack_stair_material(PropertyGroup
):
1549 index
: EnumProperty(
1550 items
=materials_enum
,
1555 def find_datablock_in_selection(self
, context
):
1557 find witch selected object this instance belongs to
1558 provide support for "copy to selected"
1560 selected
= context
.selected_objects
[:]
1562 props
= archipack_stair
.datablock(o
)
1564 for part
in props
.rail_mat
:
1569 def update(self
, context
):
1570 props
= self
.find_datablock_in_selection(context
)
1571 if props
is not None:
1572 props
.update(context
)
1575 class archipack_stair_part(PropertyGroup
):
1576 type : EnumProperty(
1578 ('S_STAIR', 'Straight stair', '', 0),
1579 ('C_STAIR', 'Curved stair', '', 1),
1580 ('D_STAIR', 'Dual Curved stair', '', 2),
1581 ('S_LANDING', 'Straight landing', '', 3),
1582 ('C_LANDING', 'Curved landing', '', 4),
1583 ('D_LANDING', 'Dual Curved landing', '', 5)
1586 update
=update_manipulators
1588 length
: FloatProperty(
1592 unit
='LENGTH', subtype
='DISTANCE',
1595 radius
: FloatProperty(
1599 unit
='LENGTH', subtype
='DISTANCE',
1607 subtype
='ANGLE', unit
='ROTATION',
1610 left_shape
: EnumProperty(
1612 ('RECTANGLE', 'Straight', '', 0),
1613 ('CIRCLE', 'Curved ', '', 1)
1615 default
='RECTANGLE',
1618 right_shape
: EnumProperty(
1620 ('RECTANGLE', 'Straight', '', 0),
1621 ('CIRCLE', 'Curved ', '', 1)
1623 default
='RECTANGLE',
1626 manipulators
: CollectionProperty(type=archipack_manipulator
)
1628 def find_datablock_in_selection(self
, context
):
1630 find witch selected object this instance belongs to
1631 provide support for "copy to selected"
1633 selected
= context
.selected_objects
[:]
1635 props
= archipack_stair
.datablock(o
)
1637 for part
in props
.parts
:
1642 def update(self
, context
, manipulable_refresh
=False):
1643 props
= self
.find_datablock_in_selection(context
)
1644 if props
is not None:
1645 props
.update(context
, manipulable_refresh
)
1647 def draw(self
, layout
, context
, index
, user_mode
):
1651 row
.prop(self
, "type", text
=str(index
+ 1))
1652 if self
.type in ['C_STAIR', 'C_LANDING', 'D_STAIR', 'D_LANDING']:
1654 row
.prop(self
, "radius")
1656 row
.prop(self
, "da")
1659 row
.prop(self
, "length")
1660 if self
.type in ['C_STAIR', 'C_LANDING', 'D_STAIR', 'D_LANDING']:
1661 row
= box
.row(align
=True)
1662 row
.prop(self
, "left_shape", text
="")
1663 row
.prop(self
, "right_shape", text
="")
1665 if self
.type in ['S_STAIR', 'S_LANDING']:
1668 row
.prop(self
, "length")
1671 class archipack_stair(ArchipackObject
, Manipulable
, PropertyGroup
):
1673 parts
: CollectionProperty(type=archipack_stair_part
)
1674 n_parts
: IntProperty(
1678 default
=1, update
=update_manipulators
1680 step_depth
: FloatProperty(
1684 unit
='LENGTH', subtype
='DISTANCE',
1687 width
: FloatProperty(
1691 unit
='LENGTH', subtype
='DISTANCE',
1694 height
: FloatProperty(
1697 default
=2.4, precision
=2, step
=1,
1698 unit
='LENGTH', subtype
='DISTANCE',
1701 nose_y
: FloatProperty(
1704 default
=0.02, precision
=2, step
=1,
1705 unit
='LENGTH', subtype
='DISTANCE',
1708 x_offset
: FloatProperty(
1710 default
=0.0, precision
=2, step
=1,
1711 unit
='LENGTH', subtype
='DISTANCE',
1714 nose_z
: FloatProperty(
1717 default
=0.03, precision
=2, step
=1,
1718 unit
='LENGTH', subtype
='DISTANCE',
1721 bottom_z
: FloatProperty(
1724 default
=0.03, precision
=2, step
=1,
1725 unit
='LENGTH', subtype
='DISTANCE',
1728 radius
: FloatProperty(
1732 unit
='LENGTH', subtype
='DISTANCE',
1740 subtype
='ANGLE', unit
='ROTATION',
1743 total_angle
: FloatProperty(
1748 subtype
='ANGLE', unit
='ROTATION',
1751 steps_type
: EnumProperty(
1754 ('CLOSED', 'Closed', '', 0),
1755 ('FULL', 'Full height', '', 1),
1756 ('OPEN', 'Open ', '', 2)
1761 nose_type
: EnumProperty(
1764 ('STRAIGHT', 'Straight', '', 0),
1765 ('OBLIQUE', 'Oblique', '', 1),
1770 left_shape
: EnumProperty(
1772 ('RECTANGLE', 'Straight', '', 0),
1773 ('CIRCLE', 'Curved ', '', 1)
1775 default
='RECTANGLE',
1778 right_shape
: EnumProperty(
1780 ('RECTANGLE', 'Straight', '', 0),
1781 ('CIRCLE', 'Curved ', '', 1)
1783 default
='RECTANGLE',
1786 z_mode
: EnumProperty(
1789 ('STANDARD', 'Standard', '', 0),
1790 ('LINEAR', 'Bottom Linear', '', 1),
1791 ('LINEAR_TOP', 'All Linear', '', 2)
1796 presets
: EnumProperty(
1798 ('STAIR_I', 'I stair', '', 0),
1799 ('STAIR_L', 'L stair', '', 1),
1800 ('STAIR_U', 'U stair', '', 2),
1801 ('STAIR_O', 'O stair', '', 3),
1802 ('STAIR_USER', 'User defined stair', '', 4),
1804 default
='STAIR_I', update
=update_preset
1806 left_post
: BoolProperty(
1811 right_post
: BoolProperty(
1816 post_spacing
: FloatProperty(
1819 default
=1.0, precision
=2, step
=1,
1820 unit
='LENGTH', subtype
='DISTANCE',
1823 post_x
: FloatProperty(
1826 default
=0.04, precision
=2, step
=1,
1827 unit
='LENGTH', subtype
='DISTANCE',
1830 post_y
: FloatProperty(
1833 default
=0.04, precision
=2, step
=1,
1834 unit
='LENGTH', subtype
='DISTANCE',
1837 post_z
: FloatProperty(
1840 default
=1, precision
=2, step
=1,
1841 unit
='LENGTH', subtype
='DISTANCE',
1844 post_alt
: FloatProperty(
1847 default
=0, precision
=2, step
=1,
1848 unit
='LENGTH', subtype
='DISTANCE',
1851 post_offset_x
: FloatProperty(
1853 min=-100.0, max=100,
1854 default
=0.02, precision
=2, step
=1,
1855 unit
='LENGTH', subtype
='DISTANCE',
1858 post_corners
: BoolProperty(
1859 name
="Only on edges",
1863 user_defined_post_enable
: BoolProperty(
1868 user_defined_post
: StringProperty(
1869 name
="User defined",
1872 idmat_post
: EnumProperty(
1874 items
=materials_enum
,
1878 left_subs
: BoolProperty(
1883 right_subs
: BoolProperty(
1888 subs_spacing
: FloatProperty(
1891 default
=0.10, precision
=2, step
=1,
1892 unit
='LENGTH', subtype
='DISTANCE',
1895 subs_x
: FloatProperty(
1898 default
=0.02, precision
=2, step
=1,
1899 unit
='LENGTH', subtype
='DISTANCE',
1902 subs_y
: FloatProperty(
1905 default
=0.02, precision
=2, step
=1,
1906 unit
='LENGTH', subtype
='DISTANCE',
1909 subs_z
: FloatProperty(
1912 default
=1, precision
=2, step
=1,
1913 unit
='LENGTH', subtype
='DISTANCE',
1916 subs_alt
: FloatProperty(
1919 default
=0, precision
=2, step
=1,
1920 unit
='LENGTH', subtype
='DISTANCE',
1923 subs_offset_x
: FloatProperty(
1925 min=-100.0, max=100,
1926 default
=0.0, precision
=2, step
=1,
1927 unit
='LENGTH', subtype
='DISTANCE',
1930 subs_bottom
: EnumProperty(
1933 ('STEP', 'Follow step', '', 0),
1934 ('LINEAR', 'Linear', '', 1),
1939 user_defined_subs_enable
: BoolProperty(
1944 user_defined_subs
: StringProperty(
1945 name
="User defined",
1948 idmat_subs
: EnumProperty(
1950 items
=materials_enum
,
1954 left_panel
: BoolProperty(
1959 right_panel
: BoolProperty(
1964 panel_alt
: FloatProperty(
1966 default
=0.25, precision
=2, step
=1,
1967 unit
='LENGTH', subtype
='DISTANCE',
1970 panel_x
: FloatProperty(
1973 default
=0.01, precision
=2, step
=1,
1974 unit
='LENGTH', subtype
='DISTANCE',
1977 panel_z
: FloatProperty(
1980 default
=0.6, precision
=2, step
=1,
1981 unit
='LENGTH', subtype
='DISTANCE',
1984 panel_dist
: FloatProperty(
1987 default
=0.05, precision
=2, step
=1,
1988 unit
='LENGTH', subtype
='DISTANCE',
1991 panel_offset_x
: FloatProperty(
1993 default
=0.0, precision
=2, step
=1,
1994 unit
='LENGTH', subtype
='DISTANCE',
1997 idmat_panel
: EnumProperty(
1999 items
=materials_enum
,
2003 left_rail
: BoolProperty(
2008 right_rail
: BoolProperty(
2013 rail_n
: IntProperty(
2020 rail_x
: FloatVectorProperty(
2023 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05,
2024 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05,
2025 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05,
2026 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05
2030 precision
=2, step
=1,
2034 rail_z
: FloatVectorProperty(
2037 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05,
2038 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05,
2039 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05,
2040 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05
2044 precision
=2, step
=1,
2048 rail_offset
: FloatVectorProperty(
2051 0, 0, 0, 0, 0, 0, 0, 0,
2052 0, 0, 0, 0, 0, 0, 0, 0,
2053 0, 0, 0, 0, 0, 0, 0, 0,
2057 precision
=2, step
=1,
2061 rail_alt
: FloatVectorProperty(
2064 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
2065 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
2066 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
2067 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0
2070 precision
=2, step
=1,
2074 rail_mat
: CollectionProperty(type=archipack_stair_material
)
2076 left_handrail
: BoolProperty(
2081 right_handrail
: BoolProperty(
2086 handrail_offset
: FloatProperty(
2088 default
=0.0, precision
=2, step
=1,
2089 unit
='LENGTH', subtype
='DISTANCE',
2092 handrail_alt
: FloatProperty(
2094 default
=1.0, precision
=2, step
=1,
2095 unit
='LENGTH', subtype
='DISTANCE',
2098 handrail_extend
: FloatProperty(
2100 default
=0.1, precision
=2, step
=1,
2101 unit
='LENGTH', subtype
='DISTANCE',
2104 handrail_slice_left
: BoolProperty(
2109 handrail_slice_right
: BoolProperty(
2114 handrail_profil
: EnumProperty(
2117 ('SQUARE', 'Square', '', 0),
2118 ('CIRCLE', 'Circle', '', 1),
2119 ('COMPLEX', 'Circle over square', '', 2)
2124 handrail_x
: FloatProperty(
2127 default
=0.04, precision
=2, step
=1,
2128 unit
='LENGTH', subtype
='DISTANCE',
2131 handrail_y
: FloatProperty(
2134 default
=0.04, precision
=2, step
=1,
2135 unit
='LENGTH', subtype
='DISTANCE',
2138 handrail_radius
: FloatProperty(
2141 default
=0.02, precision
=2, step
=1,
2142 unit
='LENGTH', subtype
='DISTANCE',
2146 left_string
: BoolProperty(
2151 right_string
: BoolProperty(
2156 string_x
: FloatProperty(
2159 default
=0.02, precision
=2, step
=1,
2160 unit
='LENGTH', subtype
='DISTANCE',
2163 string_z
: FloatProperty(
2165 default
=0.3, precision
=2, step
=1,
2166 unit
='LENGTH', subtype
='DISTANCE',
2169 string_offset
: FloatProperty(
2171 default
=0.0, precision
=2, step
=1,
2172 unit
='LENGTH', subtype
='DISTANCE',
2175 string_alt
: FloatProperty(
2177 default
=-0.04, precision
=2, step
=1,
2178 unit
='LENGTH', subtype
='DISTANCE',
2182 idmat_bottom
: EnumProperty(
2184 items
=materials_enum
,
2188 idmat_raise
: EnumProperty(
2190 items
=materials_enum
,
2194 idmat_step_front
: EnumProperty(
2196 items
=materials_enum
,
2200 idmat_top
: EnumProperty(
2202 items
=materials_enum
,
2206 idmat_side
: EnumProperty(
2208 items
=materials_enum
,
2212 idmat_step_side
: EnumProperty(
2214 items
=materials_enum
,
2218 idmat_handrail
: EnumProperty(
2220 items
=materials_enum
,
2224 idmat_string
: EnumProperty(
2226 items
=materials_enum
,
2232 parts_expand
: BoolProperty(
2235 steps_expand
: BoolProperty(
2238 rail_expand
: BoolProperty(
2241 idmats_expand
: BoolProperty(
2244 handrail_expand
: BoolProperty(
2247 string_expand
: BoolProperty(
2250 post_expand
: BoolProperty(
2253 panel_expand
: BoolProperty(
2256 subs_expand
: BoolProperty(
2260 auto_update
: BoolProperty(
2261 options
={'SKIP_SAVE'},
2263 update
=update_manipulators
2266 def setup_manipulators(self
):
2268 if len(self
.manipulators
) == 0:
2269 s
= self
.manipulators
.add()
2270 s
.prop1_name
= "width"
2271 s
= self
.manipulators
.add()
2272 s
.prop1_name
= "height"
2273 s
.normal
= Vector((0, 1, 0))
2275 for i
in range(self
.n_parts
):
2277 n_manips
= len(p
.manipulators
)
2279 m
= p
.manipulators
.add()
2281 m
.prop1_name
= 'length'
2283 def update_parts(self
):
2285 # remove rails materials
2286 for i
in range(len(self
.rail_mat
), self
.rail_n
, -1):
2287 self
.rail_mat
.remove(i
- 1)
2290 for i
in range(len(self
.rail_mat
), self
.rail_n
):
2294 for i
in range(len(self
.parts
), self
.n_parts
, -1):
2295 self
.parts
.remove(i
- 1)
2298 for i
in range(len(self
.parts
), self
.n_parts
):
2301 self
.setup_manipulators()
2303 def update(self
, context
, manipulable_refresh
=False):
2305 o
= self
.find_in_selection(context
, self
.auto_update
)
2310 # clean up manipulators before any data model change
2311 if manipulable_refresh
:
2312 self
.manipulable_disable(context
)
2316 center
= Vector((0, 0))
2321 id_materials
= [int(self
.idmat_top
), int(self
.idmat_step_front
), int(self
.idmat_raise
),
2322 int(self
.idmat_side
), int(self
.idmat_bottom
), int(self
.idmat_step_side
)]
2325 bottom_z
= self
.bottom_z
2326 if self
.steps_type
== 'OPEN':
2328 bottom_z
= self
.nose_z
2330 width_left
= 0.5 * self
.width
- self
.x_offset
2331 width_right
= 0.5 * self
.width
+ self
.x_offset
2333 self
.manipulators
[0].set_pts([(-width_left
, 0, 0), (width_right
, 0, 0), (1, 0, 0)])
2334 self
.manipulators
[1].set_pts([(0, 0, 0), (0, 0, self
.height
), (1, 0, 0)])
2336 g
= StairGenerator(self
.parts
)
2337 if self
.presets
== 'STAIR_USER':
2338 for part
in self
.parts
:
2339 g
.add_part(part
.type, self
.steps_type
, self
.nose_type
, self
.z_mode
, self
.nose_z
,
2340 bottom_z
, center
, max(width_left
+ 0.01, width_right
+ 0.01, part
.radius
), part
.da
,
2341 width_left
, width_right
, part
.length
, part
.left_shape
, part
.right_shape
)
2343 elif self
.presets
== 'STAIR_O':
2344 n_parts
= max(1, int(round(abs(self
.total_angle
) / pi
, 0)))
2345 if self
.total_angle
> 0:
2349 last_da
= self
.total_angle
- dir * (n_parts
- 1) * pi
2350 if dir * last_da
> pi
:
2353 abs_last
= dir * last_da
2355 for part
in range(n_parts
- 1):
2356 g
.add_part('D_STAIR', self
.steps_type
, self
.nose_type
, self
.z_mode
, self
.nose_z
,
2357 bottom_z
, center
, max(width_left
+ 0.01, width_right
+ 0.01, self
.radius
), dir * pi
,
2358 width_left
, width_right
, 1.0, self
.left_shape
, self
.right_shape
)
2359 if round(abs_last
, 2) > 0:
2360 if abs_last
> pi
/ 2:
2361 g
.add_part('C_STAIR', self
.steps_type
, self
.nose_type
, self
.z_mode
, self
.nose_z
,
2362 bottom_z
, center
, max(width_left
+ 0.01, width_right
+ 0.01, self
.radius
),
2364 width_left
, width_right
, 1.0, self
.left_shape
, self
.right_shape
)
2365 g
.add_part('C_STAIR', self
.steps_type
, self
.nose_type
, self
.z_mode
, self
.nose_z
,
2366 bottom_z
, center
, max(width_left
+ 0.01, width_right
+ 0.01, self
.radius
),
2367 last_da
- dir * pi
/ 2,
2368 width_left
, width_right
, 1.0, self
.left_shape
, self
.right_shape
)
2370 g
.add_part('C_STAIR', self
.steps_type
, self
.nose_type
, self
.z_mode
, self
.nose_z
,
2371 bottom_z
, center
, max(width_left
+ 0.01, width_right
+ 0.01, self
.radius
), last_da
,
2372 width_left
, width_right
, 1.0, self
.left_shape
, self
.right_shape
)
2374 # STAIR_L STAIR_I STAIR_U
2375 for part
in self
.parts
:
2376 g
.add_part(part
.type, self
.steps_type
, self
.nose_type
, self
.z_mode
, self
.nose_z
,
2377 bottom_z
, center
, max(width_left
+ 0.01, width_right
+ 0.01, self
.radius
), self
.da
,
2378 width_left
, width_right
, part
.length
, self
.left_shape
, self
.right_shape
)
2381 g
.set_matids(id_materials
)
2382 g
.make_stair(self
.height
, self
.step_depth
, verts
, faces
, matids
, uvs
, nose_y
=self
.nose_y
)
2385 offset_x
= 0.5 * self
.width
- self
.post_offset_x
2386 post_spacing
= self
.post_spacing
2387 if self
.post_corners
:
2388 post_spacing
= 10000
2390 if self
.user_defined_post_enable
:
2391 # user defined posts
2392 user_def_post
= context
.scene
.objects
.get(self
.user_defined_post
.strip())
2393 if user_def_post
is not None and user_def_post
.type == 'MESH':
2394 g
.setup_user_defined_post(user_def_post
, self
.post_x
, self
.post_y
, self
.post_z
)
2397 g
.make_post(self
.height
, self
.step_depth
, 0.5 * self
.post_x
, 0.5 * self
.post_y
,
2398 self
.post_z
, self
.post_alt
, 'LEFT', post_spacing
, self
.post_corners
,
2399 self
.x_offset
, offset_x
, int(self
.idmat_post
), verts
, faces
, matids
, uvs
)
2402 g
.make_post(self
.height
, self
.step_depth
, 0.5 * self
.post_x
, 0.5 * self
.post_y
,
2403 self
.post_z
, self
.post_alt
, 'RIGHT', post_spacing
, self
.post_corners
,
2404 self
.x_offset
, offset_x
, int(self
.idmat_post
), verts
, faces
, matids
, uvs
)
2406 # reset user def posts
2407 g
.user_defined_post
= None
2410 if self
.user_defined_subs_enable
:
2411 user_def_subs
= context
.scene
.objects
.get(self
.user_defined_subs
.strip())
2412 if user_def_subs
is not None and user_def_subs
.type == 'MESH':
2413 g
.setup_user_defined_post(user_def_subs
, self
.subs_x
, self
.subs_y
, self
.subs_z
)
2416 g
.make_subs(self
.height
, self
.step_depth
, 0.5 * self
.subs_x
, 0.5 * self
.subs_y
,
2417 self
.subs_z
, 0.5 * self
.post_y
, self
.subs_alt
, self
.subs_bottom
, 'LEFT',
2418 self
.handrail_slice_left
, post_spacing
, self
.subs_spacing
, self
.post_corners
,
2419 self
.x_offset
, offset_x
, -self
.subs_offset_x
, int(self
.idmat_subs
), verts
, faces
, matids
, uvs
)
2422 g
.make_subs(self
.height
, self
.step_depth
, 0.5 * self
.subs_x
, 0.5 * self
.subs_y
,
2423 self
.subs_z
, 0.5 * self
.post_y
, self
.subs_alt
, self
.subs_bottom
, 'RIGHT',
2424 self
.handrail_slice_right
, post_spacing
, self
.subs_spacing
, self
.post_corners
,
2425 self
.x_offset
, offset_x
, self
.subs_offset_x
, int(self
.idmat_subs
), verts
, faces
, matids
, uvs
)
2427 g
.user_defined_post
= None
2430 g
.make_panels(self
.height
, self
.step_depth
, 0.5 * self
.panel_x
, self
.panel_z
, 0.5 * self
.post_y
,
2431 self
.panel_alt
, 'LEFT', post_spacing
, self
.panel_dist
, self
.post_corners
,
2432 self
.x_offset
, offset_x
, -self
.panel_offset_x
, int(self
.idmat_panel
), verts
, faces
, matids
, uvs
)
2434 if self
.right_panel
:
2435 g
.make_panels(self
.height
, self
.step_depth
, 0.5 * self
.panel_x
, self
.panel_z
, 0.5 * self
.post_y
,
2436 self
.panel_alt
, 'RIGHT', post_spacing
, self
.panel_dist
, self
.post_corners
,
2437 self
.x_offset
, offset_x
, self
.panel_offset_x
, int(self
.idmat_panel
), verts
, faces
, matids
, uvs
)
2440 for i
in range(self
.rail_n
):
2441 id_materials
= [int(self
.rail_mat
[i
].index
) for j
in range(6)]
2442 g
.set_matids(id_materials
)
2443 g
.make_part(self
.height
, self
.step_depth
, self
.rail_x
[i
], self
.rail_z
[i
],
2444 self
.x_offset
, offset_x
+ self
.rail_offset
[i
],
2445 self
.rail_alt
[i
], 'LINEAR', 'CLOSED', verts
, faces
, matids
, uvs
)
2448 for i
in range(self
.rail_n
):
2449 id_materials
= [int(self
.rail_mat
[i
].index
) for j
in range(6)]
2450 g
.set_matids(id_materials
)
2451 g
.make_part(self
.height
, self
.step_depth
, self
.rail_x
[i
], self
.rail_z
[i
],
2452 self
.x_offset
, -offset_x
- self
.rail_offset
[i
],
2453 self
.rail_alt
[i
], 'LINEAR', 'CLOSED', verts
, faces
, matids
, uvs
)
2455 if self
.handrail_profil
== 'COMPLEX':
2456 sx
= self
.handrail_x
2457 sy
= self
.handrail_y
2458 handrail
= [Vector((sx
* x
, sy
* y
)) for x
, y
in [
2459 (-0.28, 1.83), (-0.355, 1.77), (-0.415, 1.695), (-0.46, 1.605), (-0.49, 1.51), (-0.5, 1.415),
2460 (-0.49, 1.315), (-0.46, 1.225), (-0.415, 1.135), (-0.355, 1.06), (-0.28, 1.0), (-0.255, 0.925),
2461 (-0.33, 0.855), (-0.5, 0.855), (-0.5, 0.0), (0.5, 0.0), (0.5, 0.855), (0.33, 0.855), (0.255, 0.925),
2462 (0.28, 1.0), (0.355, 1.06), (0.415, 1.135), (0.46, 1.225), (0.49, 1.315), (0.5, 1.415),
2463 (0.49, 1.51), (0.46, 1.605), (0.415, 1.695), (0.355, 1.77), (0.28, 1.83), (0.19, 1.875),
2464 (0.1, 1.905), (0.0, 1.915), (-0.095, 1.905), (-0.19, 1.875)]]
2466 elif self
.handrail_profil
== 'SQUARE':
2467 x
= 0.5 * self
.handrail_x
2469 handrail
= [Vector((-x
, y
)), Vector((-x
, 0)), Vector((x
, 0)), Vector((x
, y
))]
2470 elif self
.handrail_profil
== 'CIRCLE':
2471 r
= self
.handrail_radius
2472 handrail
= [Vector((r
* sin(0.1 * -a
* pi
), r
* (0.5 + cos(0.1 * -a
* pi
)))) for a
in range(0, 20)]
2474 if self
.right_handrail
:
2475 g
.make_profile(handrail
, int(self
.idmat_handrail
), "RIGHT", self
.handrail_slice_right
,
2476 self
.height
, self
.step_depth
, self
.x_offset
+ offset_x
+ self
.handrail_offset
,
2477 self
.handrail_alt
, self
.handrail_extend
, verts
, faces
, matids
, uvs
)
2479 if self
.left_handrail
:
2480 g
.make_profile(handrail
, int(self
.idmat_handrail
), "LEFT", self
.handrail_slice_left
,
2481 self
.height
, self
.step_depth
, -self
.x_offset
+ offset_x
+ self
.handrail_offset
,
2482 self
.handrail_alt
, self
.handrail_extend
, verts
, faces
, matids
, uvs
)
2484 w
= 0.5 * self
.string_x
2486 string
= [Vector((-w
, 0)), Vector((w
, 0)), Vector((w
, h
)), Vector((-w
, h
))]
2488 if self
.right_string
:
2489 g
.make_profile(string
, int(self
.idmat_string
), "RIGHT", False, self
.height
, self
.step_depth
,
2490 self
.x_offset
+ 0.5 * self
.width
+ self
.string_offset
,
2491 self
.string_alt
, 0, verts
, faces
, matids
, uvs
)
2493 if self
.left_string
:
2494 g
.make_profile(string
, int(self
.idmat_string
), "LEFT", False, self
.height
, self
.step_depth
,
2495 -self
.x_offset
+ 0.5 * self
.width
+ self
.string_offset
,
2496 self
.string_alt
, 0, verts
, faces
, matids
, uvs
)
2498 bmed
.buildmesh(context
, o
, verts
, faces
, matids
=matids
, uvs
=uvs
, weld
=True, clean
=True)
2500 # enable manipulators rebuild
2501 if manipulable_refresh
:
2502 self
.manipulable_refresh
= True
2504 self
.restore_context(context
)
2506 def manipulable_setup(self
, context
):
2508 TODO: Implement the setup part as per parent object basis
2510 self.manipulable_disable(context)
2511 o = context.active_object
2512 for m in self.manipulators:
2513 self.manip_stack.append(m.setup(context, o, self))
2516 self
.manipulable_disable(context
)
2517 o
= context
.active_object
2519 self
.setup_manipulators()
2521 if self
.presets
!= 'STAIR_O':
2522 for i
, part
in enumerate(self
.parts
):
2523 if i
>= self
.n_parts
:
2525 if "S_" in part
.type or self
.presets
in ['STAIR_USER']:
2526 for j
, m
in enumerate(part
.manipulators
):
2527 self
.manip_stack
.append(m
.setup(context
, o
, part
))
2529 if self
.presets
in ['STAIR_U', 'STAIR_L']:
2530 self
.manip_stack
.append(self
.parts
[1].manipulators
[0].setup(context
, o
, self
))
2532 for m
in self
.manipulators
:
2533 self
.manip_stack
.append(m
.setup(context
, o
, self
))
2536 class ARCHIPACK_PT_stair(Panel
):
2537 bl_idname
= "ARCHIPACK_PT_stair"
2539 bl_space_type
= 'VIEW_3D'
2540 bl_region_type
= 'UI'
2541 # bl_context = 'object'
2542 bl_category
= 'Archipack'
2545 def poll(cls
, context
):
2546 return archipack_stair
.filter(context
.active_object
)
2548 def draw(self
, context
):
2549 prop
= archipack_stair
.datablock(context
.active_object
)
2552 scene
= context
.scene
2553 layout
= self
.layout
2554 row
= layout
.row(align
=True)
2555 row
.operator('archipack.stair_manipulate', icon
='VIEW_PAN')
2556 row
= layout
.row(align
=True)
2557 row
.prop(prop
, 'presets', text
="")
2559 # box.label(text="Styles")
2560 row
= box
.row(align
=True)
2561 # row.menu("ARCHIPACK_MT_stair_preset", text=bpy.types.ARCHIPACK_MT_stair_preset.bl_label)
2562 row
.operator("archipack.stair_preset_menu", text
=bpy
.types
.ARCHIPACK_OT_stair_preset_menu
.bl_label
)
2563 row
.operator("archipack.stair_preset", text
="", icon
='ADD')
2564 row
.operator("archipack.stair_preset", text
="", icon
='REMOVE').remove_active
= True
2566 box
.prop(prop
, 'width')
2567 box
.prop(prop
, 'height')
2568 box
.prop(prop
, 'bottom_z')
2569 box
.prop(prop
, 'x_offset')
2570 # box.prop(prop, 'z_mode')
2573 if prop
.parts_expand
:
2574 row
.prop(prop
, 'parts_expand', icon
="TRIA_DOWN", text
="Parts", emboss
=False)
2575 if prop
.presets
== 'STAIR_USER':
2576 box
.prop(prop
, 'n_parts')
2577 if prop
.presets
!= 'STAIR_USER':
2578 row
= box
.row(align
=True)
2579 row
.prop(prop
, "left_shape", text
="")
2580 row
.prop(prop
, "right_shape", text
="")
2582 row
.prop(prop
, "radius")
2584 if prop
.presets
== 'STAIR_O':
2585 row
.prop(prop
, 'total_angle')
2587 row
.prop(prop
, 'da')
2588 if prop
.presets
!= 'STAIR_O':
2589 for i
, part
in enumerate(prop
.parts
):
2590 part
.draw(layout
, context
, i
, prop
.presets
== 'STAIR_USER')
2592 row
.prop(prop
, 'parts_expand', icon
="TRIA_RIGHT", text
="Parts", emboss
=False)
2596 if prop
.steps_expand
:
2597 row
.prop(prop
, 'steps_expand', icon
="TRIA_DOWN", text
="Steps", emboss
=False)
2598 box
.prop(prop
, 'steps_type')
2599 box
.prop(prop
, 'step_depth')
2600 box
.prop(prop
, 'nose_type')
2601 box
.prop(prop
, 'nose_z')
2602 box
.prop(prop
, 'nose_y')
2604 row
.prop(prop
, 'steps_expand', icon
="TRIA_RIGHT", text
="Steps", emboss
=False)
2607 row
= box
.row(align
=True)
2608 if prop
.handrail_expand
:
2609 row
.prop(prop
, 'handrail_expand', icon
="TRIA_DOWN", text
="Handrail", emboss
=False)
2611 row
.prop(prop
, 'handrail_expand', icon
="TRIA_RIGHT", text
="Handrail", emboss
=False)
2613 row
.prop(prop
, 'left_handrail')
2614 row
.prop(prop
, 'right_handrail')
2616 if prop
.handrail_expand
:
2617 box
.prop(prop
, 'handrail_alt')
2618 box
.prop(prop
, 'handrail_offset')
2619 box
.prop(prop
, 'handrail_extend')
2620 box
.prop(prop
, 'handrail_profil')
2621 if prop
.handrail_profil
!= 'CIRCLE':
2622 box
.prop(prop
, 'handrail_x')
2623 box
.prop(prop
, 'handrail_y')
2625 box
.prop(prop
, 'handrail_radius')
2626 row
= box
.row(align
=True)
2627 row
.prop(prop
, 'handrail_slice_left')
2628 row
.prop(prop
, 'handrail_slice_right')
2631 row
= box
.row(align
=True)
2632 if prop
.string_expand
:
2633 row
.prop(prop
, 'string_expand', icon
="TRIA_DOWN", text
="String", emboss
=False)
2635 row
.prop(prop
, 'string_expand', icon
="TRIA_RIGHT", text
="String", emboss
=False)
2636 row
.prop(prop
, 'left_string')
2637 row
.prop(prop
, 'right_string')
2638 if prop
.string_expand
:
2639 box
.prop(prop
, 'string_x')
2640 box
.prop(prop
, 'string_z')
2641 box
.prop(prop
, 'string_alt')
2642 box
.prop(prop
, 'string_offset')
2645 row
= box
.row(align
=True)
2646 if prop
.post_expand
:
2647 row
.prop(prop
, 'post_expand', icon
="TRIA_DOWN", text
="Post", emboss
=False)
2649 row
.prop(prop
, 'post_expand', icon
="TRIA_RIGHT", text
="Post", emboss
=False)
2650 row
.prop(prop
, 'left_post')
2651 row
.prop(prop
, 'right_post')
2652 if prop
.post_expand
:
2653 box
.prop(prop
, 'post_corners')
2654 if not prop
.post_corners
:
2655 box
.prop(prop
, 'post_spacing')
2656 box
.prop(prop
, 'post_x')
2657 box
.prop(prop
, 'post_y')
2658 box
.prop(prop
, 'post_z')
2659 box
.prop(prop
, 'post_alt')
2660 box
.prop(prop
, 'post_offset_x')
2661 row
= box
.row(align
=True)
2662 row
.prop(prop
, 'user_defined_post_enable', text
="")
2663 row
.prop_search(prop
, "user_defined_post", scene
, "objects", text
="")
2666 row
= box
.row(align
=True)
2667 if prop
.subs_expand
:
2668 row
.prop(prop
, 'subs_expand', icon
="TRIA_DOWN", text
="Subs", emboss
=False)
2670 row
.prop(prop
, 'subs_expand', icon
="TRIA_RIGHT", text
="Subs", emboss
=False)
2672 row
.prop(prop
, 'left_subs')
2673 row
.prop(prop
, 'right_subs')
2674 if prop
.subs_expand
:
2675 box
.prop(prop
, 'subs_spacing')
2676 box
.prop(prop
, 'subs_x')
2677 box
.prop(prop
, 'subs_y')
2678 box
.prop(prop
, 'subs_z')
2679 box
.prop(prop
, 'subs_alt')
2680 box
.prop(prop
, 'subs_offset_x')
2681 box
.prop(prop
, 'subs_bottom')
2682 row
= box
.row(align
=True)
2683 row
.prop(prop
, 'user_defined_subs_enable', text
="")
2684 row
.prop_search(prop
, "user_defined_subs", scene
, "objects", text
="")
2687 row
= box
.row(align
=True)
2688 if prop
.panel_expand
:
2689 row
.prop(prop
, 'panel_expand', icon
="TRIA_DOWN", text
="Panels", emboss
=False)
2691 row
.prop(prop
, 'panel_expand', icon
="TRIA_RIGHT", text
="Panels", emboss
=False)
2692 row
.prop(prop
, 'left_panel')
2693 row
.prop(prop
, 'right_panel')
2694 if prop
.panel_expand
:
2695 box
.prop(prop
, 'panel_dist')
2696 box
.prop(prop
, 'panel_x')
2697 box
.prop(prop
, 'panel_z')
2698 box
.prop(prop
, 'panel_alt')
2699 box
.prop(prop
, 'panel_offset_x')
2702 row
= box
.row(align
=True)
2703 if prop
.rail_expand
:
2704 row
.prop(prop
, 'rail_expand', icon
="TRIA_DOWN", text
="Rails", emboss
=False)
2706 row
.prop(prop
, 'rail_expand', icon
="TRIA_RIGHT", text
="Rails", emboss
=False)
2707 row
.prop(prop
, 'left_rail')
2708 row
.prop(prop
, 'right_rail')
2709 if prop
.rail_expand
:
2710 box
.prop(prop
, 'rail_n')
2711 for i
in range(prop
.rail_n
):
2713 box
.label(text
="Rail " + str(i
+ 1))
2714 box
.prop(prop
, 'rail_x', index
=i
)
2715 box
.prop(prop
, 'rail_z', index
=i
)
2716 box
.prop(prop
, 'rail_alt', index
=i
)
2717 box
.prop(prop
, 'rail_offset', index
=i
)
2718 box
.prop(prop
.rail_mat
[i
], 'index', text
="")
2723 if prop
.idmats_expand
:
2724 row
.prop(prop
, 'idmats_expand', icon
="TRIA_DOWN", text
="Materials", emboss
=False)
2725 box
.prop(prop
, 'idmat_top')
2726 box
.prop(prop
, 'idmat_side')
2727 box
.prop(prop
, 'idmat_bottom')
2728 box
.prop(prop
, 'idmat_step_side')
2729 box
.prop(prop
, 'idmat_step_front')
2730 box
.prop(prop
, 'idmat_raise')
2731 box
.prop(prop
, 'idmat_handrail')
2732 box
.prop(prop
, 'idmat_panel')
2733 box
.prop(prop
, 'idmat_post')
2734 box
.prop(prop
, 'idmat_subs')
2735 box
.prop(prop
, 'idmat_string')
2737 row
.prop(prop
, 'idmats_expand', icon
="TRIA_RIGHT", text
="Materials", emboss
=False)
2740 # ------------------------------------------------------------------
2741 # Define operator class to create object
2742 # ------------------------------------------------------------------
2745 class ARCHIPACK_OT_stair(ArchipackCreateTool
, Operator
):
2746 bl_idname
= "archipack.stair"
2748 bl_description
= "Create a Stair"
2749 bl_category
= 'Archipack'
2750 bl_options
= {'REGISTER', 'UNDO'}
2752 def create(self
, context
):
2753 m
= bpy
.data
.meshes
.new("Stair")
2754 o
= bpy
.data
.objects
.new("Stair", m
)
2755 d
= m
.archipack_stair
.add()
2756 self
.link_object_to_scene(context
, o
)
2757 o
.select_set(state
=True)
2758 context
.view_layer
.objects
.active
= o
2760 self
.add_material(o
)
2761 m
.auto_smooth_angle
= 0.20944
2764 # -----------------------------------------------------
2766 # -----------------------------------------------------
2767 def execute(self
, context
):
2768 if context
.mode
== "OBJECT":
2769 bpy
.ops
.object.select_all(action
="DESELECT")
2770 o
= self
.create(context
)
2771 o
.location
= context
.scene
.cursor
.location
2772 o
.select_set(state
=True)
2773 context
.view_layer
.objects
.active
= o
2777 self
.report({'WARNING'}, "Archipack: Option only valid in Object mode")
2778 return {'CANCELLED'}
2780 # ------------------------------------------------------------------
2781 # Define operator class to manipulate object
2782 # ------------------------------------------------------------------
2785 class ARCHIPACK_OT_stair_manipulate(Operator
):
2786 bl_idname
= "archipack.stair_manipulate"
2787 bl_label
= "Manipulate"
2788 bl_description
= "Manipulate"
2789 bl_options
= {'REGISTER', 'UNDO'}
2792 def poll(self
, context
):
2793 return archipack_stair
.filter(context
.active_object
)
2795 def invoke(self
, context
, event
):
2796 d
= archipack_stair
.datablock(context
.active_object
)
2797 d
.manipulable_invoke(context
)
2801 # ------------------------------------------------------------------
2802 # Define operator class to load / save presets
2803 # ------------------------------------------------------------------
2806 class ARCHIPACK_OT_stair_preset_menu(PresetMenuOperator
, Operator
):
2807 bl_description
= "Show Stair Presets"
2808 bl_idname
= "archipack.stair_preset_menu"
2809 bl_label
= "Stair style"
2810 preset_subdir
= "archipack_stair"
2813 class ARCHIPACK_OT_stair_preset(ArchipackPreset
, Operator
):
2814 """Add a Stair Preset"""
2815 bl_idname
= "archipack.stair_preset"
2816 bl_label
= "Add Stair Style"
2817 preset_menu
= "ARCHIPACK_OT_stair_preset_menu"
2820 def blacklist(self
):
2821 return ['manipulators']
2825 bpy
.utils
.register_class(archipack_stair_material
)
2826 bpy
.utils
.register_class(archipack_stair_part
)
2827 bpy
.utils
.register_class(archipack_stair
)
2828 Mesh
.archipack_stair
= CollectionProperty(type=archipack_stair
)
2829 bpy
.utils
.register_class(ARCHIPACK_PT_stair
)
2830 bpy
.utils
.register_class(ARCHIPACK_OT_stair
)
2831 bpy
.utils
.register_class(ARCHIPACK_OT_stair_preset_menu
)
2832 bpy
.utils
.register_class(ARCHIPACK_OT_stair_preset
)
2833 bpy
.utils
.register_class(ARCHIPACK_OT_stair_manipulate
)
2837 bpy
.utils
.unregister_class(archipack_stair_material
)
2838 bpy
.utils
.unregister_class(archipack_stair_part
)
2839 bpy
.utils
.unregister_class(archipack_stair
)
2840 del Mesh
.archipack_stair
2841 bpy
.utils
.unregister_class(ARCHIPACK_PT_stair
)
2842 bpy
.utils
.unregister_class(ARCHIPACK_OT_stair
)
2843 bpy
.utils
.unregister_class(ARCHIPACK_OT_stair_preset_menu
)
2844 bpy
.utils
.unregister_class(ARCHIPACK_OT_stair_preset
)
2845 bpy
.utils
.unregister_class(ARCHIPACK_OT_stair_manipulate
)