diff options
author | Thomas Dinges <blender@dingto.org> | 2021-11-23 11:24:55 +0300 |
---|---|---|
committer | Thomas Dinges <blender@dingto.org> | 2021-11-23 11:24:55 +0300 |
commit | d7517a6f2a69071eab53c02a645f7651ccfffd45 (patch) | |
tree | 7a496b8ada3e764b7e6c438e30d8c2be49fb95cf /archipack/archipack_stair.py | |
parent | 162cba016c8c11bcebea4d8d3cf80da9faf4ce76 (diff) |
Remove Archipack to reflect new key requirements.
https://wiki.blender.org/wiki/Process/Addons
Diffstat (limited to 'archipack/archipack_stair.py')
-rw-r--r-- | archipack/archipack_stair.py | 2845 |
1 files changed, 0 insertions, 2845 deletions
diff --git a/archipack/archipack_stair.py b/archipack/archipack_stair.py deleted file mode 100644 index 9fbcdfe2..00000000 --- a/archipack/archipack_stair.py +++ /dev/null @@ -1,2845 +0,0 @@ -# -*- coding:utf-8 -*- - -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110- 1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# ---------------------------------------------------------- -# Author: Stephen Leger (s-leger) -# -# ---------------------------------------------------------- -# noinspection PyUnresolvedReferences -import bpy -# noinspection PyUnresolvedReferences -from bpy.types import Operator, PropertyGroup, Mesh, Panel -from bpy.props import ( - FloatProperty, BoolProperty, IntProperty, CollectionProperty, - StringProperty, EnumProperty, FloatVectorProperty - ) -from .bmesh_utils import BmeshEdit as bmed -from .panel import Panel as Lofter -from mathutils import Vector, Matrix -from math import sin, cos, pi, floor, acos -from .archipack_manipulator import Manipulable, archipack_manipulator -from .archipack_2d import Line, Arc -from .archipack_preset import ArchipackPreset, PresetMenuOperator -from .archipack_object import ArchipackCreateTool, ArchipackObject - - -class Stair(): - def __init__(self, left_offset, right_offset, steps_type, nose_type, z_mode, nose_z, bottom_z): - self.steps_type = steps_type - self.nose_type = nose_type - self.l_shape = None - self.r_shape = None - self.next_type = 'NONE' - self.last_type = 'NONE' - self.z_mode = z_mode - # depth of open step - self.nose_z = nose_z - # size under the step on bottom - self.bottom_z = bottom_z - self.left_offset = left_offset - self.right_offset = right_offset - self.last_height = 0 - - def set_matids(self, matids): - self.idmat_top, self.idmat_step_front, self.idmat_raise, \ - self.idmat_side, self.idmat_bottom, self.idmat_step_side = matids - - def set_height(self, step_height, z0): - self.step_height = step_height - self.z0 = z0 - - @property - def height(self): - return self.n_step * self.step_height - - @property - def top_offset(self): - return self.t_step / self.step_depth - - @property - def top(self): - return self.z0 + self.height - - @property - def left_length(self): - return self.get_length("LEFT") - - @property - def right_length(self): - return self.get_length("RIGHT") - - def step_size(self, step_depth): - t_step, n_step = self.steps(step_depth) - self.n_step = n_step - self.t_step = t_step - self.step_depth = step_depth - return n_step - - def p3d_left(self, verts, p2d, i, t, landing=False): - x, y = p2d - nose_z = min(self.step_height, self.nose_z) - zl = self.z0 + t * self.height - zs = self.z0 + i * self.step_height - if self.z_mode == 'LINEAR': - z0 = max(0, zl) - z1 = z0 - self.bottom_z - verts.extend([(x, y, z0), (x, y, z1)]) - else: - if "FULL" in self.steps_type: - z0 = 0 - else: - z0 = max(0, zl - nose_z - self.bottom_z) - z3 = zs + max(0, self.step_height - nose_z) - z4 = zs + self.step_height - if landing: - if "FULL" in self.steps_type: - z2 = 0 - z1 = 0 - else: - z2 = max(0, min(z3, z3 - self.bottom_z)) - z1 = z2 - else: - z1 = min(z3, max(z0, zl - nose_z)) - z2 = min(z3, max(z1, zl)) - verts.extend([(x, y, z0), - (x, y, z1), - (x, y, z2), - (x, y, z3), - (x, y, z4)]) - - def p3d_right(self, verts, p2d, i, t, landing=False): - x, y = p2d - nose_z = min(self.step_height, self.nose_z) - zl = self.z0 + t * self.height - zs = self.z0 + i * self.step_height - if self.z_mode == 'LINEAR': - z0 = max(0, zl) - z1 = z0 - self.bottom_z - verts.extend([(x, y, z1), (x, y, z0)]) - else: - if "FULL" in self.steps_type: - z0 = 0 - else: - z0 = max(0, zl - nose_z - self.bottom_z) - z3 = zs + max(0, self.step_height - nose_z) - z4 = zs + self.step_height - if landing: - if "FULL" in self.steps_type: - z2 = 0 - z1 = 0 - else: - z2 = max(0, min(z3, z3 - self.bottom_z)) - z1 = z2 - else: - z1 = min(z3, max(z0, zl - nose_z)) - z2 = min(z3, max(z1, zl)) - verts.extend([(x, y, z4), - (x, y, z3), - (x, y, z2), - (x, y, z1), - (x, y, z0)]) - - def p3d_cstep_left(self, verts, p2d, i, t): - x, y = p2d - nose_z = min(self.step_height, self.nose_z) - zs = self.z0 + i * self.step_height - z3 = zs + max(0, self.step_height - nose_z) - z1 = min(z3, zs - nose_z) - verts.append((x, y, z1)) - verts.append((x, y, z3)) - - def p3d_cstep_right(self, verts, p2d, i, t): - x, y = p2d - nose_z = min(self.step_height, self.nose_z) - zs = self.z0 + i * self.step_height - z3 = zs + max(0, self.step_height - nose_z) - z1 = min(z3, zs - nose_z) - verts.append((x, y, z3)) - verts.append((x, y, z1)) - - def straight_stair(self, length): - self.next_type = 'STAIR' - s = self.straight(length) - return StraightStair(s.p, s.v, self.left_offset, self.right_offset, self.steps_type, - self.nose_type, self.z_mode, self.nose_z, self.bottom_z) - - def straight_landing(self, length, last_type='STAIR'): - self.next_type = 'LANDING' - s = self.straight(length) - return StraightLanding(s.p, s.v, self.left_offset, self.right_offset, self.steps_type, - self.nose_type, self.z_mode, self.nose_z, self.bottom_z, last_type=last_type) - - def curved_stair(self, da, radius, left_shape, right_shape, double_limit=pi): - self.next_type = 'STAIR' - n = self.normal(1) - n.v = radius * n.v.normalized() - if da < 0: - n.v = -n.v - a0 = n.angle - c = n.p - n.v - return CurvedStair(c, radius, a0, da, self.left_offset, self.right_offset, - self.steps_type, self.nose_type, self.z_mode, self.nose_z, self.bottom_z, - left_shape, right_shape, double_limit=double_limit) - - def curved_landing(self, da, radius, left_shape, right_shape, double_limit=pi, last_type='STAIR'): - self.next_type = 'LANDING' - n = self.normal(1) - n.v = radius * n.v.normalized() - if da < 0: - n.v = -n.v - a0 = n.angle - c = n.p - n.v - return CurvedLanding(c, radius, a0, da, self.left_offset, self.right_offset, - self.steps_type, self.nose_type, self.z_mode, self.nose_z, self.bottom_z, - left_shape, right_shape, double_limit=double_limit, last_type=last_type) - - def get_z(self, t, mode): - if mode == 'LINEAR': - return self.z0 + t * self.height - else: - step = 1 + floor(t / self.t_step) - return self.z0 + step * self.step_height - - def make_profile(self, t, side, profile, verts, faces, matids, next=None, tnext=0): - z0 = self.get_z(t, 'LINEAR') - dz1 = 0 - t, part, dz0, shape = self.get_part(t, side) - if next is not None: - tnext, next, dz1, shape1 = next.get_part(tnext, side) - xy, s = part.proj_xy(t, next) - v_xy = s * xy.to_3d() - z, s = part.proj_z(t, dz0, next, dz1) - v_z = s * Vector((-xy.y * z.x, xy.x * z.x, z.y)) - x, y = part.lerp(t) - verts += [Vector((x, y, z0)) + v.x * v_xy + v.y * v_z for v in profile] - - def project_uv(self, rM, uvs, verts, indexes, up_axis='Z'): - if up_axis == 'Z': - uvs.append([(rM @ Vector(verts[i])).to_2d() for i in indexes]) - elif up_axis == 'Y': - uvs.append([(x, z) for x, y, z in [(rM @ Vector(verts[i])) for i in indexes]]) - else: - uvs.append([(y, z) for x, y, z in [(rM @ Vector(verts[i])) for i in indexes]]) - - def get_proj_matrix(self, part, t, nose_y): - # a matrix to project verts - # into uv space for horizontal parts of this step - # so uv = (rM @ vertex).to_2d() - tl = t - nose_y / self.get_length("LEFT") - tr = t - nose_y / self.get_length("RIGHT") - t2, part, dz, shape = self.get_part(tl, "LEFT") - p0 = part.lerp(t2) - t2, part, dz, shape = self.get_part(tr, "RIGHT") - p1 = part.lerp(t2) - v = (p1 - p0).normalized() - return Matrix([ - [-v.y, v.x, 0, p0.x], - [v.x, v.y, 0, p0.y], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]).inverted() - - def _make_nose(self, i, s, verts, faces, matids, uvs, nose_y): - - t = self.t_step * i - - # a matrix to project verts - # into uv space for horizontal parts of this step - # so uv = (rM @ vertex).to_2d() - rM = self.get_proj_matrix(self, t, nose_y) - - if self.z_mode == 'LINEAR': - return rM - - f = len(verts) - - tl = t - nose_y / self.get_length("LEFT") - tr = t - nose_y / self.get_length("RIGHT") - - t2, part, dz, shape = self.get_part(tl, "LEFT") - p0 = part.lerp(t2) - self.p3d_left(verts, p0, s, t2) - - t2, part, dz, shape = self.get_part(tr, "RIGHT") - p1 = part.lerp(t2) - self.p3d_right(verts, p1, s, t2) - - start = 3 - end = 6 - offset = 10 - - # left, top, right - matids.extend([self.idmat_step_side, - self.idmat_top, - self.idmat_step_side]) - - faces += [(f + j, f + j + 1, f + j + offset + 1, f + j + offset) for j in range(start, end)] - - u = nose_y - v = (p1 - p0).length - w = verts[f + 2][2] - verts[f + 3][2] - s = int((end - start) / 2) - - uvs += [[(u, verts[f + j][2]), (u, verts[f + j + 1][2]), - (0, verts[f + j + 1][2]), (0, verts[f + j][2])] for j in range(start, start + s)] - - uvs.append([(0, 0), (0, v), (u, v), (u, 0)]) - - uvs += [[(u, verts[f + j][2]), (u, verts[f + j + 1][2]), - (0, verts[f + j + 1][2]), (0, verts[f + j][2])] for j in range(start + s + 1, end)] - - if 'STRAIGHT' in self.nose_type or 'OPEN' in self.steps_type: - # face bottom - matids.append(self.idmat_bottom) - faces.append((f + end, f + start, f + offset + start, f + offset + end)) - uvs.append([(u, v), (u, 0), (0, 0), (0, v)]) - - if self.steps_type != 'OPEN': - if 'STRAIGHT' in self.nose_type: - # front face bottom straight - matids.append(self.idmat_raise) - faces.append((f + 12, f + 17, f + 16, f + 13)) - uvs.append([(0, w), (v, w), (v, 0), (0, 0)]) - - elif 'OBLIQUE' in self.nose_type: - # front face bottom oblique - matids.append(self.idmat_raise) - faces.append((f + 12, f + 17, f + 6, f + 3)) - - uvs.append([(0, w), (v, w), (v, 0), (0, 0)]) - - matids.append(self.idmat_side) - faces.append((f + 3, f + 13, f + 12)) - uvs.append([(0, 0), (u, 0), (u, w)]) - - matids.append(self.idmat_side) - faces.append((f + 6, f + 17, f + 16)) - uvs.append([(0, 0), (u, w), (u, 0)]) - - # front face top - w = verts[f + 3][2] - verts[f + 4][2] - matids.append(self.idmat_step_front) - faces.append((f + 4, f + 3, f + 6, f + 5)) - uvs.append([(0, 0), (0, w), (v, w), (v, 0)]) - return rM - - def make_faces(self, f, rM, verts, faces, matids, uvs): - - if self.z_mode == 'LINEAR': - start = 0 - end = 3 - offset = 4 - matids.extend([self.idmat_side, - self.idmat_top, - self.idmat_side, - self.idmat_bottom]) - elif "OPEN" in self.steps_type: - # faces dessus-dessous-lateral marches fermees - start = 3 - end = 6 - offset = 10 - matids.extend([self.idmat_step_side, - self.idmat_top, - self.idmat_step_side, - self.idmat_bottom]) - else: - # faces dessus-dessous-lateral marches fermees - start = 0 - end = 9 - offset = 10 - matids.extend([self.idmat_side, - self.idmat_side, - self.idmat_side, - self.idmat_step_side, - self.idmat_top, - self.idmat_step_side, - self.idmat_side, - self.idmat_side, - self.idmat_side, - self.idmat_bottom]) - - u_l0 = 0 - u_l1 = self.t_step * self.left_length - u_r0 = 0 - u_r1 = self.t_step * self.right_length - - s = int((end - start) / 2) - uvs += [[(u_l0, verts[f + j][2]), (u_l0, verts[f + j + 1][2]), - (u_l1, verts[f + j + offset + 1][2]), (u_l1, verts[f + j + offset][2])] for j in range(start, start + s)] - - self.project_uv(rM, uvs, verts, [f + start + s, f + start + s + 1, - f + start + s + offset + 1, f + start + s + offset]) - - uvs += [[(u_r0, verts[f + j][2]), (u_r0, verts[f + j + 1][2]), - (u_r1, verts[f + j + offset + 1][2]), (u_r1, verts[f + j + offset][2])] for j in range(start + s + 1, end)] - - self.project_uv(rM, uvs, verts, [f + end, f + start, f + offset + start, f + offset + end]) - - faces += [(f + j, f + j + 1, f + j + offset + 1, f + j + offset) for j in range(start, end)] - faces.append((f + end, f + start, f + offset + start, f + offset + end)) - - -class StraightStair(Stair, Line): - def __init__(self, p, v, left_offset, right_offset, steps_type, nose_type, z_mode, nose_z, bottom_z): - Stair.__init__(self, left_offset, right_offset, steps_type, nose_type, z_mode, nose_z, bottom_z) - Line.__init__(self, p, v) - self.l_line = self.offset(-left_offset) - self.r_line = self.offset(right_offset) - - def make_step(self, i, verts, faces, matids, uvs, nose_y=0): - - rM = self._make_nose(i, i, verts, faces, matids, uvs, nose_y) - - t0 = self.t_step * i - - f = len(verts) - - p = self.l_line.lerp(t0) - self.p3d_left(verts, p, i, t0) - p = self.r_line.lerp(t0) - self.p3d_right(verts, p, i, t0) - - t1 = t0 + self.t_step - - p = self.l_line.lerp(t1) - self.p3d_left(verts, p, i, t1) - p = self.r_line.lerp(t1) - self.p3d_right(verts, p, i, t1) - - self.make_faces(f, rM, verts, faces, matids, uvs) - - if "OPEN" in self.steps_type: - faces.append((f + 13, f + 14, f + 15, f + 16)) - matids.append(self.idmat_step_front) - uvs.append([(0, 0), (0, 1), (1, 1), (1, 0)]) - - def get_length(self, side): - return self.length - - def get_lerp_vect(self, posts, side, i, t_step, respect_edges, z_offset=0, t0_abs=None): - if t0_abs is not None: - t0 = t0_abs - else: - t0 = i * t_step - t, part, dz, shape = self.get_part(t0, side) - dz /= part.length - n = part.normal(t) - z0 = self.get_z(t0, 'STEP') - z1 = self.get_z(t0, 'LINEAR') - posts.append((n, dz, z0, z1 + t0 * z_offset)) - return [t0] - - def n_posts(self, post_spacing, side, respect_edges): - return self.steps(post_spacing) - - def get_part(self, t, side): - if side == 'LEFT': - part = self.l_line - else: - part = self.r_line - return t, part, self.height, 'LINE' - - -class CurvedStair(Stair, Arc): - def __init__(self, c, radius, a0, da, left_offset, right_offset, steps_type, nose_type, - z_mode, nose_z, bottom_z, left_shape, right_shape, double_limit=pi): - - Stair.__init__(self, left_offset, right_offset, steps_type, nose_type, z_mode, nose_z, bottom_z) - Arc.__init__(self, c, radius, a0, da) - self.l_shape = left_shape - self.r_shape = right_shape - self.edges_multiples = round(abs(da), 6) > double_limit - # left arc, tangent at start and end - self.l_arc, self.l_t0, self.l_t1, self.l_tc = self.set_offset(-left_offset, left_shape) - self.r_arc, self.r_t0, self.r_t1, self.r_tc = self.set_offset(right_offset, right_shape) - - def set_offset(self, offset, shape): - arc = self.offset(offset) - t0 = arc.tangeant(0, 1) - t1 = arc.tangeant(1, 1) - tc = arc.tangeant(0.5, 1) - if self.edges_multiples: - i, p, t = t0.intersect(tc) - tc.v *= 2 * t - tc.p = p - i, p, t2 = tc.intersect(t1) - else: - i, p, t = t0.intersect(t1) - t0.v *= t - t1.p = p - t1.v *= t - return arc, t0, t1, tc - - def get_length(self, side): - if side == 'RIGHT': - arc = self.r_arc - shape = self.r_shape - t0 = self.r_t0 - else: - arc = self.l_arc - shape = self.l_shape - t0 = self.l_t0 - if shape == 'CIRCLE': - return arc.length - else: - if self.edges_multiples: - # two edges - return t0.length * 4 - else: - return t0.length * 2 - - def _make_step(self, t_step, i, s, verts, landing=False): - - tb = t_step * i - - f = len(verts) - - t, part, dz, shape = self.get_part(tb, "LEFT") - p = part.lerp(t) - self.p3d_left(verts, p, s, tb, landing) - - t, part, dz, shape = self.get_part(tb, "RIGHT") - p = part.lerp(t) - self.p3d_right(verts, p, s, tb, landing) - return f - - def _make_edge(self, t_step, i, j, f, rM, verts, faces, matids, uvs): - tb = t_step * i - # make edges verts after regular ones - if self.l_shape != 'CIRCLE' or self.r_shape != 'CIRCLE': - if self.edges_multiples: - # edge 1 - if tb < 0.25 and tb + t_step > 0.25: - f0 = f - f = len(verts) - if self.l_shape == 'CIRCLE': - self.p3d_left(verts, self.l_arc.lerp(0.25), j, 0.25) - else: - self.p3d_left(verts, self.l_tc.p, j, 0.25) - if self.r_shape == 'CIRCLE': - self.p3d_right(verts, self.r_arc.lerp(0.25), j, 0.25) - else: - self.p3d_right(verts, self.r_tc.p, j, 0.25) - self.make_faces(f0, rM, verts, faces, matids, uvs) - # edge 2 - if tb < 0.75 and tb + t_step > 0.75: - f0 = f - f = len(verts) - if self.l_shape == 'CIRCLE': - self.p3d_left(verts, self.l_arc.lerp(0.75), j, 0.75) - else: - self.p3d_left(verts, self.l_t1.p, j, 0.75) - if self.r_shape == 'CIRCLE': - self.p3d_right(verts, self.r_arc.lerp(0.75), j, 0.75) - else: - self.p3d_right(verts, self.r_t1.p, j, 0.75) - self.make_faces(f0, rM, verts, faces, matids, uvs) - else: - if tb < 0.5 and tb + t_step > 0.5: - f0 = f - f = len(verts) - # the step goes through the edge - if self.l_shape == 'CIRCLE': - self.p3d_left(verts, self.l_arc.lerp(0.5), j, 0.5) - else: - self.p3d_left(verts, self.l_t1.p, j, 0.5) - if self.r_shape == 'CIRCLE': - self.p3d_right(verts, self.r_arc.lerp(0.5), j, 0.5) - else: - self.p3d_right(verts, self.r_t1.p, j, 0.5) - self.make_faces(f0, rM, verts, faces, matids, uvs) - return f - - def make_step(self, i, verts, faces, matids, uvs, nose_y=0): - - # open stair with closed face - - # step nose - rM = self._make_nose(i, i, verts, faces, matids, uvs, nose_y) - f = 0 - if self.l_shape == 'CIRCLE' or self.r_shape == 'CIRCLE': - # every 6 degree - n_subs = max(1, int(abs(self.da) / pi * 30 / self.n_step)) - t_step = self.t_step / n_subs - for j in range(n_subs): - f0 = f - f = self._make_step(t_step, n_subs * i + j, i, verts) - if j > 0: - self.make_faces(f0, rM, verts, faces, matids, uvs) - f = self._make_edge(t_step, n_subs * i + j, i, f, rM, verts, faces, matids, uvs) - else: - f = self._make_step(self.t_step, i, i, verts) - f = self._make_edge(self.t_step, i, i, f, rM, verts, faces, matids, uvs) - - self._make_step(self.t_step, i + 1, i, verts) - self.make_faces(f, rM, verts, faces, matids, uvs) - - if "OPEN" in self.steps_type and self.z_mode != 'LINEAR': - # back face top - faces.append((f + 13, f + 14, f + 15, f + 16)) - matids.append(self.idmat_step_front) - uvs.append([(0, 0), (0, 1), (1, 1), (1, 0)]) - - def get_part(self, t, side): - if side == 'RIGHT': - arc = self.r_arc - shape = self.r_shape - t0, t1, tc = self.r_t0, self.r_t1, self.r_tc - else: - arc = self.l_arc - shape = self.l_shape - t0, t1, tc = self.l_t0, self.l_t1, self.l_tc - if shape == 'CIRCLE': - return t, arc, self.height, shape - else: - if self.edges_multiples: - # two edges - if t <= 0.25: - return 4 * t, t0, 0.25 * self.height, shape - elif t <= 0.75: - return 2 * (t - 0.25), tc, 0.5 * self.height, shape - else: - return 4 * (t - 0.75), t1, 0.25 * self.height, shape - else: - if t <= 0.5: - return 2 * t, t0, 0.5 * self.height, shape - else: - return 2 * (t - 0.5), t1, 0.5 * self.height, shape - - def get_lerp_vect(self, posts, side, i, t_step, respect_edges, z_offset=0, t0_abs=None): - if t0_abs is not None: - t0 = t0_abs - else: - t0 = i * t_step - res = [t0] - t1 = t0 + t_step - zs = self.get_z(t0, 'STEP') - zl = self.get_z(t0, 'LINEAR') - - # vect normal - t, part, dz, shape = self.get_part(t0, side) - n = part.normal(t) - dz /= part.length - posts.append((n, dz, zs, zl + t0 * z_offset)) - - if shape != 'CIRCLE' and respect_edges: - if self.edges_multiples: - if t0 < 0.25 and t1 > 0.25: - zs = self.get_z(0.25, 'STEP') - zl = self.get_z(0.25, 'LINEAR') - t, part, dz, shape = self.get_part(0.25, side) - n = part.normal(1) - posts.append((n, dz, zs, zl + 0.25 * z_offset)) - res.append(0.25) - if t0 < 0.75 and t1 > 0.75: - zs = self.get_z(0.75, 'STEP') - zl = self.get_z(0.75, 'LINEAR') - t, part, dz, shape = self.get_part(0.75, side) - n = part.normal(1) - posts.append((n, dz, zs, zl + 0.75 * z_offset)) - res.append(0.75) - elif t0 < 0.5 and t1 > 0.5: - zs = self.get_z(0.5, 'STEP') - zl = self.get_z(0.5, 'LINEAR') - t, part, dz, shape = self.get_part(0.5, side) - n = part.normal(1) - posts.append((n, dz, zs, zl + 0.5 * z_offset)) - res.append(0.5) - return res - - def n_posts(self, post_spacing, side, respect_edges): - if side == 'LEFT': - arc, t0, shape = self.l_arc, self.l_t0, self.l_shape - else: - arc, t0, shape = self.r_arc, self.r_t0, self.r_shape - step_factor = 1 - if shape == 'CIRCLE': - length = arc.length - else: - if self.edges_multiples: - if respect_edges: - step_factor = 2 - length = 4 * t0.length - else: - length = 2 * t0.length - steps = step_factor * max(1, round(length / post_spacing, 0)) - # print("respect_edges:%s t_step:%s n_step:%s" % (respect_edges, 1.0 / steps, int(steps))) - return 1.0 / steps, int(steps) - - -class StraightLanding(StraightStair): - def __init__(self, p, v, left_offset, right_offset, steps_type, - nose_type, z_mode, nose_z, bottom_z, last_type='STAIR'): - - StraightStair.__init__(self, p, v, left_offset, right_offset, steps_type, - nose_type, z_mode, nose_z, bottom_z) - - self.last_type = last_type - - @property - def height(self): - return 0 - - @property - def top_offset(self): - return self.t_step / self.v.length - - @property - def top(self): - if self.next_type == 'LANDING': - return self.z0 - else: - return self.z0 + self.step_height - - def step_size(self, step_depth): - self.n_step = 1 - self.t_step = 1 - self.step_depth = step_depth - if self.last_type == 'LANDING': - return 0 - else: - return 1 - - def make_step(self, i, verts, faces, matids, uvs, nose_y=0): - - if i == 0 and self.last_type != 'LANDING': - rM = self._make_nose(i, 0, verts, faces, matids, uvs, nose_y) - else: - rM = self.get_proj_matrix(self.l_line, self.t_step * i, nose_y) - - f = len(verts) - j = 0 - t0 = self.t_step * i - - p = self.l_line.lerp(t0) - self.p3d_left(verts, p, j, t0) - - p = self.r_line.lerp(t0) - self.p3d_right(verts, p, j, t0) - - t1 = t0 + self.t_step - p = self.l_line.lerp(t1) - self.p3d_left(verts, p, j, t1, self.next_type != 'LANDING') - - p = self.r_line.lerp(t1) - self.p3d_right(verts, p, j, t1, self.next_type != 'LANDING') - - self.make_faces(f, rM, verts, faces, matids, uvs) - - if "OPEN" in self.steps_type and self.next_type != 'LANDING': - faces.append((f + 13, f + 14, f + 15, f + 16)) - matids.append(self.idmat_step_front) - uvs.append([(0, 0), (0, 1), (1, 1), (1, 0)]) - - def straight_landing(self, length): - return Stair.straight_landing(self, length, last_type='LANDING') - - def curved_landing(self, da, radius, left_shape, right_shape, double_limit=pi): - return Stair.curved_landing(self, da, radius, left_shape, - right_shape, double_limit=double_limit, last_type='LANDING') - - def get_z(self, t, mode): - if mode == 'STEP': - return self.z0 + self.step_height - else: - return self.z0 - - -class CurvedLanding(CurvedStair): - def __init__(self, c, radius, a0, da, left_offset, right_offset, steps_type, - nose_type, z_mode, nose_z, bottom_z, left_shape, right_shape, double_limit=pi, last_type='STAIR'): - - CurvedStair.__init__(self, c, radius, a0, da, left_offset, right_offset, steps_type, - nose_type, z_mode, nose_z, bottom_z, left_shape, right_shape, double_limit=double_limit) - - self.last_type = last_type - - @property - def top_offset(self): - if self.l_shape == 'CIRCLE' or self.r_shape == 'CIRCLE': - return self.t_step / self.step_depth - else: - if self.edges_multiples: - return 0.5 / self.length - else: - return 1 / self.length - - @property - def height(self): - return 0 - - @property - def top(self): - if self.next_type == 'LANDING': - return self.z0 - else: - return self.z0 + self.step_height - - def step_size(self, step_depth): - if self.l_shape == 'CIRCLE' or self.r_shape == 'CIRCLE': - t_step, n_step = self.steps(step_depth) - else: - if self.edges_multiples: - t_step, n_step = 0.5, 2 - else: - t_step, n_step = 1, 1 - self.n_step = n_step - self.t_step = t_step - self.step_depth = step_depth - if self.last_type == 'LANDING': - return 0 - else: - return 1 - - def make_step(self, i, verts, faces, matids, uvs, nose_y=0): - - if i == 0 and 'LANDING' not in self.last_type: - rM = self._make_nose(i, 0, verts, faces, matids, uvs, nose_y) - else: - rM = self.get_proj_matrix(self.l_arc, self.t_step * i, nose_y) - - f = len(verts) - - if self.l_shape == 'CIRCLE' or self.r_shape == 'CIRCLE': - n_subs = max(1, int(abs(self.da / pi * 30 / self.n_step))) - t_step = self.t_step / n_subs - for j in range(n_subs): - f0 = f - f = self._make_step(t_step, n_subs * i + j, 0, verts) - if j > 0: - self.make_faces(f0, rM, verts, faces, matids, uvs) - f = self._make_edge(t_step, n_subs * i + j, 0, f, rM, verts, faces, matids, uvs) - else: - f = self._make_step(self.t_step, i, 0, verts) - f = self._make_edge(self.t_step, i, 0, f, rM, verts, faces, matids, uvs) - - self._make_step(self.t_step, i + 1, 0, verts, i == self.n_step - 1 and 'LANDING' not in self.next_type) - self.make_faces(f, rM, verts, faces, matids, uvs) - - if "OPEN" in self.steps_type and 'LANDING' not in self.next_type: - faces.append((f + 13, f + 14, f + 15, f + 16)) - matids.append(self.idmat_step_front) - uvs.append([(0, 0), (0, 1), (1, 1), (1, 0)]) - - def straight_landing(self, length): - return Stair.straight_landing(self, length, last_type='LANDING') - - def curved_landing(self, da, radius, left_shape, right_shape, double_limit=pi): - return Stair.curved_landing(self, da, radius, left_shape, - right_shape, double_limit=double_limit, last_type='LANDING') - - def get_z(self, t, mode): - if mode == 'STEP': - return self.z0 + self.step_height - else: - return self.z0 - - -class StairGenerator(): - def __init__(self, parts): - self.parts = parts - self.last_type = 'NONE' - self.stairs = [] - self.steps_type = 'NONE' - self.sum_da = 0 - self.user_defined_post = None - self.user_defined_uvs = None - self.user_defined_mat = None - - def add_part(self, type, steps_type, nose_type, z_mode, nose_z, bottom_z, center, - radius, da, width_left, width_right, length, left_shape, right_shape): - - self.steps_type = steps_type - if len(self.stairs) < 1: - s = None - else: - s = self.stairs[-1] - - if "S_" not in type: - self.sum_da += da - - # start a new stair - if s is None: - if type == 'S_STAIR': - p = Vector((0, 0)) - v = Vector((0, length)) - s = StraightStair(p, v, width_left, width_right, steps_type, nose_type, z_mode, nose_z, bottom_z) - elif type == 'C_STAIR': - if da < 0: - c = Vector((radius, 0)) - else: - c = Vector((-radius, 0)) - s = CurvedStair(c, radius, 0, da, width_left, width_right, steps_type, - nose_type, z_mode, nose_z, bottom_z, left_shape, right_shape) - elif type == 'D_STAIR': - if da < 0: - c = Vector((radius, 0)) - else: - c = Vector((-radius, 0)) - s = CurvedStair(c, radius, 0, da, width_left, width_right, steps_type, - nose_type, z_mode, nose_z, bottom_z, left_shape, right_shape, double_limit=0) - elif type == 'S_LANDING': - p = Vector((0, 0)) - v = Vector((0, length)) - s = StraightLanding(p, v, width_left, width_right, steps_type, nose_type, z_mode, nose_z, bottom_z) - elif type == 'C_LANDING': - if da < 0: - c = Vector((radius, 0)) - else: - c = Vector((-radius, 0)) - s = CurvedLanding(c, radius, 0, da, width_left, width_right, steps_type, - nose_type, z_mode, nose_z, bottom_z, left_shape, right_shape) - elif type == 'D_LANDING': - if da < 0: - c = Vector((radius, 0)) - else: - c = Vector((-radius, 0)) - s = CurvedLanding(c, radius, 0, da, width_left, width_right, steps_type, - nose_type, z_mode, nose_z, bottom_z, left_shape, right_shape, double_limit=0) - else: - if type == 'S_STAIR': - s = s.straight_stair(length) - elif type == 'C_STAIR': - s = s.curved_stair(da, radius, left_shape, right_shape) - elif type == 'D_STAIR': - s = s.curved_stair(da, radius, left_shape, right_shape, double_limit=0) - elif type == 'S_LANDING': - s = s.straight_landing(length) - elif type == 'C_LANDING': - s = s.curved_landing(da, radius, left_shape, right_shape) - elif type == 'D_LANDING': - s = s.curved_landing(da, radius, left_shape, right_shape, double_limit=0) - self.stairs.append(s) - self.last_type = type - - def n_steps(self, step_depth): - n_steps = 0 - for stair in self.stairs: - n_steps += stair.step_size(step_depth) - return n_steps - - def set_height(self, step_height): - z = 0 - for stair in self.stairs: - stair.set_height(step_height, z) - z = stair.top - - def make_stair(self, height, step_depth, verts, faces, matids, uvs, nose_y=0): - n_steps = self.n_steps(step_depth) - self.set_height(height / n_steps) - - for s, stair in enumerate(self.stairs): - if s < len(self.parts): - manipulator = self.parts[s].manipulators[0] - # Store Gl Points for manipulators - if 'Curved' in type(stair).__name__: - c = stair.c - p0 = (stair.p0 - c).to_3d() - p1 = (stair.p1 - c).to_3d() - manipulator.set_pts([(c.x, c.y, stair.top), p0, p1]) - manipulator.type_key = 'ARC_ANGLE_RADIUS' - manipulator.prop1_name = 'da' - manipulator.prop2_name = 'radius' - else: - if self.sum_da > 0: - side = 1 - else: - side = -1 - v0 = stair.p0 - v1 = stair.p1 - manipulator.set_pts([(v0.x, v0.y, stair.top), (v1.x, v1.y, stair.top), (side, 0, 0)]) - manipulator.type_key = 'SIZE' - manipulator.prop1_name = 'length' - - for i in range(stair.n_step): - stair.make_step(i, verts, faces, matids, uvs, nose_y=nose_y) - if s < len(self.stairs) - 1 and self.steps_type != 'OPEN' and \ - 'Landing' in type(stair).__name__ and stair.next_type != "LANDING": - f = len(verts) - 10 - faces.append((f, f + 1, f + 8, f + 9)) - matids.append(self.stairs[-1].idmat_bottom) - u = verts[f + 1][2] - verts[f][2] - v = (Vector(verts[f]) - Vector(verts[f + 9])).length - uvs.append([(0, 0), (0, u), (v, u), (v, 0)]) - - if self.steps_type != 'OPEN' and len(self.stairs) > 0: - f = len(verts) - 10 - faces.append((f, f + 1, f + 2, f + 3, f + 4, f + 5, f + 6, f + 7, f + 8, f + 9)) - matids.append(self.stairs[-1].idmat_bottom) - uvs.append([(0, 0), (.1, 0), (.2, 0), (.3, 0), (.4, 0), (.4, 1), (.3, 1), (.2, 1), (.1, 1), (0, 1)]) - - def setup_user_defined_post(self, o, post_x, post_y, post_z): - self.user_defined_post = o - x = o.bound_box[6][0] - o.bound_box[0][0] - y = o.bound_box[6][1] - o.bound_box[0][1] - z = o.bound_box[6][2] - o.bound_box[0][2] - self.user_defined_post_scale = Vector((post_x / x, post_y / -y, post_z / z)) - m = o.data - # create vertex group lookup dictionary for names - vgroup_names = {vgroup.index: vgroup.name for vgroup in o.vertex_groups} - # create dictionary of vertex group assignments per vertex - self.vertex_groups = [[vgroup_names[g.group] for g in v.groups] for v in m.vertices] - # uvs - uv_act = m.uv_layers.active - if uv_act is not None: - uv_layer = uv_act.data - self.user_defined_uvs = [[uv_layer[li].uv for li in p.loop_indices] for p in m.polygons] - else: - self.user_defined_uvs = [[(0, 0) for i in p.vertices] for p in m.polygons] - # material ids - self.user_defined_mat = [p.material_index for p in m.polygons] - - def get_user_defined_post(self, tM, z0, z1, z2, slope, post_z, verts, faces, matids, uvs): - f = len(verts) - m = self.user_defined_post.data - for i, g in enumerate(self.vertex_groups): - co = m.vertices[i].co.copy() - co.x *= self.user_defined_post_scale.x - co.y *= self.user_defined_post_scale.y - co.z *= self.user_defined_post_scale.z - if 'Top' in g: - co.z += z2 - elif 'Bottom' in g: - co.z += 0 - else: - co.z += z1 - if 'Slope' in g: - co.z += co.y * slope - verts.append(tM @ co) - matids += self.user_defined_mat - faces += [tuple([i + f for i in p.vertices]) for p in m.polygons] - uvs += self.user_defined_uvs - - def get_post(self, post, post_x, post_y, post_z, post_alt, sub_offset_x, - id_mat, verts, faces, matids, uvs, bottom="STEP"): - - n, dz, zs, zl = post - slope = dz * post_y - - if self.user_defined_post is not None: - if bottom == "STEP": - z0 = zs - else: - z0 = zl - z1 = zl - z0 - z2 = zl - z0 - x, y = -n.v.normalized() - tM = Matrix([ - [x, y, 0, n.p.x], - [y, -x, 0, n.p.y], - [0, 0, 1, z0 + post_alt], - [0, 0, 0, 1] - ]) - self.get_user_defined_post(tM, z0, z1, z2, dz, post_z, verts, faces, matids, uvs) - return - - z3 = zl + post_z + post_alt - slope - z4 = zl + post_z + post_alt + slope - if bottom == "STEP": - z0 = zs + post_alt - z1 = zs + post_alt - else: - z0 = zl + post_alt - slope - z1 = zl + post_alt + slope - vn = n.v.normalized() - dx = post_x * vn - dy = post_y * Vector((vn.y, -vn.x)) - oy = sub_offset_x * vn - x0, y0 = n.p - dx + dy + oy - x1, y1 = n.p - dx - dy + oy - x2, y2 = n.p + dx - dy + oy - x3, y3 = n.p + dx + dy + oy - f = len(verts) - verts.extend([(x0, y0, z0), (x0, y0, z3), - (x1, y1, z1), (x1, y1, z4), - (x2, y2, z1), (x2, y2, z4), - (x3, y3, z0), (x3, y3, z3)]) - faces.extend([(f, f + 1, f + 3, f + 2), - (f + 2, f + 3, f + 5, f + 4), - (f + 4, f + 5, f + 7, f + 6), - (f + 6, f + 7, f + 1, f), - (f, f + 2, f + 4, f + 6), - (f + 7, f + 5, f + 3, f + 1)]) - matids.extend([id_mat, id_mat, id_mat, id_mat, id_mat, id_mat]) - x = [(0, 0), (0, post_z), (post_x, post_z), (post_x, 0)] - y = [(0, 0), (0, post_z), (post_y, post_z), (post_y, 0)] - z = [(0, 0), (post_x, 0), (post_x, post_y), (0, post_y)] - uvs.extend([x, y, x, y, z, z]) - - def get_panel(self, subs, altitude, panel_x, panel_z, sub_offset_x, idmat, verts, faces, matids, uvs): - n_subs = len(subs) - if n_subs < 1: - return - f = len(verts) - x0 = sub_offset_x - 0.5 * panel_x - x1 = sub_offset_x + 0.5 * panel_x - z0 = 0 - z1 = panel_z - profile = [Vector((x0, z0)), Vector((x1, z0)), Vector((x1, z1)), Vector((x0, z1))] - user_path_uv_v = [] - n_sections = n_subs - 1 - n, dz, zs, zl = subs[0] - p0 = n.p - v0 = n.v.normalized() - for s, section in enumerate(subs): - n, dz, zs, zl = section - p1 = n.p - if s < n_sections: - v1 = subs[s + 1][0].v.normalized() - dir = (v0 + v1).normalized() - scale = 1 / cos(0.5 * acos(min(1, max(-1, v0.dot(v1))))) - for p in profile: - x, y = n.p + scale * p.x * dir - z = zl + p.y + altitude - verts.append((x, y, z)) - if s > 0: - user_path_uv_v.append((p1 - p0).length) - p0 = p1 - v0 = v1 - - # build faces using Panel - lofter = Lofter( - # closed_shape, index, x, y, idmat - True, - [i for i in range(len(profile))], - [p.x for p in profile], - [p.y for p in profile], - [idmat for i in range(len(profile))], - closed_path=False, - user_path_uv_v=user_path_uv_v, - user_path_verts=n_subs - ) - faces += lofter.faces(16, offset=f, path_type='USER_DEFINED') - matids += lofter.mat(16, idmat, idmat, path_type='USER_DEFINED') - v = Vector((0, 0)) - uvs += lofter.uv(16, v, v, v, v, 0, v, 0, 0, path_type='USER_DEFINED') - - def reset_shapes(self): - for s, stair in enumerate(self.stairs): - if 'Curved' in type(stair).__name__: - stair.l_shape = self.parts[s].left_shape - stair.r_shape = self.parts[s].right_shape - - def make_subs(self, height, step_depth, x, y, z, post_y, altitude, bottom, side, slice, - post_spacing, sub_spacing, respect_edges, move_x, x_offset, sub_offset_x, mat, - verts, faces, matids, uvs): - - n_steps = self.n_steps(step_depth) - self.set_height(height / n_steps) - n_stairs = len(self.stairs) - 1 - subs = [] - - if side == "LEFT": - offset = move_x - x_offset - # offset_sub = offset - sub_offset_x - else: - offset = move_x + x_offset - # offset_sub = offset + sub_offset_x - - for s, stair in enumerate(self.stairs): - if 'Curved' in type(stair).__name__: - if side == "LEFT": - part = stair.l_arc - shape = stair.l_shape - else: - part = stair.r_arc - shape = stair.r_shape - # Note: use left part as reference for post distances - # use right part as reference for panels - stair.l_arc, stair.l_t0, stair.l_t1, stair.l_tc = stair.set_offset(offset, shape) - stair.r_arc, stair.r_t0, stair.r_t1, stair.r_tc = stair.set_offset(offset, shape) - else: - stair.l_line = stair.offset(offset) - stair.r_line = stair.offset(offset) - part = stair.l_line - - lerp_z = 0 - edge_t = 1 - edge_size = 0 - # interpolate z near end landing - if 'Landing' in type(stair).__name__ and stair.next_type == 'STAIR': - if not slice: - line = stair.normal(1).offset(self.stairs[s + 1].step_depth) - res, p, t_part = part.intersect(line) - # does perpendicular line intersects circle ? - if res: - edge_size = self.stairs[s + 1].step_depth / stair.get_length(side) - edge_t = 1 - edge_size - else: - # in this case, lerp z over one step - lerp_z = stair.step_height - - t_step, n_step = stair.n_posts(post_spacing, side, respect_edges) - - # space between posts - sp = stair.get_length(side) - # post size - t_post = post_y / sp - - if s == n_stairs: - n_step += 1 - for i in range(n_step): - res_t = stair.get_lerp_vect([], side, i, t_step, respect_edges) - # subs - if s < n_stairs or i < n_step - 1: - res_t.append((i + 1) * t_step) - for j in range(len(res_t) - 1): - t0 = res_t[j] + t_post - t1 = res_t[j + 1] - t_post - dt = t1 - t0 - n_subs = int(sp * dt / sub_spacing) - if n_subs > 0: - t_subs = dt / n_subs - for k in range(1, n_subs): - t = t0 + k * t_subs - stair.get_lerp_vect(subs, side, 1, t0 + k * t_subs, False) - if t > edge_t: - n, dz, z0, z1 = subs[-1] - subs[-1] = n, dz, z0, z1 + (t - edge_t) / edge_size * stair.step_height - if lerp_z > 0: - n, dz, z0, z1 = subs[-1] - subs[-1] = n, dz, z0, z1 + t * stair.step_height - - for i, post in enumerate(subs): - self.get_post(post, x, y, z, altitude, sub_offset_x, mat, verts, faces, matids, uvs, bottom=bottom) - - def make_post(self, height, step_depth, x, y, z, altitude, side, post_spacing, respect_edges, move_x, x_offset, mat, - verts, faces, matids, uvs): - n_steps = self.n_steps(step_depth) - self.set_height(height / n_steps) - l_posts = [] - n_stairs = len(self.stairs) - 1 - - for s, stair in enumerate(self.stairs): - if type(stair).__name__ in ['CurvedStair', 'CurvedLanding']: - stair.l_arc, stair.l_t0, stair.l_t1, stair.l_tc = stair.set_offset(move_x - x_offset, stair.l_shape) - stair.r_arc, stair.r_t0, stair.r_t1, stair.r_tc = stair.set_offset(move_x + x_offset, stair.r_shape) - else: - stair.l_line = stair.offset(move_x - x_offset) - stair.r_line = stair.offset(move_x + x_offset) - - t_step, n_step = stair.n_posts(post_spacing, side, respect_edges) - - if s == n_stairs: - n_step += 1 - for i in range(n_step): - stair.get_lerp_vect(l_posts, side, i, t_step, respect_edges) - - if s == n_stairs and i == n_step - 1: - n, dz, z0, z1 = l_posts[-1] - l_posts[-1] = (n, dz, z0 - stair.step_height, z1) - - for i, post in enumerate(l_posts): - self.get_post(post, x, y, z, altitude, 0, mat, verts, faces, matids, uvs) - - def make_panels(self, height, step_depth, x, z, post_y, altitude, side, post_spacing, - panel_dist, respect_edges, move_x, x_offset, sub_offset_x, mat, verts, faces, matids, uvs): - - n_steps = self.n_steps(step_depth) - self.set_height(height / n_steps) - subs = [] - n_stairs = len(self.stairs) - 1 - - if side == "LEFT": - offset = move_x - x_offset - else: - offset = move_x + x_offset - - for s, stair in enumerate(self.stairs): - - is_circle = False - if 'Curved' in type(stair).__name__: - if side == "LEFT": - is_circle = stair.l_shape == "CIRCLE" - shape = stair.l_shape - else: - is_circle = stair.r_shape == "CIRCLE" - shape = stair.r_shape - stair.l_arc, stair.l_t0, stair.l_t1, stair.l_tc = stair.set_offset(offset, shape) - stair.r_arc, stair.r_t0, stair.r_t1, stair.r_tc = stair.set_offset(offset, shape) - else: - stair.l_line = stair.offset(offset) - stair.r_line = stair.offset(offset) - - # space between posts - sp = stair.get_length(side) - - t_step, n_step = stair.n_posts(post_spacing, side, respect_edges) - - if is_circle and 'Curved' in type(stair).__name__: - panel_da = abs(stair.da) / pi * 180 / n_step - panel_step = max(1, int(panel_da / 6)) - else: - panel_step = 1 - - # post size - t_post = (post_y + panel_dist) / sp - - if s == n_stairs: - n_step += 1 - for i in range(n_step): - res_t = stair.get_lerp_vect([], side, i, t_step, respect_edges) - # subs - if s < n_stairs or i < n_step - 1: - res_t.append((i + 1) * t_step) - for j in range(len(res_t) - 1): - t0 = res_t[j] + t_post - t1 = res_t[j + 1] - t_post - dt = t1 - t0 - t_curve = dt / panel_step - if dt > 0: - panel = [] - for k in range(panel_step): - stair.get_lerp_vect(panel, side, 1, t_curve, True, t0_abs=t0 + k * t_curve) - stair.get_lerp_vect(panel, side, 1, t1, False) - subs.append(panel) - for sub in subs: - self.get_panel(sub, altitude, x, z, sub_offset_x, mat, verts, faces, matids, uvs) - - def make_part(self, height, step_depth, part_x, part_z, x_move, x_offset, - z_offset, z_mode, steps_type, verts, faces, matids, uvs): - - params = [(stair.z_mode, stair.l_shape, stair.r_shape, - stair.bottom_z, stair.steps_type) for stair in self.stairs] - - for stair in self.stairs: - if x_offset > 0: - stair.l_shape = stair.r_shape - else: - stair.r_shape = stair.l_shape - stair.steps_type = steps_type - stair.z_mode = "LINEAR" - stair.bottom_z = part_z - if 'Curved' in type(stair).__name__: - stair.l_arc, stair.l_t0, stair.l_t1, stair.l_tc = \ - stair.set_offset(x_move + x_offset + 0.5 * part_x, stair.l_shape) - stair.r_arc, stair.r_t0, stair.r_t1, stair.r_tc = \ - stair.set_offset(x_move + x_offset - 0.5 * part_x, stair.r_shape) - else: - stair.l_line = stair.offset(x_move + x_offset + 0.5 * part_x) - stair.r_line = stair.offset(x_move + x_offset - 0.5 * part_x) - n_steps = self.n_steps(step_depth) - self.set_height(height / n_steps) - for j, stair in enumerate(self.stairs): - stair.z0 += z_offset + part_z - stair.n_step *= 2 - stair.t_step /= 2 - stair.step_height /= 2 - for i in range(stair.n_step): - stair.make_step(i, verts, faces, matids, uvs, nose_y=0) - stair.n_step /= 2 - stair.t_step *= 2 - stair.step_height *= 2 - stair.z_mode = params[j][0] - stair.l_shape = params[j][1] - stair.r_shape = params[j][2] - stair.bottom_z = params[j][3] - stair.steps_type = params[j][4] - stair.z0 -= z_offset + part_z - - def make_profile(self, profile, idmat, side, slice, height, step_depth, - x_offset, z_offset, extend, verts, faces, matids, uvs): - - for stair in self.stairs: - if 'Curved' in type(stair).__name__: - stair.l_arc, stair.l_t0, stair.l_t1, stair.l_tc = stair.set_offset(-x_offset, stair.l_shape) - stair.r_arc, stair.r_t0, stair.r_t1, stair.r_tc = stair.set_offset(x_offset, stair.r_shape) - else: - stair.l_line = stair.offset(-x_offset) - stair.r_line = stair.offset(x_offset) - - n_steps = self.n_steps(step_depth) - self.set_height(height / n_steps) - - n_stairs = len(self.stairs) - 1 - - if n_stairs < 0: - return - - sections = [] - sections.append([]) - - # first step - if extend != 0: - t = -extend / self.stairs[0].length - self.stairs[0].get_lerp_vect(sections[-1], side, 1, t, True) - - for s, stair in enumerate(self.stairs): - n_step = 1 - is_circle = False - - if 'Curved' in type(stair).__name__: - if side == "LEFT": - part = stair.l_arc - is_circle = stair.l_shape == "CIRCLE" - else: - part = stair.r_arc - is_circle = stair.r_shape == "CIRCLE" - else: - if side == "LEFT": - part = stair.l_line - else: - part = stair.r_line - - if is_circle: - n_step = 3 * stair.n_step - - t_step = 1 / n_step - - last_t = 1.0 - do_last = True - lerp_z = 0 - # last section 1 step before stair - if 'Landing' in type(stair).__name__ and stair.next_type == 'STAIR': - if not slice: - line = stair.normal(1).offset(self.stairs[s + 1].step_depth) - res, p, t_part = part.intersect(line) - # does perpendicular line intersects circle ? - if res: - last_t = 1 - self.stairs[s + 1].step_depth / stair.get_length(side) - if last_t < 0: - do_last = False - else: - # in this case, lerp z over one step - do_last = False - lerp_z = stair.step_height - - if s == n_stairs: - n_step += 1 - - for i in range(n_step): - res_t = stair.get_lerp_vect(sections[-1], side, i, t_step, True, z_offset=lerp_z) - # remove corner section - for cur_t in res_t: - if cur_t > 0 and cur_t > last_t: - sections[-1] = sections[-1][:-1] - - # last section 1 step before next stair start - if 'Landing' in type(stair).__name__ and stair.next_type == 'STAIR': - if do_last: - stair.get_lerp_vect(sections[-1], side, 1, last_t, False) - if slice: - sections.append([]) - if extend > 0: - t = -extend / self.stairs[s + 1].length - self.stairs[s + 1].get_lerp_vect(sections[-1], side, 1, t, True) - - t = 1 + extend / self.stairs[-1].length - self.stairs[-1].get_lerp_vect(sections[-1], side, 1, t, True) - - for cur_sect in sections: - user_path_verts = len(cur_sect) - f = len(verts) - if user_path_verts > 0: - user_path_uv_v = [] - n, dz, z0, z1 = cur_sect[-1] - cur_sect[-1] = (n, dz, z0 - stair.step_height, z1) - n_sections = user_path_verts - 1 - n, dz, zs, zl = cur_sect[0] - p0 = n.p - v0 = n.v.normalized() - for s, section in enumerate(cur_sect): - n, dz, zs, zl = section - p1 = n.p - if s < n_sections: - v1 = cur_sect[s + 1][0].v.normalized() - dir = (v0 + v1).normalized() - scale = 1 / cos(0.5 * acos(min(1, max(-1, v0.dot(v1))))) - for p in profile: - x, y = n.p + scale * p.x * dir - z = zl + p.y + z_offset - verts.append((x, y, z)) - if s > 0: - user_path_uv_v.append((p1 - p0).length) - p0 = p1 - v0 = v1 - - # build faces using Panel - lofter = Lofter( - # closed_shape, index, x, y, idmat - True, - [i for i in range(len(profile))], - [p.x for p in profile], - [p.y for p in profile], - [idmat for i in range(len(profile))], - closed_path=False, - user_path_uv_v=user_path_uv_v, - user_path_verts=user_path_verts - ) - faces += lofter.faces(16, offset=f, path_type='USER_DEFINED') - matids += lofter.mat(16, idmat, idmat, path_type='USER_DEFINED') - v = Vector((0, 0)) - uvs += lofter.uv(16, v, v, v, v, 0, v, 0, 0, path_type='USER_DEFINED') - - def set_matids(self, id_materials): - for stair in self.stairs: - stair.set_matids(id_materials) - - -def update(self, context): - self.update(context) - - -def update_manipulators(self, context): - self.update(context, manipulable_refresh=True) - - -def update_preset(self, context): - auto_update = self.auto_update - self.auto_update = False - if self.presets == 'STAIR_I': - self.n_parts = 1 - self.update_parts() - self.parts[0].type = 'S_STAIR' - elif self.presets == 'STAIR_L': - self.n_parts = 3 - self.update_parts() - self.parts[0].type = 'S_STAIR' - self.parts[1].type = 'C_STAIR' - self.parts[2].type = 'S_STAIR' - self.da = pi / 2 - elif self.presets == 'STAIR_U': - self.n_parts = 3 - self.update_parts() - self.parts[0].type = 'S_STAIR' - self.parts[1].type = 'D_STAIR' - self.parts[2].type = 'S_STAIR' - self.da = pi - elif self.presets == 'STAIR_O': - self.n_parts = 2 - self.update_parts() - self.parts[0].type = 'D_STAIR' - self.parts[1].type = 'D_STAIR' - self.da = pi - # keep auto_update state same - # prevent unwanted load_preset update - self.auto_update = auto_update - - -materials_enum = ( - ('0', 'Ceiling', '', 0), - ('1', 'White', '', 1), - ('2', 'Concrete', '', 2), - ('3', 'Wood', '', 3), - ('4', 'Metal', '', 4), - ('5', 'Glass', '', 5) - ) - - -class archipack_stair_material(PropertyGroup): - index : EnumProperty( - items=materials_enum, - default='4', - update=update - ) - - def find_datablock_in_selection(self, context): - """ - find witch selected object this instance belongs to - provide support for "copy to selected" - """ - selected = context.selected_objects[:] - for o in selected: - props = archipack_stair.datablock(o) - if props: - for part in props.rail_mat: - if part == self: - return props - return None - - def update(self, context): - props = self.find_datablock_in_selection(context) - if props is not None: - props.update(context) - - -class archipack_stair_part(PropertyGroup): - type : EnumProperty( - items=( - ('S_STAIR', 'Straight stair', '', 0), - ('C_STAIR', 'Curved stair', '', 1), - ('D_STAIR', 'Dual Curved stair', '', 2), - ('S_LANDING', 'Straight landing', '', 3), - ('C_LANDING', 'Curved landing', '', 4), - ('D_LANDING', 'Dual Curved landing', '', 5) - ), - default='S_STAIR', - update=update_manipulators - ) - length : FloatProperty( - name="Length", - min=0.01, - default=2.0, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - radius : FloatProperty( - name="Radius", - min=0.01, - default=0.7, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - da : FloatProperty( - name="Angle", - min=-pi, - max=pi, - default=pi / 2, - subtype='ANGLE', unit='ROTATION', - update=update - ) - left_shape : EnumProperty( - items=( - ('RECTANGLE', 'Straight', '', 0), - ('CIRCLE', 'Curved ', '', 1) - ), - default='RECTANGLE', - update=update - ) - right_shape : EnumProperty( - items=( - ('RECTANGLE', 'Straight', '', 0), - ('CIRCLE', 'Curved ', '', 1) - ), - default='RECTANGLE', - update=update - ) - manipulators : CollectionProperty(type=archipack_manipulator) - - def find_datablock_in_selection(self, context): - """ - find witch selected object this instance belongs to - provide support for "copy to selected" - """ - selected = context.selected_objects[:] - for o in selected: - props = archipack_stair.datablock(o) - if props: - for part in props.parts: - if part == self: - return props - return None - - def update(self, context, manipulable_refresh=False): - props = self.find_datablock_in_selection(context) - if props is not None: - props.update(context, manipulable_refresh) - - def draw(self, layout, context, index, user_mode): - if user_mode: - box = layout.box() - row = box.row() - row.prop(self, "type", text=str(index + 1)) - if self.type in ['C_STAIR', 'C_LANDING', 'D_STAIR', 'D_LANDING']: - row = box.row() - row.prop(self, "radius") - row = box.row() - row.prop(self, "da") - else: - row = box.row() - row.prop(self, "length") - if self.type in ['C_STAIR', 'C_LANDING', 'D_STAIR', 'D_LANDING']: - row = box.row(align=True) - row.prop(self, "left_shape", text="") - row.prop(self, "right_shape", text="") - else: - if self.type in ['S_STAIR', 'S_LANDING']: - box = layout.box() - row = box.row() - row.prop(self, "length") - - -class archipack_stair(ArchipackObject, Manipulable, PropertyGroup): - - parts : CollectionProperty(type=archipack_stair_part) - n_parts : IntProperty( - name="Parts", - min=1, - max=32, - default=1, update=update_manipulators - ) - step_depth : FloatProperty( - name="Going", - min=0.2, - default=0.25, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - width : FloatProperty( - name="Width", - min=0.01, - default=1.2, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - height : FloatProperty( - name="Height", - min=0.1, - default=2.4, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - nose_y : FloatProperty( - name="Depth", - min=0.0, - default=0.02, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - x_offset : FloatProperty( - name="Offset", - default=0.0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - nose_z : FloatProperty( - name="Height", - min=0.001, - default=0.03, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - bottom_z : FloatProperty( - name="Thickness", - min=0.001, - default=0.03, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - radius : FloatProperty( - name="Radius", - min=0.5, - default=0.7, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - da : FloatProperty( - name="Angle", - min=-pi, - max=pi, - default=pi / 2, - subtype='ANGLE', unit='ROTATION', - update=update - ) - total_angle : FloatProperty( - name="Angle", - min=-50 * pi, - max=50 * pi, - default=2 * pi, - subtype='ANGLE', unit='ROTATION', - update=update - ) - steps_type : EnumProperty( - name="Steps", - items=( - ('CLOSED', 'Closed', '', 0), - ('FULL', 'Full height', '', 1), - ('OPEN', 'Open ', '', 2) - ), - default='CLOSED', - update=update - ) - nose_type : EnumProperty( - name="Nosing", - items=( - ('STRAIGHT', 'Straight', '', 0), - ('OBLIQUE', 'Oblique', '', 1), - ), - default='STRAIGHT', - update=update - ) - left_shape : EnumProperty( - items=( - ('RECTANGLE', 'Straight', '', 0), - ('CIRCLE', 'Curved ', '', 1) - ), - default='RECTANGLE', - update=update - ) - right_shape : EnumProperty( - items=( - ('RECTANGLE', 'Straight', '', 0), - ('CIRCLE', 'Curved ', '', 1) - ), - default='RECTANGLE', - update=update - ) - z_mode : EnumProperty( - name="Interp z", - items=( - ('STANDARD', 'Standard', '', 0), - ('LINEAR', 'Bottom Linear', '', 1), - ('LINEAR_TOP', 'All Linear', '', 2) - ), - default='STANDARD', - update=update - ) - presets : EnumProperty( - items=( - ('STAIR_I', 'I stair', '', 0), - ('STAIR_L', 'L stair', '', 1), - ('STAIR_U', 'U stair', '', 2), - ('STAIR_O', 'O stair', '', 3), - ('STAIR_USER', 'User defined stair', '', 4), - ), - default='STAIR_I', update=update_preset - ) - left_post : BoolProperty( - name='left', - default=True, - update=update - ) - right_post : BoolProperty( - name='right', - default=True, - update=update - ) - post_spacing : FloatProperty( - name="Spacing", - min=0.1, - default=1.0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - post_x : FloatProperty( - name="Width", - min=0.001, - default=0.04, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - post_y : FloatProperty( - name="Length", - min=0.001, - default=0.04, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - post_z : FloatProperty( - name="Height", - min=0.001, - default=1, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - post_alt : FloatProperty( - name="Altitude", - min=-100, - default=0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - post_offset_x : FloatProperty( - name="Offset", - min=-100.0, max=100, - default=0.02, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - post_corners : BoolProperty( - name="Only on edges", - update=update, - default=False - ) - user_defined_post_enable : BoolProperty( - name="User", - update=update, - default=True - ) - user_defined_post : StringProperty( - name="User defined", - update=update - ) - idmat_post : EnumProperty( - name="Post", - items=materials_enum, - default='4', - update=update - ) - left_subs : BoolProperty( - name='left', - default=False, - update=update - ) - right_subs : BoolProperty( - name='right', - default=False, - update=update - ) - subs_spacing : FloatProperty( - name="Spacing", - min=0.05, - default=0.10, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - subs_x : FloatProperty( - name="Width", - min=0.001, - default=0.02, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - subs_y : FloatProperty( - name="Length", - min=0.001, - default=0.02, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - subs_z : FloatProperty( - name="Height", - min=0.001, - default=1, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - subs_alt : FloatProperty( - name="Altitude", - min=-100, - default=0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - subs_offset_x : FloatProperty( - name="Offset", - min=-100.0, max=100, - default=0.0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - subs_bottom : EnumProperty( - name="Bottom", - items=( - ('STEP', 'Follow step', '', 0), - ('LINEAR', 'Linear', '', 1), - ), - default='STEP', - update=update - ) - user_defined_subs_enable : BoolProperty( - name="User", - update=update, - default=True - ) - user_defined_subs : StringProperty( - name="User defined", - update=update - ) - idmat_subs : EnumProperty( - name="Subs", - items=materials_enum, - default='4', - update=update - ) - left_panel : BoolProperty( - name='left', - default=True, - update=update - ) - right_panel : BoolProperty( - name='right', - default=True, - update=update - ) - panel_alt : FloatProperty( - name="Altitude", - default=0.25, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - panel_x : FloatProperty( - name="Width", - min=0.001, - default=0.01, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - panel_z : FloatProperty( - name="Height", - min=0.001, - default=0.6, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - panel_dist : FloatProperty( - name="Spacing", - min=0.001, - default=0.05, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - panel_offset_x : FloatProperty( - name="Offset", - default=0.0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - idmat_panel : EnumProperty( - name="Panels", - items=materials_enum, - default='5', - update=update - ) - left_rail : BoolProperty( - name="left", - update=update, - default=False - ) - right_rail : BoolProperty( - name="right", - update=update, - default=False - ) - rail_n : IntProperty( - name="#", - default=1, - min=0, - max=31, - update=update - ) - rail_x : FloatVectorProperty( - name="Width", - default=[ - 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, - 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, - 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, - 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05 - ], - size=31, - min=0.001, - precision=2, step=1, - unit='LENGTH', - update=update - ) - rail_z : FloatVectorProperty( - name="Height", - default=[ - 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, - 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, - 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, - 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05 - ], - size=31, - min=0.001, - precision=2, step=1, - unit='LENGTH', - update=update - ) - rail_offset : FloatVectorProperty( - name="Offset", - default=[ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 - ], - size=31, - precision=2, step=1, - unit='LENGTH', - update=update - ) - rail_alt : FloatVectorProperty( - name="Altitude", - default=[ - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 - ], - size=31, - precision=2, step=1, - unit='LENGTH', - update=update - ) - rail_mat : CollectionProperty(type=archipack_stair_material) - - left_handrail : BoolProperty( - name="left", - update=update, - default=True - ) - right_handrail : BoolProperty( - name="right", - update=update, - default=True - ) - handrail_offset : FloatProperty( - name="Offset", - default=0.0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - handrail_alt : FloatProperty( - name="Altitude", - default=1.0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - handrail_extend : FloatProperty( - name="Extend", - default=0.1, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - handrail_slice_left : BoolProperty( - name='Slice', - default=True, - update=update - ) - handrail_slice_right : BoolProperty( - name='Slice', - default=True, - update=update - ) - handrail_profil : EnumProperty( - name="Profil", - items=( - ('SQUARE', 'Square', '', 0), - ('CIRCLE', 'Circle', '', 1), - ('COMPLEX', 'Circle over square', '', 2) - ), - default='SQUARE', - update=update - ) - handrail_x : FloatProperty( - name="Width", - min=0.001, - default=0.04, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - handrail_y : FloatProperty( - name="Height", - min=0.001, - default=0.04, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - handrail_radius : FloatProperty( - name="Radius", - min=0.001, - default=0.02, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - - left_string : BoolProperty( - name="left", - update=update, - default=False - ) - right_string : BoolProperty( - name="right", - update=update, - default=False - ) - string_x : FloatProperty( - name="Width", - min=-100.0, - default=0.02, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - string_z : FloatProperty( - name="Height", - default=0.3, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - string_offset : FloatProperty( - name="Offset", - default=0.0, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - string_alt : FloatProperty( - name="Altitude", - default=-0.04, precision=2, step=1, - unit='LENGTH', subtype='DISTANCE', - update=update - ) - - idmat_bottom : EnumProperty( - name="Bottom", - items=materials_enum, - default='1', - update=update - ) - idmat_raise : EnumProperty( - name="Raise", - items=materials_enum, - default='1', - update=update - ) - idmat_step_front : EnumProperty( - name="Step front", - items=materials_enum, - default='3', - update=update - ) - idmat_top : EnumProperty( - name="Top", - items=materials_enum, - default='3', - update=update - ) - idmat_side : EnumProperty( - name="Side", - items=materials_enum, - default='1', - update=update - ) - idmat_step_side : EnumProperty( - name="Step Side", - items=materials_enum, - default='3', - update=update - ) - idmat_handrail : EnumProperty( - name="Handrail", - items=materials_enum, - default='3', - update=update - ) - idmat_string : EnumProperty( - name="String", - items=materials_enum, - default='3', - update=update - ) - - # UI layout related - parts_expand : BoolProperty( - default=False - ) - steps_expand : BoolProperty( - default=False - ) - rail_expand : BoolProperty( - default=False - ) - idmats_expand : BoolProperty( - default=False - ) - handrail_expand : BoolProperty( - default=False - ) - string_expand : BoolProperty( - default=False - ) - post_expand : BoolProperty( - default=False - ) - panel_expand : BoolProperty( - default=False - ) - subs_expand : BoolProperty( - default=False - ) - - auto_update : BoolProperty( - options={'SKIP_SAVE'}, - default=True, - update=update_manipulators - ) - - def setup_manipulators(self): - - if len(self.manipulators) == 0: - s = self.manipulators.add() - s.prop1_name = "width" - s = self.manipulators.add() - s.prop1_name = "height" - s.normal = Vector((0, 1, 0)) - - for i in range(self.n_parts): - p = self.parts[i] - n_manips = len(p.manipulators) - if n_manips < 1: - m = p.manipulators.add() - m.type_key = 'SIZE' - m.prop1_name = 'length' - - def update_parts(self): - - # remove rails materials - for i in range(len(self.rail_mat), self.rail_n, -1): - self.rail_mat.remove(i - 1) - - # add rails - for i in range(len(self.rail_mat), self.rail_n): - self.rail_mat.add() - - # remove parts - for i in range(len(self.parts), self.n_parts, -1): - self.parts.remove(i - 1) - - # add parts - for i in range(len(self.parts), self.n_parts): - self.parts.add() - - self.setup_manipulators() - - def update(self, context, manipulable_refresh=False): - - o = self.find_in_selection(context, self.auto_update) - - if o is None: - return - - # clean up manipulators before any data model change - if manipulable_refresh: - self.manipulable_disable(context) - - self.update_parts() - - center = Vector((0, 0)) - verts = [] - faces = [] - matids = [] - uvs = [] - id_materials = [int(self.idmat_top), int(self.idmat_step_front), int(self.idmat_raise), - int(self.idmat_side), int(self.idmat_bottom), int(self.idmat_step_side)] - - # depth at bottom - bottom_z = self.bottom_z - if self.steps_type == 'OPEN': - # depth at front - bottom_z = self.nose_z - - width_left = 0.5 * self.width - self.x_offset - width_right = 0.5 * self.width + self.x_offset - - self.manipulators[0].set_pts([(-width_left, 0, 0), (width_right, 0, 0), (1, 0, 0)]) - self.manipulators[1].set_pts([(0, 0, 0), (0, 0, self.height), (1, 0, 0)]) - - g = StairGenerator(self.parts) - if self.presets == 'STAIR_USER': - for part in self.parts: - g.add_part(part.type, self.steps_type, self.nose_type, self.z_mode, self.nose_z, - bottom_z, center, max(width_left + 0.01, width_right + 0.01, part.radius), part.da, - width_left, width_right, part.length, part.left_shape, part.right_shape) - - elif self.presets == 'STAIR_O': - n_parts = max(1, int(round(abs(self.total_angle) / pi, 0))) - if self.total_angle > 0: - dir = 1 - else: - dir = -1 - last_da = self.total_angle - dir * (n_parts - 1) * pi - if dir * last_da > pi: - n_parts += 1 - last_da -= dir * pi - abs_last = dir * last_da - - for part in range(n_parts - 1): - g.add_part('D_STAIR', self.steps_type, self.nose_type, self.z_mode, self.nose_z, - bottom_z, center, max(width_left + 0.01, width_right + 0.01, self.radius), dir * pi, - width_left, width_right, 1.0, self.left_shape, self.right_shape) - if round(abs_last, 2) > 0: - if abs_last > pi / 2: - g.add_part('C_STAIR', self.steps_type, self.nose_type, self.z_mode, self.nose_z, - bottom_z, center, max(width_left + 0.01, width_right + 0.01, self.radius), - dir * pi / 2, - width_left, width_right, 1.0, self.left_shape, self.right_shape) - g.add_part('C_STAIR', self.steps_type, self.nose_type, self.z_mode, self.nose_z, - bottom_z, center, max(width_left + 0.01, width_right + 0.01, self.radius), - last_da - dir * pi / 2, - width_left, width_right, 1.0, self.left_shape, self.right_shape) - else: - g.add_part('C_STAIR', self.steps_type, self.nose_type, self.z_mode, self.nose_z, - bottom_z, center, max(width_left + 0.01, width_right + 0.01, self.radius), last_da, - width_left, width_right, 1.0, self.left_shape, self.right_shape) - else: - # STAIR_L STAIR_I STAIR_U - for part in self.parts: - g.add_part(part.type, self.steps_type, self.nose_type, self.z_mode, self.nose_z, - bottom_z, center, max(width_left + 0.01, width_right + 0.01, self.radius), self.da, - width_left, width_right, part.length, self.left_shape, self.right_shape) - - # Stair basis - g.set_matids(id_materials) - g.make_stair(self.height, self.step_depth, verts, faces, matids, uvs, nose_y=self.nose_y) - - # Ladder - offset_x = 0.5 * self.width - self.post_offset_x - post_spacing = self.post_spacing - if self.post_corners: - post_spacing = 10000 - - if self.user_defined_post_enable: - # user defined posts - user_def_post = context.scene.objects.get(self.user_defined_post.strip()) - if user_def_post is not None and user_def_post.type == 'MESH': - g.setup_user_defined_post(user_def_post, self.post_x, self.post_y, self.post_z) - - if self.left_post: - g.make_post(self.height, self.step_depth, 0.5 * self.post_x, 0.5 * self.post_y, - self.post_z, self.post_alt, 'LEFT', post_spacing, self.post_corners, - self.x_offset, offset_x, int(self.idmat_post), verts, faces, matids, uvs) - - if self.right_post: - g.make_post(self.height, self.step_depth, 0.5 * self.post_x, 0.5 * self.post_y, - self.post_z, self.post_alt, 'RIGHT', post_spacing, self.post_corners, - self.x_offset, offset_x, int(self.idmat_post), verts, faces, matids, uvs) - - # reset user def posts - g.user_defined_post = None - - # user defined subs - if self.user_defined_subs_enable: - user_def_subs = context.scene.objects.get(self.user_defined_subs.strip()) - if user_def_subs is not None and user_def_subs.type == 'MESH': - g.setup_user_defined_post(user_def_subs, self.subs_x, self.subs_y, self.subs_z) - - if self.left_subs: - g.make_subs(self.height, self.step_depth, 0.5 * self.subs_x, 0.5 * self.subs_y, - self.subs_z, 0.5 * self.post_y, self.subs_alt, self.subs_bottom, 'LEFT', - self.handrail_slice_left, post_spacing, self.subs_spacing, self.post_corners, - self.x_offset, offset_x, -self.subs_offset_x, int(self.idmat_subs), verts, faces, matids, uvs) - - if self.right_subs: - g.make_subs(self.height, self.step_depth, 0.5 * self.subs_x, 0.5 * self.subs_y, - self.subs_z, 0.5 * self.post_y, self.subs_alt, self.subs_bottom, 'RIGHT', - self.handrail_slice_right, post_spacing, self.subs_spacing, self.post_corners, - self.x_offset, offset_x, self.subs_offset_x, int(self.idmat_subs), verts, faces, matids, uvs) - - g.user_defined_post = None - - if self.left_panel: - g.make_panels(self.height, self.step_depth, 0.5 * self.panel_x, self.panel_z, 0.5 * self.post_y, - self.panel_alt, 'LEFT', post_spacing, self.panel_dist, self.post_corners, - self.x_offset, offset_x, -self.panel_offset_x, int(self.idmat_panel), verts, faces, matids, uvs) - - if self.right_panel: - g.make_panels(self.height, self.step_depth, 0.5 * self.panel_x, self.panel_z, 0.5 * self.post_y, - self.panel_alt, 'RIGHT', post_spacing, self.panel_dist, self.post_corners, - self.x_offset, offset_x, self.panel_offset_x, int(self.idmat_panel), verts, faces, matids, uvs) - - if self.right_rail: - for i in range(self.rail_n): - id_materials = [int(self.rail_mat[i].index) for j in range(6)] - g.set_matids(id_materials) - g.make_part(self.height, self.step_depth, self.rail_x[i], self.rail_z[i], - self.x_offset, offset_x + self.rail_offset[i], - self.rail_alt[i], 'LINEAR', 'CLOSED', verts, faces, matids, uvs) - - if self.left_rail: - for i in range(self.rail_n): - id_materials = [int(self.rail_mat[i].index) for j in range(6)] - g.set_matids(id_materials) - g.make_part(self.height, self.step_depth, self.rail_x[i], self.rail_z[i], - self.x_offset, -offset_x - self.rail_offset[i], - self.rail_alt[i], 'LINEAR', 'CLOSED', verts, faces, matids, uvs) - - if self.handrail_profil == 'COMPLEX': - sx = self.handrail_x - sy = self.handrail_y - handrail = [Vector((sx * x, sy * y)) for x, y in [ - (-0.28, 1.83), (-0.355, 1.77), (-0.415, 1.695), (-0.46, 1.605), (-0.49, 1.51), (-0.5, 1.415), - (-0.49, 1.315), (-0.46, 1.225), (-0.415, 1.135), (-0.355, 1.06), (-0.28, 1.0), (-0.255, 0.925), - (-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), - (0.28, 1.0), (0.355, 1.06), (0.415, 1.135), (0.46, 1.225), (0.49, 1.315), (0.5, 1.415), - (0.49, 1.51), (0.46, 1.605), (0.415, 1.695), (0.355, 1.77), (0.28, 1.83), (0.19, 1.875), - (0.1, 1.905), (0.0, 1.915), (-0.095, 1.905), (-0.19, 1.875)]] - - elif self.handrail_profil == 'SQUARE': - x = 0.5 * self.handrail_x - y = self.handrail_y - handrail = [Vector((-x, y)), Vector((-x, 0)), Vector((x, 0)), Vector((x, y))] - elif self.handrail_profil == 'CIRCLE': - r = self.handrail_radius - handrail = [Vector((r * sin(0.1 * -a * pi), r * (0.5 + cos(0.1 * -a * pi)))) for a in range(0, 20)] - - if self.right_handrail: - g.make_profile(handrail, int(self.idmat_handrail), "RIGHT", self.handrail_slice_right, - self.height, self.step_depth, self.x_offset + offset_x + self.handrail_offset, - self.handrail_alt, self.handrail_extend, verts, faces, matids, uvs) - - if self.left_handrail: - g.make_profile(handrail, int(self.idmat_handrail), "LEFT", self.handrail_slice_left, - self.height, self.step_depth, -self.x_offset + offset_x + self.handrail_offset, - self.handrail_alt, self.handrail_extend, verts, faces, matids, uvs) - - w = 0.5 * self.string_x - h = self.string_z - string = [Vector((-w, 0)), Vector((w, 0)), Vector((w, h)), Vector((-w, h))] - - if self.right_string: - g.make_profile(string, int(self.idmat_string), "RIGHT", False, self.height, self.step_depth, - self.x_offset + 0.5 * self.width + self.string_offset, - self.string_alt, 0, verts, faces, matids, uvs) - - if self.left_string: - g.make_profile(string, int(self.idmat_string), "LEFT", False, self.height, self.step_depth, - -self.x_offset + 0.5 * self.width + self.string_offset, - self.string_alt, 0, verts, faces, matids, uvs) - - bmed.buildmesh(context, o, verts, faces, matids=matids, uvs=uvs, weld=True, clean=True) - - # enable manipulators rebuild - if manipulable_refresh: - self.manipulable_refresh = True - - self.restore_context(context) - - def manipulable_setup(self, context): - """ - TODO: Implement the setup part as per parent object basis - - self.manipulable_disable(context) - o = context.active_object - for m in self.manipulators: - self.manip_stack.append(m.setup(context, o, self)) - - """ - self.manipulable_disable(context) - o = context.active_object - - self.setup_manipulators() - - if self.presets != 'STAIR_O': - for i, part in enumerate(self.parts): - if i >= self.n_parts: - break - if "S_" in part.type or self.presets in ['STAIR_USER']: - for j, m in enumerate(part.manipulators): - self.manip_stack.append(m.setup(context, o, part)) - - if self.presets in ['STAIR_U', 'STAIR_L']: - self.manip_stack.append(self.parts[1].manipulators[0].setup(context, o, self)) - - for m in self.manipulators: - self.manip_stack.append(m.setup(context, o, self)) - - -class ARCHIPACK_PT_stair(Panel): - bl_idname = "ARCHIPACK_PT_stair" - bl_label = "Stair" - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - # bl_context = 'object' - bl_category = 'Archipack' - - @classmethod - def poll(cls, context): - return archipack_stair.filter(context.active_object) - - def draw(self, context): - prop = archipack_stair.datablock(context.active_object) - if prop is None: - return - scene = context.scene - layout = self.layout - row = layout.row(align=True) - row.operator('archipack.stair_manipulate', icon='VIEW_PAN') - row = layout.row(align=True) - row.prop(prop, 'presets', text="") - box = layout.box() - # box.label(text="Styles") - row = box.row(align=True) - # row.menu("ARCHIPACK_MT_stair_preset", text=bpy.types.ARCHIPACK_MT_stair_preset.bl_label) - row.operator("archipack.stair_preset_menu", text=bpy.types.ARCHIPACK_OT_stair_preset_menu.bl_label) - row.operator("archipack.stair_preset", text="", icon='ADD') - row.operator("archipack.stair_preset", text="", icon='REMOVE').remove_active = True - box = layout.box() - box.prop(prop, 'width') - box.prop(prop, 'height') - box.prop(prop, 'bottom_z') - box.prop(prop, 'x_offset') - # box.prop(prop, 'z_mode') - box = layout.box() - row = box.row() - if prop.parts_expand: - row.prop(prop, 'parts_expand', icon="TRIA_DOWN", text="Parts", emboss=False) - if prop.presets == 'STAIR_USER': - box.prop(prop, 'n_parts') - if prop.presets != 'STAIR_USER': - row = box.row(align=True) - row.prop(prop, "left_shape", text="") - row.prop(prop, "right_shape", text="") - row = box.row() - row.prop(prop, "radius") - row = box.row() - if prop.presets == 'STAIR_O': - row.prop(prop, 'total_angle') - else: - row.prop(prop, 'da') - if prop.presets != 'STAIR_O': - for i, part in enumerate(prop.parts): - part.draw(layout, context, i, prop.presets == 'STAIR_USER') - else: - row.prop(prop, 'parts_expand', icon="TRIA_RIGHT", text="Parts", emboss=False) - - box = layout.box() - row = box.row() - if prop.steps_expand: - row.prop(prop, 'steps_expand', icon="TRIA_DOWN", text="Steps", emboss=False) - box.prop(prop, 'steps_type') - box.prop(prop, 'step_depth') - box.prop(prop, 'nose_type') - box.prop(prop, 'nose_z') - box.prop(prop, 'nose_y') - else: - row.prop(prop, 'steps_expand', icon="TRIA_RIGHT", text="Steps", emboss=False) - - box = layout.box() - row = box.row(align=True) - if prop.handrail_expand: - row.prop(prop, 'handrail_expand', icon="TRIA_DOWN", text="Handrail", emboss=False) - else: - row.prop(prop, 'handrail_expand', icon="TRIA_RIGHT", text="Handrail", emboss=False) - - row.prop(prop, 'left_handrail') - row.prop(prop, 'right_handrail') - - if prop.handrail_expand: - box.prop(prop, 'handrail_alt') - box.prop(prop, 'handrail_offset') - box.prop(prop, 'handrail_extend') - box.prop(prop, 'handrail_profil') - if prop.handrail_profil != 'CIRCLE': - box.prop(prop, 'handrail_x') - box.prop(prop, 'handrail_y') - else: - box.prop(prop, 'handrail_radius') - row = box.row(align=True) - row.prop(prop, 'handrail_slice_left') - row.prop(prop, 'handrail_slice_right') - - box = layout.box() - row = box.row(align=True) - if prop.string_expand: - row.prop(prop, 'string_expand', icon="TRIA_DOWN", text="String", emboss=False) - else: - row.prop(prop, 'string_expand', icon="TRIA_RIGHT", text="String", emboss=False) - row.prop(prop, 'left_string') - row.prop(prop, 'right_string') - if prop.string_expand: - box.prop(prop, 'string_x') - box.prop(prop, 'string_z') - box.prop(prop, 'string_alt') - box.prop(prop, 'string_offset') - - box = layout.box() - row = box.row(align=True) - if prop.post_expand: - row.prop(prop, 'post_expand', icon="TRIA_DOWN", text="Post", emboss=False) - else: - row.prop(prop, 'post_expand', icon="TRIA_RIGHT", text="Post", emboss=False) - row.prop(prop, 'left_post') - row.prop(prop, 'right_post') - if prop.post_expand: - box.prop(prop, 'post_corners') - if not prop.post_corners: - box.prop(prop, 'post_spacing') - box.prop(prop, 'post_x') - box.prop(prop, 'post_y') - box.prop(prop, 'post_z') - box.prop(prop, 'post_alt') - box.prop(prop, 'post_offset_x') - row = box.row(align=True) - row.prop(prop, 'user_defined_post_enable', text="") - row.prop_search(prop, "user_defined_post", scene, "objects", text="") - - box = layout.box() - row = box.row(align=True) - if prop.subs_expand: - row.prop(prop, 'subs_expand', icon="TRIA_DOWN", text="Subs", emboss=False) - else: - row.prop(prop, 'subs_expand', icon="TRIA_RIGHT", text="Subs", emboss=False) - - row.prop(prop, 'left_subs') - row.prop(prop, 'right_subs') - if prop.subs_expand: - box.prop(prop, 'subs_spacing') - box.prop(prop, 'subs_x') - box.prop(prop, 'subs_y') - box.prop(prop, 'subs_z') - box.prop(prop, 'subs_alt') - box.prop(prop, 'subs_offset_x') - box.prop(prop, 'subs_bottom') - row = box.row(align=True) - row.prop(prop, 'user_defined_subs_enable', text="") - row.prop_search(prop, "user_defined_subs", scene, "objects", text="") - - box = layout.box() - row = box.row(align=True) - if prop.panel_expand: - row.prop(prop, 'panel_expand', icon="TRIA_DOWN", text="Panels", emboss=False) - else: - row.prop(prop, 'panel_expand', icon="TRIA_RIGHT", text="Panels", emboss=False) - row.prop(prop, 'left_panel') - row.prop(prop, 'right_panel') - if prop.panel_expand: - box.prop(prop, 'panel_dist') - box.prop(prop, 'panel_x') - box.prop(prop, 'panel_z') - box.prop(prop, 'panel_alt') - box.prop(prop, 'panel_offset_x') - - box = layout.box() - row = box.row(align=True) - if prop.rail_expand: - row.prop(prop, 'rail_expand', icon="TRIA_DOWN", text="Rails", emboss=False) - else: - row.prop(prop, 'rail_expand', icon="TRIA_RIGHT", text="Rails", emboss=False) - row.prop(prop, 'left_rail') - row.prop(prop, 'right_rail') - if prop.rail_expand: - box.prop(prop, 'rail_n') - for i in range(prop.rail_n): - box = layout.box() - box.label(text="Rail " + str(i + 1)) - box.prop(prop, 'rail_x', index=i) - box.prop(prop, 'rail_z', index=i) - box.prop(prop, 'rail_alt', index=i) - box.prop(prop, 'rail_offset', index=i) - box.prop(prop.rail_mat[i], 'index', text="") - - box = layout.box() - row = box.row() - - if prop.idmats_expand: - row.prop(prop, 'idmats_expand', icon="TRIA_DOWN", text="Materials", emboss=False) - box.prop(prop, 'idmat_top') - box.prop(prop, 'idmat_side') - box.prop(prop, 'idmat_bottom') - box.prop(prop, 'idmat_step_side') - box.prop(prop, 'idmat_step_front') - box.prop(prop, 'idmat_raise') - box.prop(prop, 'idmat_handrail') - box.prop(prop, 'idmat_panel') - box.prop(prop, 'idmat_post') - box.prop(prop, 'idmat_subs') - box.prop(prop, 'idmat_string') - else: - row.prop(prop, 'idmats_expand', icon="TRIA_RIGHT", text="Materials", emboss=False) - - -# ------------------------------------------------------------------ -# Define operator class to create object -# ------------------------------------------------------------------ - - -class ARCHIPACK_OT_stair(ArchipackCreateTool, Operator): - bl_idname = "archipack.stair" - bl_label = "Stair" - bl_description = "Create a Stair" - bl_category = 'Archipack' - bl_options = {'REGISTER', 'UNDO'} - - def create(self, context): - m = bpy.data.meshes.new("Stair") - o = bpy.data.objects.new("Stair", m) - d = m.archipack_stair.add() - self.link_object_to_scene(context, o) - o.select_set(state=True) - context.view_layer.objects.active = o - self.load_preset(d) - self.add_material(o) - m.auto_smooth_angle = 0.20944 - return o - - # ----------------------------------------------------- - # Execute - # ----------------------------------------------------- - def execute(self, context): - if context.mode == "OBJECT": - bpy.ops.object.select_all(action="DESELECT") - o = self.create(context) - o.location = context.scene.cursor.location - o.select_set(state=True) - context.view_layer.objects.active = o - self.manipulate() - return {'FINISHED'} - else: - self.report({'WARNING'}, "Archipack: Option only valid in Object mode") - return {'CANCELLED'} - -# ------------------------------------------------------------------ -# Define operator class to manipulate object -# ------------------------------------------------------------------ - - -class ARCHIPACK_OT_stair_manipulate(Operator): - bl_idname = "archipack.stair_manipulate" - bl_label = "Manipulate" - bl_description = "Manipulate" - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(self, context): - return archipack_stair.filter(context.active_object) - - def invoke(self, context, event): - d = archipack_stair.datablock(context.active_object) - d.manipulable_invoke(context) - return {'FINISHED'} - - -# ------------------------------------------------------------------ -# Define operator class to load / save presets -# ------------------------------------------------------------------ - - -class ARCHIPACK_OT_stair_preset_menu(PresetMenuOperator, Operator): - bl_description = "Show Stair Presets" - bl_idname = "archipack.stair_preset_menu" - bl_label = "Stair style" - preset_subdir = "archipack_stair" - - -class ARCHIPACK_OT_stair_preset(ArchipackPreset, Operator): - """Add a Stair Preset""" - bl_idname = "archipack.stair_preset" - bl_label = "Add Stair Style" - preset_menu = "ARCHIPACK_OT_stair_preset_menu" - - @property - def blacklist(self): - return ['manipulators'] - - -def register(): - bpy.utils.register_class(archipack_stair_material) - bpy.utils.register_class(archipack_stair_part) - bpy.utils.register_class(archipack_stair) - Mesh.archipack_stair = CollectionProperty(type=archipack_stair) - bpy.utils.register_class(ARCHIPACK_PT_stair) - bpy.utils.register_class(ARCHIPACK_OT_stair) - bpy.utils.register_class(ARCHIPACK_OT_stair_preset_menu) - bpy.utils.register_class(ARCHIPACK_OT_stair_preset) - bpy.utils.register_class(ARCHIPACK_OT_stair_manipulate) - - -def unregister(): - bpy.utils.unregister_class(archipack_stair_material) - bpy.utils.unregister_class(archipack_stair_part) - bpy.utils.unregister_class(archipack_stair) - del Mesh.archipack_stair - bpy.utils.unregister_class(ARCHIPACK_PT_stair) - bpy.utils.unregister_class(ARCHIPACK_OT_stair) - bpy.utils.unregister_class(ARCHIPACK_OT_stair_preset_menu) - bpy.utils.unregister_class(ARCHIPACK_OT_stair_preset) - bpy.utils.unregister_class(ARCHIPACK_OT_stair_manipulate) |