From 989c4eb483ec16ac645eaaf52f28fd4d55f8bf29 Mon Sep 17 00:00:00 2001 From: s-leger Date: Sun, 7 Jan 2018 01:38:00 +0100 Subject: [PATCH 1/5] Archipack Blinds object Initial commit for blinds --- __init__.py | 12 +- archipack_blind.py | 971 ++++++++++++++++++++++++++++++++++++++++++++ archipack_window.py | 181 +++++---- 3 files changed, 1086 insertions(+), 78 deletions(-) create mode 100644 archipack_blind.py diff --git a/__init__.py b/__init__.py index 331656a..afd1361 100644 --- a/__init__.py +++ b/__init__.py @@ -63,6 +63,7 @@ imp.reload(archipack_truss) # imp.reload(archipack_toolkit) imp.reload(archipack_floor) + imp.reload(archipack_blind) imp.reload(archipack_rendering) # imp.reload(archipack_envi) imp.reload(archipack_io) @@ -89,6 +90,7 @@ from . import archipack_truss # from . import archipack_toolkit from . import archipack_floor + from . import archipack_blind from . import archipack_rendering # from . import archipack_envi from . import archipack_io @@ -596,7 +598,13 @@ def draw(self, context): row.operator("archipack.floor_preset_menu", text="", icon='CURVE_DATA').preset_operator = "archipack.floor_from_curve" - + row = box.row(align=True) + row.operator("archipack.blind_preset_menu", + text="Blind" + # , + # icon_value=icons["floor"].icon_id + ).preset_operator = "archipack.blind" + box.label(text="Custom objects") box.operator("archipack.wall", text="Custom wall") @@ -717,6 +725,7 @@ def register(): archipack_slab.register() archipack_fence.register() archipack_truss.register() + archipack_blind.register() # archipack_toolkit.register() archipack_floor.register() archipack_rendering.register() @@ -758,6 +767,7 @@ def unregister(): archipack_slab.unregister() archipack_fence.unregister() archipack_truss.unregister() + archipack_blind.unregister() # archipack_toolkit.unregister() archipack_floor.unregister() archipack_rendering.unregister() diff --git a/archipack_blind.py b/archipack_blind.py new file mode 100644 index 0000000..506dc4a --- /dev/null +++ b/archipack_blind.py @@ -0,0 +1,971 @@ +# -*- 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 ##### + +# + +# ---------------------------------------------------------- +# Author: Stephen Leger (s-leger) +# +# ---------------------------------------------------------- +import bpy +from bpy.types import Operator, PropertyGroup, Mesh, Panel +from bpy.props import ( + FloatProperty, + BoolProperty, + CollectionProperty, + IntProperty, + EnumProperty + ) +from math import sin, cos, tan, ceil, floor, pi, asin, acos, radians, atan2, sqrt +from mathutils import Vector, Matrix +from .bmesh_utils import BmeshEdit as bmed +from .archipack_manipulator import Manipulable +from .archipack_preset import ArchipackPreset, PresetMenuOperator +from .archipack_object import ArchipackCreateTool, ArchipackObject +import logging +logger = logging.getLogger("archipack") + +def update(self, context): + self.update(context) + + + + + +class archipack_blind(ArchipackObject, Manipulable, PropertyGroup): + width = FloatProperty( + name='Width', + min=0.10, default=1, precision=3, + description='Total width', update=update, + ) + frame_height = FloatProperty( + name='Height', + min=0.01, default=0.2, precision=3, + description='Frame height', + update=update, + ) + altitude = FloatProperty( + name='Altitude', + min=0, default=1.0, precision=3, + description='Altitude', + update=update, + ) + frame_depth = FloatProperty( + name='Frame depth', min=0.02, default=0.04, + precision=3, + description='Frame depth', update=update, + ) + height = FloatProperty( + name='Height', + min=0.20, max=10, default=1.7, precision=3, + description='Total height', + update=update, + ) + depth = FloatProperty( + name='Depth', min=0.02, default=0.04, + precision=3, + description='Slat depth/width', update=update, + ) + panels = IntProperty( + name="Panels", + min=1, + default=2, + description='#panels', update=update + ) + angle = FloatProperty( + name='Angle', + min=-pi, + max=pi, + default=0, + precision=1, + subtype='ANGLE', unit='ROTATION', + description='Angle of the slats', update=update, + ) + blind_angle = FloatProperty( + name='Angle', + min=0, + max=0.5*pi, + default=0.5*pi, + precision=1, + subtype='ANGLE', unit='ROTATION', + description='Angle of the slats', update=update, + ) + ratio = FloatProperty( + name='Extend', min=0, max=100, default=100, + description='% of extension (100 full extend)', update=update, + ) + style = EnumProperty( + items=( + ('VENITIAN', 'Venitian', 'Venitian', 0), # / / /// + ('SLAT', 'Slat', 'Slat', 1), # / / / + ('ROLLER', 'Roller', 'Roller', 2), # __________ + ('BLADES', 'Blades', 'Blades', 3), # _ _ ___ + ('PLATED', 'Plated', 'Plated', 4), # Vertical \/\/ -> regular + # ('CELLULAR', 'Cellular blind', 'Cellular blind', 5), # OOOO -> venitian like variant + ('JAPANESE', 'Japanese', 'Japanese', 6), # _- -> horizontal + ('VERTICAL_SLOTTED', 'Vertical slotted', 'Vertical slotted', 7) # / / -> horizontal + ), + default='VENITIAN', update=update + ) + auto_update = BoolProperty( + # Wont save auto_update state in any case + options={'SKIP_SAVE'}, + default=True, + update=update + ) + + def frame(self, verts, faces, matids): + nv = len(verts) + x = 0.5 * self.width + y = 0.5 * self.frame_depth + z0 = self.altitude + self.height + self.frame_height + z1 = self.altitude + self.height + verts.extend([(-x, -y, z0), + (-x, y, z0), + (x, y, z0), + (x, -y, z0), + (-x, -y, z1), + (-x, y, z1), + (x, y, z1), + (x, -y, z1)]) + faces.extend([tuple([idx + nv for idx in f]) for f in [ + (0, 3, 2, 1), + (0, 1, 5, 4), + (1, 2, 6, 5), + (2, 3, 7, 6), + (5, 6, 7, 4), + (0, 4, 7, 3)]]) + matids.extend([ + 0,0,0,0,0,0 + ]) + + def cylinder(self, radius, size, x, y, z, axis, verts, faces, matids): + seg = 12 + deg = 2 * pi / seg + nv = len(verts) + + if axis == 'X': + tM = Matrix([ + [0, 0, size, x], + [0, radius, 0, y], + [radius, 0, 0, z], + [0, 0, 0, 1] + ]) + + elif axis == 'Y': + tM = Matrix([ + [radius, 0, 0, x], + [0, 0, size, y], + [0, radius, 0, z], + [0, 0, 0, 1] + ]) + + else: + tM = Matrix([ + [radius, 0, 0, x], + [0, radius, 0, y], + [0, 0, size, z], + [0, 0, 0, 1] + ]) + + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 0)) for a in range(seg)]) + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 1)) for a in range(seg)]) + + faces.extend([tuple([nv + i + f for f in (0, 1, seg + 1, seg)]) for i in range(seg - 1)]) + faces.append((nv + seg - 1, nv, nv + seg, nv + 2 * seg - 1)) + matids.extend([2 for i in range(seg)]) + + def make_blade(self, verts, faces, matids, posz, offset_z, angle): + # ------------------------------------ + # Mesh data + # ------------------------------------ + c = cos(angle) + s = sin(angle) + x = 0.5 * self.width + y = 0.5 * self.depth + + tM0 = Matrix([ + [1, 0, 0, 0], + [0, c, -s, c * posz], + [0, s, c, s * posz + offset_z], + [0, 0, 0, 1] + ]) + tM1 = Matrix([ + [-1, 0, 0, 0], + [0, c, -s, c * posz], + [0, s, c, s * posz + offset_z], + [0, 0, 0, 1] + ]) + + nv = len(verts) + + # blade shape angle + a = tan(radians(3)) + + # y from center + y0 = -0.0025 + y1 = y - 0.00195 + + # z from center to out + z0 = 0 + z1 = -a * y1 + z2 = -a * y + + # x from out to center + x1 = x - 0.0045 + x2 = x - 0.0017 + + shape =[(x2, -y1, z1), + (x2, y1, z1), + (x1, -y, z2), + (x1, y, z2), + (x, -y0, z0), + (x, y0, z0), + (x1, -y0, z0), + (x1, y0, z0)] + + # Vertex + verts.extend([tM0 * Vector(v) for v in shape]) + verts.extend([tM1 * Vector(v) for v in shape]) + + # Faces + faces.extend([tuple([i + nv for i in f]) for f in [ + (6, 4, 1, 3), (7, 5, 4, 6), (2, 0, 5, 7), + (14, 6, 3, 11), (15, 7, 6, 14), (10, 2, 7, 15), + (12, 14, 11, 9), (13, 15, 14, 12), (8, 10, 15, 13)]]) + + matids.extend([1 for i in range(9)]) + """""" + + def roller_blades(self, verts, faces, matids): + gap = 0.01 + # total available space + height = self.height + bottom = self.altitude + # numslats + numslats = ceil(height / self.depth) + separation = gap + self.depth + + # upper blade may overflow inside frame + # top when all blades are collapsed + absolute_top = bottom + numslats * self.depth + + # when up, there are gaps between blades + unfold_height = numslats * separation + + # fully open upper blade + unfold_top = absolute_top + unfold_height + + # altitude of upper blade + upper_altitude = unfold_top - unfold_height * self.ratio / 100 + + # available space, to compute collapsed one + available_space = min(unfold_height, upper_altitude - bottom) + # space left for gaps + available_gaps = available_space - numslats * self.depth + # regular including gaps + regular_slats = floor(available_gaps / gap) + regular_space = separation * regular_slats + collapsed_slats = numslats - regular_slats + collapsed_space = self.depth * collapsed_slats + + top = upper_altitude - (0.5 * self.depth + gap) - (self.altitude + self.height) + + for i in range(regular_slats): + if top <= 0.5 * self.depth: + self.make_blade(verts, faces, matids, top, self.altitude + self.height, self.blind_angle) + top -= separation + + if collapsed_slats > 0: + top -= available_space - (collapsed_space + regular_space) - gap + + for i in range(collapsed_slats): + self.make_blade (verts, faces, matids, top, self.altitude + self.height, self.blind_angle) + top -= self.depth + + def make_slat(self, verts, faces, matids, posz, angle): + # ------------------------------------ + # Mesh data + # ------------------------------------ + x = 0.5 * self.width + y = 0.5 * self.depth + c = cos(angle) + s = sin(angle) + + tM0 = Matrix([ + [1, 0, 0, 0], + [0, c, s, 0], + [0, -s, c, posz], + [0, 0, 0, 1] + ]) + tM1 = Matrix([ + [-1, 0, 0, 0], + [0, c, s, 0], + [0, -s, c, posz], + [0, 0, 0, 1] + ]) + + # half gap width + gap = 0.0025 + + # gap distance from border + if self.width < 0.60: + sep = 0.06 + else: + sep = 0.15 + + nv = len(verts) + + # slat angle + a = tan(radians(3)) + + # y from center + y0 = gap + y1 = y - 0.00195 + + # z from center to out + z0 = 0 + z1 = -a * y1 + z2 = -a * y + + # x from out to center + x1 = x - 0.0045 + x2 = x - 0.0017 + # border gap + x3 = x - sep - gap + x4 = x - sep + gap + # center gap + x5 = gap + + shape =[(x2, -y1, z1), + (x2, y1, z1), + (x1, -y, z2), + (x1, y, z2), + (x, -y0, z0), + (x, y0, z0), + (x1, -y0, z0), + (x1, y0, z0), + (x5, -y, z2), + (x5, y, z2), + (x5, -y0, z0), + (x5, y0, z0), + (x3, -y, z2), + (x3, y, z2), + (x3, -y0, z0), + (x3, y0, z0), + (x4, -y, z2), + (x4, y, z2), + (x4, -y0, z0), + (x4, y0, z0)] + + # Vertex + verts.extend([tM0 * Vector(v) for v in shape]) + verts.extend([tM1 * Vector(v) for v in shape]) + + # Faces + faces.extend([tuple([idx + nv for idx in f]) for f in [ + (7, 5, 1, 3), (6, 4, 5, 7), (2, 0, 4, 6), + (19, 7, 3, 17), (16, 2, 6, 18), (18, 6, 7, 19), + (11, 15, 13, 9), (8, 12, 14, 10), + (10, 14, 15, 11), (15, 19, 17, 13), (12, 16, 18, 14), + (39, 35, 33, 37), (34, 38, 36, 32), + (27, 23, 21, 25), (27, 25, 24, 26), (24, 20, 22, 26), + (39, 37, 23, 27), (39, 27, 26, 38), (38, 26, 22, 36), + (30, 34, 32, 28), (34, 30, 31, 35), (35, 31, 29, 33), + (11, 9, 29, 31), (8, 10, 30, 28)]]) + + matids.extend([1 for i in range(24)]) + + def roller_slats(self, verts, faces, matids): + # total available space + height = self.height + # numslats (slats +1) + numslats = ceil(height / (self.depth * 0.85)) + # separation + separation = height / numslats + half_separation = 0.5 * separation + available = height * self.ratio / 100 + regular_slats = available / separation + top = self.altitude + self.height - (regular_slats % 1 - 2) * separation + absolute_top = self.altitude + self.height + 0.5 * self.depth + + angle = min(0.47222 * pi, max(-0.47222 * pi, self.angle)) + + for i in range(int(regular_slats) + 2): + top -= half_separation + if top < absolute_top: + self.make_slat(verts, faces, matids, top, angle) + top -= half_separation + + radius = 0.00025 + bottom = top + half_separation + z = self.altitude + self.height + y = 0 + if self.width < 0.60: + sep = 0.06 + else: + sep = 0.15 + + s = bottom - z + + self.cylinder(radius, s, 0, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s, 0.5 * self.width - sep, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s, sep - 0.5 * self.width, y, z, 'Z', verts, faces, matids) + + def venitian_slats(self, verts, faces, matids): + gap = 0.0015 + # total available space + height = self.height + top = self.altitude + self.height + angle = min(0.47222 * pi, max(-0.47222 * pi, self.angle)) + # numslats + numslats = ceil(height / (self.depth * 0.85)) + # separation + separation = height / numslats + half_separation = 0.5 * separation + # available space + available = (height - numslats * gap) * self.ratio / 100 + collapsed_parts = (available - numslats * (separation - gap)) / (gap - separation) + collapsed_slats = floor(collapsed_parts) + collapsed_space = collapsed_slats * gap + regular_slats = floor(numslats - collapsed_parts) + regular_space = regular_slats * separation + rotated_space = available + numslats * gap - (regular_space + collapsed_space) + rotated_height = abs(sin(angle) * 0.5 * self.depth) + """ + logger.debug("n_c:%s, n_r:%s, n_s:%s, c:%s, r:%s, rot:%s, a:%s", + collapsed_slats, + regular_slats, + numslats, + collapsed_space, + regular_space, + rotated_space, + available + ) + """ + for i in range(regular_slats): + top -= half_separation + self.make_slat(verts, faces, matids, top, angle) + top -= half_separation + + # rotated slat + if collapsed_slats < numslats and regular_slats < numslats: + # space available between collapsed and regular + # + # \| + # |\ _ + # _|_ _ + before = min(half_separation, rotated_space) + + # space in use for collapsed + if before < half_separation: + rotated_angle = 0 + else: + rotated_angle = asin(2 * (rotated_space - half_separation) / self.depth) + if self.angle < 0: + rotated_angle = max(self.angle, -rotated_angle) + else: + rotated_angle = min(self.angle, rotated_angle) + + top -= before + self.make_slat(verts, faces, matids, top, rotated_angle) + top -= 0.5 * gap + max(0, rotated_space - before) + + + for i in range(collapsed_slats): + top -= 0.5 * gap + self.make_slat(verts, faces, matids, top, 0) + top -= 0.5 * gap + + radius = 0.0003 + bottom = top + + if collapsed_slats > 0: + bottom += 0.5 * gap + else: + bottom = max( + top + 0.5 * gap, + self.altitude + self.height - separation * (numslats - 0.5) + ) + + z = self.altitude + self.height + + y = 0 + if self.width < 0.60: + sep = 0.06 + else: + sep = 0.15 + + s = bottom - z + self.cylinder(radius, s, 0, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s, 0.5 * self.width - sep, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s, sep - 0.5 * self.width, y, z, 'Z', verts, faces, matids) + + # curvature of slats (z) + ca = cos(angle) + sa = sin(angle) + slat_depth = tan(radians(3)) * 0.5 * self.depth + curvature = slat_depth * sa + + rot = 0 + + if self.ratio == 100: + rot = sa * 0.5 * self.depth + s -= slat_depth + + y = 0.5 * self.depth * ca - curvature + radius + + self.cylinder(radius, s - rot, 0, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s - rot, 0.5 * self.width - sep, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s - rot, sep - 0.5 * self.width, y, z, 'Z', verts, faces, matids) + + y = -0.5 * self.depth * ca - curvature - radius + + self.cylinder(radius, s + rot, 0, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s + rot, 0.5 * self.width - sep, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s + rot, sep - 0.5 * self.width, y, z, 'Z', verts, faces, matids) + + def roller_curtain(self, verts, faces, matids): + x = 0.5 * self.width + y = 0.5 * self.depth + c = cos(self.blind_angle) + s = sin(self.blind_angle) + z0 = self.altitude + self.height + ext = -self.height * self.ratio / 100 + z1 = z0 + s * ext + y = c * ext + nv = len(verts) + verts.extend([Vector(v) for v in [ + [x, 0, z0], + [-x, 0, z0], + [-x, y, z1], + [x, y, z1] + ]]) + faces.append(tuple([nv + f for f in range(4)])) + matids.append(1) + + def plated_blind(self, verts, faces, matids): + x = 0.5 * self.width + a = atan2(0.002, 0.5 * self.depth) + zmax = self.depth * cos(a) + num = ceil(self.height / zmax) + z = self.height / num * self.ratio / 100 + a = asin(z / self.depth) + y = 0.5 * cos(a) * self.depth + z0 = self.altitude + self.height + + nv = len(verts) + for yi, zi in [[(2 * (i % 2) - 1) * y, z0 - z * i] for i in range(num + 1)]: + verts.extend([Vector((x, yi, zi)), Vector((-x, yi, zi))]) + if num % 2 == 1: + y = -y + + verts.extend([Vector((x, y, z0 - num * z)), Vector((-x, y, z0 - num * z))]) + faces.extend([tuple([nv + f, nv + 1 + f, nv + 3 + f, nv + 2 + f]) for f in range(0, 2 * (num + 1), 2)]) + matids.extend([1 for i in range(num + 1)]) + + radius = 0.0003 + + s = -num * z + z = self.altitude + self.height + y = 0 + if self.width < 0.60: + sep = 0.06 + else: + sep = 0.15 + + self.cylinder(radius, s, 0.5 * self.width - sep, y, z, 'Z', verts, faces, matids) + self.cylinder(radius, s, sep - 0.5 * self.width, y, z, 'Z', verts, faces, matids) + + def cathenary(self, num, x, y, z, r, h, w, verts, faces, matids): + # pts = [round(v.co.z, 4) for v in C.object.data.splines[0].points] + zs = [-0.0, -0.3056, -0.5556, -0.75, -0.8889, -0.9722] + seg = 6 + deg = 2 * pi / seg + da = pi / 24 + nv = len(verts) + for j in range(num): + # rotate on y axis + ay = -pi / 4 + for i, zi in enumerate(zs): + xi = 1 / 12 * i + tM = Matrix([ + [r * sin(ay), 0, r * cos(ay), x + xi * w], + [0, r, 0, y], + [-r * cos(ay), 0, r * sin(ay), z + zi * h], + [0, 0, 0, 1] + ]) + ay += da + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 0)) for a in range(seg)]) + + # lower vert (center) + tM = Matrix([ + [r * sin(ay), 0, r * cos(ay), x + 0.5 * w], + [0, r, 0, y], + [-r * cos(ay), 0, r * sin(ay), z - h], + [0, 0, 0, 1] + ]) + ay += da + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 0)) for a in range(seg)]) + + for i, zi in enumerate(reversed(zs)): + xi = 0.5 + 1 / 12 * (i + 1) + tM = Matrix([ + [r * sin(ay), 0, r * cos(ay), x + xi * w], + [0, r, 0, y], + [-r * cos(ay), 0, r * sin(ay), z + zi * h], + [0, 0, 0, 1] + ]) + ay += da + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 0)) for a in range(seg)]) + + x += w + + for s in range(12): + faces.extend([tuple([nv + i + f for f in (0, 1, seg + 1, seg)]) for i in range(seg - 1)]) + faces.append((nv + seg - 1, nv, nv + seg, nv + 2 * seg - 1)) + nv += seg + matids.extend([2 for i in range(seg)]) + nv += seg + + def vertical_slotted(self, verts, faces, matids): + c = cos(self.angle) + s = sin(self.angle) + x = 0.5 * self.depth + z = self.altitude + h = self.height + nv = len(verts) + num = ceil(self.width / (0.85 * self.depth)) + spacing = max(0.002, (self.width / num) * self.ratio / 100) + x0 = 0.5 * spacing - 0.5 * self.width + dx = c * x + dy = s * x + for i in range(num): + tM = Matrix([ + [dx, -dy, 0, x0 + i * spacing], + [dy, dx, 0, 0], + [0, 0, h, z], + [0, 0, 0, 1] + ]) + verts.extend([tM * Vector(v) for v in [ + [1, 0, 1], + [-1, 0, 1], + [-1, 0, 0], + [1, 0, 0] + ]]) + faces.append(tuple([nv + 4 * i + j for j in range(4)])) + matids.append(1) + + # cathenary scaling estimation + h = (self.width / num) * sin(acos(self.ratio / 101.003) * 1.01003) + w = self.ratio / 100 + # pts = [round(v.co.z, 4) for v in C.object.data.splines[0].points] + + r = 0.00025 + self.cathenary(num - 1, x0 + dx, dy, z, r, h, spacing, verts, faces, matids) + self.cathenary(num - 1, x0 - dx, -dy, z, r, h, spacing, verts, faces, matids) + + def japanese(self, verts, faces, matids): + z = self.altitude + nv = len(verts) + num = self.panels + w = self.width / num + h = self.height + dx = self.ratio / 100 * w + for i in range(num): + x = -0.5 * self.width + i * dx + y = 0.01 * (2 * (i % 2) - 1) + verts.extend([Vector(v) for v in [ + [x + w, y, h + z], + [x, y, h + z], + [x, y, z], + [x + w, y, z] + ]]) + faces.append(tuple([nv + 4 * i + j for j in range(4)])) + matids.append(1) + + def setup_manipulators(self): + if len(self.manipulators) < 1: + # add manipulator for x property + s = self.manipulators.add() + s.prop1_name = "width" + s.prop2_name = "x" + s.type_key = 'SNAP_SIZE_LOC' + + # add manipulator for y property + s = self.manipulators.add() + s.prop1_name = "height" + s.type_key = 'SIZE' + s.normal = Vector((0, 1, 0)) + + # add manipulator for z property + s = self.manipulators.add() + s.prop1_name = "altitude" + s.type_key = 'SIZE' + # draw this one on xz plane + s.normal = Vector((0, 1, 0)) + + def update(self, context): + + # provide support for "copy to selected" + o = self.find_in_selection(context, self.auto_update) + + if o is None: + return + + # dynamically create manipulators when needed + self.setup_manipulators() + verts = [] + faces = [] + matids = [] + self.frame(verts, faces, matids) + + if self.style == 'VENITIAN': + self.venitian_slats(verts, faces, matids) + elif self.style == 'SLAT': + self.roller_slats(verts, faces, matids) + elif self.style == 'BLADES': + self.roller_blades(verts, faces, matids) + elif self.style == 'ROLLER': + self.roller_curtain(verts, faces, matids) + elif self.style == 'PLATED': + self.plated_blind(verts, faces, matids) + elif self.style == 'VERTICAL_SLOTTED': + self.vertical_slotted(verts, faces, matids) + elif self.style == 'JAPANESE': + self.japanese(verts, faces, matids) + + # update your mesh from parameters + bmed.buildmesh(context, + o, + verts, + faces, + matids=matids, + weld=False) + + # update manipulators location (3d location in object coordsystem) + x, y = 0.5 * self.width, 0 + self.manipulators[0].set_pts([(-x, -y, 0), (x, -y, 0), (1, 0, 0)]) + self.manipulators[1].set_pts([(x, -y, self.altitude), (x, -y, self.altitude + self.height), (-1, 0, 0)]) + self.manipulators[2].set_pts([(x, -y, 0), (x, -y, self.altitude), (-1, 0, 0)]) + + # always restore context + self.restore_context(context) + + +class ARCHIPACK_PT_blind(Panel): + bl_idname = "ARCHIPACK_PT_blind" + bl_label = "Blind" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'ArchiPack' + + @classmethod + def poll(cls, context): + # ensure your object panel only show when active object is the right one + return archipack_blind.filter(context.active_object) + + def draw(self, context): + o = context.active_object + if not archipack_blind.filter(o): + return + layout = self.layout + + # retrieve datablock of your object + props = archipack_blind.datablock(o) + + # Manipulate mode operator + layout.operator('archipack.blind_manipulate', icon='HAND') + + box = layout.box() + row = box.row(align=True) + + # Presets operators + row.operator("archipack.blind_preset_menu", + text=bpy.types.ARCHIPACK_OT_blind_preset_menu.bl_label) + row.operator("archipack.blind_preset", + text="", + icon='ZOOMIN') + row.operator("archipack.blind_preset", + text="", + icon='ZOOMOUT').remove_active = True + + box = layout.box() + box.prop(props, 'style') + box.prop(props, 'width') + box.prop(props, 'height') + box.prop(props, 'altitude') + box.prop(props, 'ratio') + + box = layout.box() + box.label(text="Slat") + + if props.style == 'JAPANESE': + box.prop(props, 'panels') + + else: + if props.style != 'ROLLER': + box.prop(props, 'depth') + + if props.style in {'ROLLER', 'BLADES'}: + box.prop(props, 'blind_angle') + + elif props.style != 'PLATED': + box.prop(props, 'angle') + + box = layout.box() + box.label(text="Frame") + box.prop(props, 'frame_height') + box.prop(props, 'frame_depth') + + +class ARCHIPACK_OT_blind(ArchipackCreateTool, Operator): + bl_idname = "archipack.blind" + bl_label = "Blind" + bl_description = "Create Blind" + bl_category = 'Archipack' + bl_options = {'REGISTER', 'UNDO'} + + width = FloatProperty( + name='Width', + min=0.10, default=1, precision=3, + description='Total width' + ) + height = FloatProperty( + name='Height', + min=0.1, default=1.2, precision=3, + description='Height' + ) + altitude = FloatProperty( + name='Altitude', + min=0, default=0, precision=3, + description='Altitude' + ) + style = EnumProperty( + items=( + ('VENITIAN', 'Venitian', 'Venitian', 0), # / / /// + ('SLAT', 'Slat', 'Slat', 1), # / / / + ('ROLLER', 'Roller', 'Roller', 2), # __________ + ('BLADES', 'Blades', 'Blades', 3), # _ _ ___ + ('PLATED', 'Plated', 'Plated', 4), # Vertical \/\/ -> regular + # ('CELLULAR', 'Cellular blind', 'Cellular blind', 5), # OOOO -> venitian like variant + ('JAPANESE', 'Japanese', 'Japanese', 6), # _- -> horizontal + ('VERTICAL_SLOTTED', 'Vertical slotted', 'Vertical slotted', 7) # / / -> horizontal + ), + default='SLAT' + ) + + def create(self, context): + + # Create an empty mesh datablock + m = bpy.data.meshes.new("Blind") + + # Create an object using the mesh datablock + o = bpy.data.objects.new("Blind", m) + + # Add your properties on mesh datablock + d = m.archipack_blind.add() + + d.width = self.width + d.height = self.height + d.altitude = self.altitude + d.style = self.style + + # Link object into scene + context.scene.objects.link(o) + + # select and make active + o.select = True + context.scene.objects.active = o + + # Load preset into datablock + self.load_preset(d) + + # add a material + self.add_material(o) + return o + + def execute(self, context): + if context.mode == "OBJECT": + bpy.ops.object.select_all(action="DESELECT") + o = self.create(context) + o.location = bpy.context.scene.cursor_location + o.select = True + context.scene.objects.active = o + + # Start manipulate mode + self.manipulate() + return {'FINISHED'} + else: + self.report({'WARNING'}, "Archipack: Option only valid in Object mode") + return {'CANCELLED'} + + +class ARCHIPACK_OT_blind_preset_menu(PresetMenuOperator, Operator): + bl_idname = "archipack.blind_preset_menu" + bl_label = "Blind preset" + preset_subdir = "archipack_blind" + + +class ARCHIPACK_OT_blind_preset(ArchipackPreset, Operator): + """Add a Blind Preset""" + bl_idname = "archipack.blind_preset" + bl_label = "Add Blind preset" + preset_menu = "ARCHIPACK_OT_blind_preset_menu" + + @property + def blacklist(self): + return ['manipulators'] + + +class ARCHIPACK_OT_blind_manipulate(Operator): + bl_idname = "archipack.blind_manipulate" + bl_label = "Manipulate" + bl_description = "Manipulate" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + return archipack_blind.filter(context.active_object) + + def invoke(self, context, event): + d = archipack_blind.datablock(context.active_object) + d.manipulable_invoke(context) + return {'FINISHED'} + + +def register(): + bpy.utils.register_class(archipack_blind) + Mesh.archipack_blind = CollectionProperty(type=archipack_blind) + bpy.utils.register_class(ARCHIPACK_PT_blind) + bpy.utils.register_class(ARCHIPACK_OT_blind) + bpy.utils.register_class(ARCHIPACK_OT_blind_preset_menu) + bpy.utils.register_class(ARCHIPACK_OT_blind_preset) + bpy.utils.register_class(ARCHIPACK_OT_blind_manipulate) + + +def unregister(): + bpy.utils.unregister_class(archipack_blind) + del Mesh.archipack_blind + bpy.utils.unregister_class(ARCHIPACK_PT_blind) + bpy.utils.unregister_class(ARCHIPACK_OT_blind) + bpy.utils.unregister_class(ARCHIPACK_OT_blind_preset_menu) + bpy.utils.unregister_class(ARCHIPACK_OT_blind_preset) + bpy.utils.unregister_class(ARCHIPACK_OT_blind_manipulate) diff --git a/archipack_window.py b/archipack_window.py index 0337a78..7cfa95a 100644 --- a/archipack_window.py +++ b/archipack_window.py @@ -583,6 +583,19 @@ class archipack_window(ArchipackObject, Manipulable, PropertyGroup): unit='LENGTH', subtype='DISTANCE', description='tablet height', update=update, ) + blind_inside = BoolProperty( + default=False, + name="Blind inside", + description="Generate a blind inside", + update=update + ) + blind_outside = BoolProperty( + default=False, + name="Blind outside", + description="Generate a blind outside", + update=update + ) + blind_enable = BoolProperty( name="Blind", default=False, update=update, @@ -710,7 +723,7 @@ class archipack_window(ArchipackObject, Manipulable, PropertyGroup): description="Generate a portal", update=update ) - + @property def shape(self): if self.window_type == 'RAIL': @@ -904,31 +917,6 @@ def in_tablet(self): closed_path=False # closed path ) - @property - def blind(self): - # profil blind - # y0 - # | / | / | / - # y1 - # xn x1 x0 - dx = self.z / self.blind_z - nx = int(self.z / dx) - y0 = -0.5 * self.offset - # -0.5 * self.y + 0.5 * (0.5 * self.y - self.offset) - # 0.5 * (-0.5 * self.y-0.5 * self.offset) - y1 = y0 + self.blind_y - nx = int(self.z / self.blind_z) - dx = self.z / nx - open = 1.0 - self.blind_open / 100 - return WindowPanel( - False, # profil closed - [int((i + (i % 2)) / 2) for i in range(2 * nx)], # x index - [self.z - (dx * i * open) for i in range(nx + 1)], # x - [[y1, y0][i % 2] for i in range(2 * nx)], # - [5 for i in range(2 * nx - 1)], # material index - closed_path=False # - ) - @property def verts(self): center, origin, size, radius = self.get_radius() @@ -979,9 +967,6 @@ def verts(self): Vector((size.x + 2 * (self.frame_x + self.in_tablet_x), size.y, size.z)), radius, self.angle_y, 0, shape_z=None, path_type='HORIZONTAL') - if is_not_circle and self.blind_enable: - verts += self.blind.vertices(self.curve_steps, offset, center, origin, - Vector((-self.x, 0, 0)), radius, 0, 0, shape_z=None, path_type='HORIZONTAL') return verts @property @@ -1002,11 +987,7 @@ def faces(self): tablet = self.in_tablet faces += tablet.faces(self.curve_steps, path_type='HORIZONTAL', offset=verts_offset) verts_offset += tablet.n_verts(self.curve_steps, path_type='HORIZONTAL') - if is_not_circle and self.blind_enable: - blind = self.blind - faces += blind.faces(self.curve_steps, path_type='HORIZONTAL', offset=verts_offset) - verts_offset += blind.n_verts(self.curve_steps, path_type='HORIZONTAL') - + return faces @property @@ -1019,8 +1000,6 @@ def matids(self): mat += self.out_tablet.mat(self.curve_steps, 0, 0, path_type='HORIZONTAL') if is_not_circle and self.in_tablet_enable: mat += self.in_tablet.mat(self.curve_steps, 0, 0, path_type='HORIZONTAL') - if is_not_circle and self.blind_enable: - mat += self.blind.mat(self.curve_steps, 0, 0, path_type='HORIZONTAL') return mat @property @@ -1038,11 +1017,82 @@ def uvs(self): if is_not_circle and self.in_tablet_enable: uvs += self.in_tablet.uv(self.curve_steps, center, origin, size, radius, self.angle_y, 0, 0, self.frame_x, path_type='HORIZONTAL') - if is_not_circle and self.blind_enable: - uvs += self.blind.uv(self.curve_steps, center, origin, size, radius, - self.angle_y, 0, 0, self.frame_x, path_type='HORIZONTAL') return uvs + + def find_blind(self, o, inside): + for child in o.children: + if child.type == 'MESH' and 'archipack_blind' in child.data: + loc = o.matrix_world.inverted() * child.matrix_world.translation + if inside: + if loc.y > 0: + return child + elif loc.y < 0: + return child + return None + + def update_blind(self, context, o, inside, enabled): + blind = self.find_blind(o, inside) + + if enabled: + if inside: + overflow = 2 * self.frame_overflow + style = 'VENITIAN' + else: + overflow = 0 + style = 'SLAT' + + x = self.x + overflow + z = self.z + overflow + a = self.altitude - 0.5 * overflow + + if blind is None: + bpy.ops.archipack.blind( + width=x, + height=z, + altitude=a, + style=style, + auto_manipulate=False + ) + blind = context.active_object + blind.parent = o + else: + d = blind.data.archipack_blind[0] + if (d.width != x or + d.height != z or + d.altitude != a): + d.auto_update = False + d.width = x + d.height = z + d.altitude = a + d.auto_update = True + + if inside: + # offset for handle + half width + w = 0.06 + 0.02 + tM = Matrix([ + [-1, 0, 0, 0], + [0, -1, 0, 0.5 * self.y + w], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]) + else: + tM = Matrix([ + [1, 0, 0, 0], + [0, 1, 0, -0.5 * self.offset], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]) + blind.matrix_world = o.matrix_world * tM + + elif blind is not None: + d = blind.data + context.scene.objects.unlink(blind) + bpy.data.objects.remove(blind) + bpy.data.meshes.remove(d) + + context.scene.objects.active = o + def find_portal(self, o): for child in o.children: if child.type == 'LAMP': @@ -1165,7 +1215,12 @@ def _synch_portal(self, context, o, linked, childs): # update portal dl = archipack_window.datablock(linked) dl.update_portal(context, linked) - + + def _synch_blind(self, context, o, linked, childs): + dl = archipack_window.datablock(linked) + dl.update_blind(context, linked, True, dl.blind_inside) + dl.update_blind(context, linked, False, dl.blind_outside) + def _synch_childs(self, context, o, linked, childs): """ sub synch childs nodes of linked object @@ -1262,6 +1317,7 @@ def synch_childs(self, context, o): for linked in context.selected_objects: if linked != o: self._synch_portal(context, o, linked, childs) + self._synch_blind(context, o, linked, childs) self._synch_childs(context, o, linked, childs) if hole is not None: self._synch_hole(context, linked, hole) @@ -1481,6 +1537,8 @@ def update(self, context, childs_only=False): bmed.buildmesh(context, o, self.verts, self.faces, self.matids, self.uvs) self.update_portal(context, o) + self.update_blind(context, o, True, self.blind_inside) + self.update_blind(context, o, False, self.blind_outside) self.update_childs(context, o) # update hole @@ -1554,41 +1612,6 @@ def interactive_hole(self, context, o): bmed.buildmesh(context, hole_obj, verts, faces, matids=matids, uvs=uvs, auto_smooth=False) return hole_obj - def robust_hole(self, context, tM): - hole = self.hole - center, origin, size, radius = self.get_radius() - - if self.out_frame is False: - x0 = 0 - else: - x0 = min(self.frame_x - 0.001, self.out_frame_y + self.out_frame_offset) - - if self.out_tablet_enable: - x0 -= min(self.frame_x - 0.001, self.out_tablet_z) - shape_z = [0, x0] - - m = bpy.data.meshes.new("hole") - o = bpy.data.objects.new("hole", m) - o['archipack_robusthole'] = True - context.scene.objects.link(o) - verts = hole.vertices(self.curve_steps, Vector((0, self.altitude, 0)), center, origin, size, radius, - self.angle_y, 0, shape_z=shape_z, path_type=self.shape) - - verts = [tM * Vector(v) for v in verts] - - faces = hole.faces(self.curve_steps, path_type=self.shape) - - matids = hole.mat(self.curve_steps, 2, 2, path_type=self.shape) - - uvs = hole.uv(self.curve_steps, center, origin, size, radius, - self.angle_y, 0, 0, self.frame_x, path_type=self.shape) - - bmed.buildmesh(context, o, verts, faces, matids=matids, uvs=uvs, auto_smooth=False) - # MaterialUtils.add_wall2_materials(o) - o.select = True - context.scene.objects.active = o - return o - class ARCHIPACK_PT_window(Panel): bl_idname = "ARCHIPACK_PT_window" @@ -1695,12 +1718,15 @@ def draw(self, context): box.prop(prop, 'in_tablet_y') box.prop(prop, 'in_tablet_z') box = layout.box() - row = box.row(align=True) + box.prop(prop, 'blind_inside') + box.prop(prop, 'blind_outside') + """ row.prop(prop, 'blind_enable') if prop.blind_enable: box.prop(prop, 'blind_open') box.prop(prop, 'blind_y') box.prop(prop, 'blind_z') + """ if prop.window_shape != 'CIRCLE': row = layout.row() if prop.display_panels: @@ -1734,8 +1760,9 @@ def draw(self, context): box.prop(prop, 'hole_outside_mat') layout.prop(prop, 'portal', icon="LAMP_AREA") + - + class ARCHIPACK_PT_window_panel(Panel): bl_idname = "ARCHIPACK_PT_window_panel" bl_label = "Window panel" From 8ff99756537dd175f81a072eca3303e439cd1779 Mon Sep 17 00:00:00 2001 From: s-leger Date: Sun, 7 Jan 2018 01:38:56 +0100 Subject: [PATCH 2/5] blind basic preset --- presets/archipack_blind/default.png | Bin 0 -> 10660 bytes presets/archipack_blind/default.py | 13 ++++ presets/archipack_materials/blind.txt | 3 + presets/archipack_materials/door.txt | 8 +- presets/archipack_materials/fence.txt | 8 +- presets/archipack_materials/floor.txt | 42 +++++------ presets/archipack_materials/handle.txt | 4 +- presets/archipack_materials/roof.txt | 97 ++++++++++++------------- presets/archipack_materials/slab.txt | 6 +- presets/archipack_materials/stair.txt | 12 +-- presets/archipack_materials/truss.txt | 2 +- presets/archipack_materials/wall.txt | 16 ++-- presets/archipack_materials/wall2.txt | 16 ++-- presets/archipack_materials/window.txt | 12 +-- 14 files changed, 127 insertions(+), 112 deletions(-) create mode 100644 presets/archipack_blind/default.png create mode 100644 presets/archipack_blind/default.py create mode 100644 presets/archipack_materials/blind.txt diff --git a/presets/archipack_blind/default.png b/presets/archipack_blind/default.png new file mode 100644 index 0000000000000000000000000000000000000000..ae175fc35a7350dfa5df9bca005d65b4adebb441 GIT binary patch literal 10660 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 Zs=WV9Uv`7}Zw4ci@m;e!mA9Rr{~w=rLV5rI literal 0 HcmV?d00001 diff --git a/presets/archipack_blind/default.py b/presets/archipack_blind/default.py new file mode 100644 index 0000000..e2da882 --- /dev/null +++ b/presets/archipack_blind/default.py @@ -0,0 +1,13 @@ +import bpy +d = bpy.context.active_object.data.archipack_blind[0] +bpy.ops.archipack.material(category='blind', material='DEFAULT') + +d.altitude = 1.7000000476837158 +d.angle = 0.0 +d.depth = 0.03999999910593033 +d.frame_depth = 0.03999999910593033 +d.frame_height = 0.20000000298023224 +d.height = 1.7000000476837158 +d.ratio = 100.0 +d.stack_inside_frame = True +d.width = 1.0 diff --git a/presets/archipack_materials/blind.txt b/presets/archipack_materials/blind.txt new file mode 100644 index 0000000..5982132 --- /dev/null +++ b/presets/archipack_materials/blind.txt @@ -0,0 +1,3 @@ +DEFAULT##|##Frame +DEFAULT##|##Blind +DEFAULT##|##Rope diff --git a/presets/archipack_materials/door.txt b/presets/archipack_materials/door.txt index 1895149..a909767 100644 --- a/presets/archipack_materials/door.txt +++ b/presets/archipack_materials/door.txt @@ -1,4 +1,4 @@ -DEFAULT##|##Door_inside -DEFAULT##|##Door_outside -DEFAULT##|##Door_glass -DEFAULT##|##Door_metal +DEFAULT##|##Door_inside +DEFAULT##|##Door_outside +DEFAULT##|##Door_glass +DEFAULT##|##Door_metal diff --git a/presets/archipack_materials/fence.txt b/presets/archipack_materials/fence.txt index 0082758..b784b8e 100644 --- a/presets/archipack_materials/fence.txt +++ b/presets/archipack_materials/fence.txt @@ -1,4 +1,4 @@ -DEFAULT##|##Fence_wood -DEFAULT##|##Fence_metal -DEFAULT##|##Fence_glass -DEFAULT##|##Fence_concrete +DEFAULT##|##Fence_wood +DEFAULT##|##Fence_metal +DEFAULT##|##Fence_glass +DEFAULT##|##Fence_concrete diff --git a/presets/archipack_materials/floor.txt b/presets/archipack_materials/floor.txt index ad94757..981e9d2 100644 --- a/presets/archipack_materials/floor.txt +++ b/presets/archipack_materials/floor.txt @@ -1,22 +1,22 @@ -DEFAULT##|##Floor_grout -DEFAULT##|##Floor_alt1 -DEFAULT##|##Floor_alt2 -DEFAULT##|##Floor_alt3 -DEFAULT##|##Floor_alt4 -DEFAULT##|##Floor_alt5 -DEFAULT##|##Floor_alt6 -DEFAULT##|##Floor_alt7 -DEFAULT##|##Floor_alt8 -DEFAULT##|##Floor_alt9 -DEFAULT##|##Floor_alt10 -TILES##|##Floor_grout -TILES##|##Floor_tiles_alt1 -TILES##|##Floor_tiles_alt2 -TILES##|##Floor_tiles_alt3 -TILES##|##Floor_tiles_alt4 -TILES##|##Floor_tiles_alt5 -TILES##|##Floor_tiles_alt6 -TILES##|##Floor_alt7 -TILES##|##Floor_alt8 -TILES##|##Floor_alt9 +DEFAULT##|##Floor_grout +DEFAULT##|##Floor_alt1 +DEFAULT##|##Floor_alt2 +DEFAULT##|##Floor_alt3 +DEFAULT##|##Floor_alt4 +DEFAULT##|##Floor_alt5 +DEFAULT##|##Floor_alt6 +DEFAULT##|##Floor_alt7 +DEFAULT##|##Floor_alt8 +DEFAULT##|##Floor_alt9 +DEFAULT##|##Floor_alt10 +TILES##|##Floor_grout +TILES##|##Floor_tiles_alt1 +TILES##|##Floor_tiles_alt2 +TILES##|##Floor_tiles_alt3 +TILES##|##Floor_tiles_alt4 +TILES##|##Floor_tiles_alt5 +TILES##|##Floor_tiles_alt6 +TILES##|##Floor_alt7 +TILES##|##Floor_alt8 +TILES##|##Floor_alt9 TILES##|##Floor_alt10 \ No newline at end of file diff --git a/presets/archipack_materials/handle.txt b/presets/archipack_materials/handle.txt index 458cb1c..4b1be1c 100644 --- a/presets/archipack_materials/handle.txt +++ b/presets/archipack_materials/handle.txt @@ -1,2 +1,2 @@ -DEFAULT##|##Handle_inside -DEFAULT##|##Handle_outside +DEFAULT##|##Handle_inside +DEFAULT##|##Handle_outside diff --git a/presets/archipack_materials/roof.txt b/presets/archipack_materials/roof.txt index 1527ea7..89e3127 100644 --- a/presets/archipack_materials/roof.txt +++ b/presets/archipack_materials/roof.txt @@ -1,49 +1,48 @@ -DEFAULT##|##Roof_sheeting -DEFAULT##|##Roof_rakes -DEFAULT##|##Roof_eaves -DEFAULT##|##Roof_ridge -DEFAULT##|##Roof_rafter -DEFAULT##|##Roof_valley -DEFAULT##|##Roof_hip_tiles -DEFAULT##|##Roof_tiles -DEFAULT##|##Roof_tiles2 -DEFAULT##|##Roof_tiles3 -DEFAULT##|##Roof_tiles4 -DEFAULT##|##Roof_tiles5 -STONE##|##Roof_sheeting -STONE##|##Roof_rakes -STONE##|##Roof_eaves -STONE##|##Roof_ridge -STONE##|##Roof_rafter -STONE##|##Roof_valley -STONE##|##Roof_hip_stone -STONE##|##Roof_tiles_stone -STONE##|##Roof_tiles_stone2 -STONE##|##Roof_tiles_stone3 -STONE##|##Roof_tiles_stone4 -STONE##|##Roof_tiles_stone5 -BLACK##|##Roof_sheeting -BLACK##|##Roof_rakes -BLACK##|##Roof_eaves -BLACK##|##Roof_ridge -BLACK##|##Roof_rafter -BLACK##|##Roof_valley -BLACK##|##Roof_hip_black -BLACK##|##Roof_tiles_black -BLACK##|##Roof_tiles_black2 -BLACK##|##Roof_tiles_black3 -BLACK##|##Roof_tiles_black4 -BLACK##|##Roof_tiles_black5 -METAL##|##Roof_sheeting -METAL##|##Roof_rakes -METAL##|##Roof_eaves -METAL##|##Roof_ridge -METAL##|##Roof_rafter -METAL##|##Roof_valley -METAL##|##Roof_hip_metal -METAL##|##Roof_metal -METAL##|##Roof_metal2 -METAL##|##Roof_metal3 -METAL##|##Roof_metal4 -METAL##|##Roof_metal5 - +DEFAULT##|##Roof_sheeting +DEFAULT##|##Roof_rakes +DEFAULT##|##Roof_eaves +DEFAULT##|##Roof_ridge +DEFAULT##|##Roof_rafter +DEFAULT##|##Roof_valley +DEFAULT##|##Roof_hip_tiles +DEFAULT##|##Roof_tiles +DEFAULT##|##Roof_tiles2 +DEFAULT##|##Roof_tiles3 +DEFAULT##|##Roof_tiles4 +DEFAULT##|##Roof_tiles5 +STONE##|##Roof_sheeting +STONE##|##Roof_rakes +STONE##|##Roof_eaves +STONE##|##Roof_ridge +STONE##|##Roof_rafter +STONE##|##Roof_valley +STONE##|##Roof_hip_stone +STONE##|##Roof_tiles_stone +STONE##|##Roof_tiles_stone2 +STONE##|##Roof_tiles_stone3 +STONE##|##Roof_tiles_stone4 +STONE##|##Roof_tiles_stone5 +BLACK##|##Roof_sheeting +BLACK##|##Roof_rakes +BLACK##|##Roof_eaves +BLACK##|##Roof_ridge +BLACK##|##Roof_rafter +BLACK##|##Roof_valley +BLACK##|##Roof_hip_black +BLACK##|##Roof_tiles_black +BLACK##|##Roof_tiles_black2 +BLACK##|##Roof_tiles_black3 +BLACK##|##Roof_tiles_black4 +BLACK##|##Roof_tiles_black5 +METAL##|##Roof_sheeting +METAL##|##Roof_rakes +METAL##|##Roof_eaves +METAL##|##Roof_ridge +METAL##|##Roof_rafter +METAL##|##Roof_valley +METAL##|##Roof_hip_metal +METAL##|##Roof_metal +METAL##|##Roof_metal2 +METAL##|##Roof_metal3 +METAL##|##Roof_metal4 +METAL##|##Roof_metal5 \ No newline at end of file diff --git a/presets/archipack_materials/slab.txt b/presets/archipack_materials/slab.txt index 8d3490f..a14bc82 100644 --- a/presets/archipack_materials/slab.txt +++ b/presets/archipack_materials/slab.txt @@ -1,3 +1,3 @@ -DEFAULT##|##Slab_bottom -DEFAULT##|##Slab_top -DEFAULT##|##Slab_side +DEFAULT##|##Slab_bottom +DEFAULT##|##Slab_top +DEFAULT##|##Slab_side diff --git a/presets/archipack_materials/stair.txt b/presets/archipack_materials/stair.txt index 44966d3..c11e5fe 100644 --- a/presets/archipack_materials/stair.txt +++ b/presets/archipack_materials/stair.txt @@ -1,6 +1,6 @@ -DEFAULT##|##Stair_ceiling -DEFAULT##|##Stair_white -DEFAULT##|##Stair_concrete -DEFAULT##|##Stair_wood -DEFAULT##|##Stair_metal -DEFAULT##|##Stair_glass +DEFAULT##|##Stair_ceiling +DEFAULT##|##Stair_white +DEFAULT##|##Stair_concrete +DEFAULT##|##Stair_wood +DEFAULT##|##Stair_metal +DEFAULT##|##Stair_glass diff --git a/presets/archipack_materials/truss.txt b/presets/archipack_materials/truss.txt index 00718d4..5f3bab0 100644 --- a/presets/archipack_materials/truss.txt +++ b/presets/archipack_materials/truss.txt @@ -1 +1 @@ -DEFAULT##|##Truss_truss +DEFAULT##|##Truss_truss diff --git a/presets/archipack_materials/wall.txt b/presets/archipack_materials/wall.txt index e489e4a..514a7e2 100644 --- a/presets/archipack_materials/wall.txt +++ b/presets/archipack_materials/wall.txt @@ -1,8 +1,8 @@ -DEFAULT##|##Wall_inside -DEFAULT##|##Wall_outside -DEFAULT##|##Wall_cuts -DEFAULT##|##Wall_alt1 -DEFAULT##|##Wall_alt2 -DEFAULT##|##Wall_alt3 -DEFAULT##|##Wall_alt4 -DEFAULT##|##Wall_alt5 +DEFAULT##|##Wall_inside +DEFAULT##|##Wall_outside +DEFAULT##|##Wall_cuts +DEFAULT##|##Wall_alt1 +DEFAULT##|##Wall_alt2 +DEFAULT##|##Wall_alt3 +DEFAULT##|##Wall_alt4 +DEFAULT##|##Wall_alt5 diff --git a/presets/archipack_materials/wall2.txt b/presets/archipack_materials/wall2.txt index 784cd38..77a864d 100644 --- a/presets/archipack_materials/wall2.txt +++ b/presets/archipack_materials/wall2.txt @@ -1,8 +1,8 @@ -DEFAULT##|##Wall2_outside -DEFAULT##|##Wall2_inside -DEFAULT##|##Wall2_cuts -DEFAULT##|##Wall2_alt1 -DEFAULT##|##Wall2_alt2 -DEFAULT##|##Wall2_alt3 -DEFAULT##|##Wall2_alt4 -DEFAULT##|##Wall2_alt5 +DEFAULT##|##Wall2_outside +DEFAULT##|##Wall2_inside +DEFAULT##|##Wall2_cuts +DEFAULT##|##Wall2_alt1 +DEFAULT##|##Wall2_alt2 +DEFAULT##|##Wall2_alt3 +DEFAULT##|##Wall2_alt4 +DEFAULT##|##Wall2_alt5 diff --git a/presets/archipack_materials/window.txt b/presets/archipack_materials/window.txt index 8f5f857..e0e58ab 100644 --- a/presets/archipack_materials/window.txt +++ b/presets/archipack_materials/window.txt @@ -1,6 +1,6 @@ -DEFAULT##|##Window_inside -DEFAULT##|##Window_outside -DEFAULT##|##Window_glass -DEFAULT##|##Window_metal -DEFAULT##|##Window_stone -DEFAULT##|##Window_blind +DEFAULT##|##Window_inside +DEFAULT##|##Window_outside +DEFAULT##|##Window_glass +DEFAULT##|##Window_metal +DEFAULT##|##Window_stone +DEFAULT##|##Window_blind From ebfabb39e8c06e065639774a5e4f101342ddbadc Mon Sep 17 00:00:00 2001 From: s-leger Date: Tue, 9 Jan 2018 16:11:31 +0100 Subject: [PATCH 3/5] [FEAUTRE] Window shutters --- __init__.py | 16 +- archipack_window.py | 895 ++++++++++++++++++++++++++++++++++++++++---- panel.py | 5 +- 3 files changed, 838 insertions(+), 78 deletions(-) diff --git a/__init__.py b/__init__.py index afd1361..3c66e3c 100644 --- a/__init__.py +++ b/__init__.py @@ -31,11 +31,11 @@ 'author': 's-leger', 'license': 'GPL', 'deps': '', - 'version': (1, 3, 5), + 'version': (1, 3, 6), 'blender': (2, 7, 8), 'location': 'View3D > Tools > Create > Archipack', 'warning': '', - 'wiki_url': 'https://github.com/s-leger/archipack/wiki', + 'wiki_url': 'https://s-leger.github.io/archipack/index.html', 'tracker_url': 'https://github.com/s-leger/archipack/issues', 'link': 'https://github.com/s-leger/archipack', 'support': 'COMMUNITY', @@ -600,11 +600,11 @@ def draw(self, context): icon='CURVE_DATA').preset_operator = "archipack.floor_from_curve" row = box.row(align=True) row.operator("archipack.blind_preset_menu", - text="Blind" - # , - # icon_value=icons["floor"].icon_id + text="Blind", + icon_value=icons["blind"].icon_id ).preset_operator = "archipack.blind" + box = layout.box() box.label(text="Custom objects") box.operator("archipack.wall", text="Custom wall") @@ -658,8 +658,12 @@ def draw_menu(self, context): text="Roof", icon_value=icons["roof"].icon_id ).preset_operator = "archipack.roof" + layout.operator("archipack.blind_preset_menu", + text="Blind", + icon_value=icons["blind"].icon_id + ).preset_operator = "archipack.blind" - + class ARCHIPACK_create_menu(Menu): bl_label = 'Archipack' bl_idname = 'ARCHIPACK_create_menu' diff --git a/archipack_window.py b/archipack_window.py index 7cfa95a..691def0 100644 --- a/archipack_window.py +++ b/archipack_window.py @@ -33,7 +33,7 @@ CollectionProperty, FloatVectorProperty, EnumProperty, StringProperty ) from mathutils import Vector, Matrix -from math import tan, sqrt +from math import tan, sqrt, pi, sin, cos from .bmesh_utils import BmeshEdit as bmed from .panel import Panel as WindowPanel from .archipack_handle import create_handle, window_handle_vertical_01, window_handle_vertical_02 @@ -424,6 +424,367 @@ def update(self, context): self.restore_context(context) +class archipack_window_shutter(ArchipackObject, PropertyGroup): + center = FloatVectorProperty( + subtype='XYZ' + ) + origin = FloatVectorProperty( + subtype='XYZ' + ) + size = FloatVectorProperty( + subtype='XYZ' + ) + radius = FloatVectorProperty( + subtype='XYZ' + ) + angle_y = FloatProperty( + name='angle', + unit='ROTATION', + subtype='ANGLE', + min=-1.5, max=1.5, + default=0, precision=2, + description='angle' + ) + frame_y = FloatProperty( + name='Depth', + min=0, + default=0.06, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='frame depth' + ) + frame_x = FloatProperty( + name='Width', + min=0, + default=0.06, precision=2, + unit='LENGTH', subtype='DISTANCE', + description='frame width' + ) + curve_steps = IntProperty( + name="curve steps", + min=1, + max=128, + default=1 + ) + shape = EnumProperty( + name='Shape', + items=( + ('RECTANGLE', 'Rectangle', '', 0), + ('ROUND', 'Top Round', '', 1), + ('ELLIPSIS', 'Top elliptic', '', 2), + ('QUADRI', 'Top oblique', '', 3), + ('CIRCLE', 'Full circle', '', 4) + ), + default='RECTANGLE' + ) + pivot = FloatProperty( + name='pivot', + min=-1, max=1, + default=-1, precision=2, + description='pivot' + ) + offset = FloatProperty( + name='offset', + default=0, precision=2, + description='x offset' + ) + hinge_enable = BoolProperty( + name="Hinge", + default=False + ) + hinge_count = IntProperty( + name="#Hinge", + min=0, + max=4, + default=2 + ) + + @property + def shutter(self): + + chanfer = 0.004 + border = self.frame_x + spacing = 0.75 * self.frame_x + x0 = 0 + x1 = border - 0.5 * spacing + x3 = chanfer + w = 0.5 * self.frame_y + # offset pivot point on outside part + y0 = 0 + y1 = y0 + w + y2 = y1 - 0.5 * w + y3 = y1 - chanfer + y4 = y0 + chanfer + + # profil carre avec support pour verre + # p ______ y1 + # / | y3 + # | + # x y2 + # | y4 + # \______| y0 + # x0 x3 x1 + # + + side = WindowPanel( + False, # closed + [2, 1, 0, 0, 0, 1, 2], # x index + [x0, x3, x1], + [y0, y0, y4, y2, y3, y1, y1], + [5, 5, 5, 5, 5, 5, 5], # materials + closed_path=True, # + subdiv_x=0, + subdiv_y=1 + ) + + # / y2-y3 + # __/ y1-y0 + # x2 x3 + # x2 = 0.5 * self.panel_spacing + x2 = 0.5 * spacing + x3 = x2 + chanfer + y2 = y1 - chanfer + y3 = y0 + chanfer + + face = WindowPanel( + False, # profil closed + [0, 1, 2], # x index + [0, x2, x3], + [y1, y1, y2], + [5, 5, 5], # material index + side_cap_front=2, # cap index + closed_path=True + ) + + back = WindowPanel( + False, # profil closed + [0, 1, 2], # x index + [x3, x2, 0], + [y3, y0, y0], + [5, 5, 5], # material index + side_cap_back=0, # cap index + closed_path=True + ) + + return side, face, back + + def hinge(self, altitude, verts): + chanfer = 0.004 + + seg = 12 + deg = 2 * pi / seg + radius = 0.005 + x = 0 + y = 0 + z = altitude + size = 0.03 + + d = (self.offset + self.pivot * chanfer) / radius + tM = Matrix([ + [radius, 0, 0, x], + [0, radius, 0, y], + [0, 0, size, z], + [0, 0, 0, 1] + ]) + if self.pivot < 0: + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 0)) for a in range(seg - 2)]) + verts.extend([ + tM * Vector((d, cos(deg * (seg - 3)), 0)), + tM * Vector((d, 1, 0)), + ]) + else: + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 0)) for a in range(3, seg + 1)]) + verts.extend([ + tM * Vector((d, 1, 0)), + tM * Vector((d, cos(deg * (seg - 3)), 0)), + ]) + + if self.pivot < 0: + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 1)) for a in range(seg - 2)]) + verts.extend([ + tM * Vector((d, cos(deg * (seg - 3)), 1)), + tM * Vector((d, 1, 1)), + ]) + else: + verts.extend([tM * Vector((sin(deg * a), cos(deg * a), 1)) for a in range(3, seg + 1)]) + verts.extend([ + tM * Vector((d, 1, 1)), + tM * Vector((d, cos(deg * (seg - 3)), 1)), + ]) + + @property + def verts(self): + + side, face, back = self.shutter + border = self.frame_x + spacing = 0.75 * self.frame_x + + x1 = border - 0.5 * spacing + offset = Vector((self.offset, 0, 0)) + verts = side.vertices(self.curve_steps, offset, self.center, self.origin, self.size, + self.radius, self.angle_y, self.pivot, shape_z=None, path_type=self.shape) + + p_radius = self.radius.copy() + p_radius.x -= x1 + p_radius.y -= x1 + + p_size = Vector((self.size.x - 2 * x1, (self.size.y - 2 * x1) / 2, 0)) + + for j in range(2): + if j < 1: + shape = 'RECTANGLE' + else: + shape = self.shape + offset = Vector(( + self.offset + self.pivot * x1, # + (self.pivot - 0.5) * self.size.x + p_size.x * 0.5 + x1, + p_size.y * j + x1, + 0)) + + origin = Vector(( + self.origin.x + self.pivot * x1, #p_size.x * 0.5 - 0.5 * self.size.x + x1, + p_size.y * j + x1, + 0)) + + verts += face.vertices(self.curve_steps, offset, self.center, origin, + p_size, p_radius, self.angle_y, self.pivot, shape_z=None, path_type=shape) + verts += back.vertices(self.curve_steps, offset, self.center, origin, + p_size, p_radius, self.angle_y, self.pivot, shape_z=None, path_type=shape) + + if self.hinge_enable: + z0 = 0.15 + dz = (self.size.y - 2 * z0) / (self.hinge_count - 1) + for j in range(self.hinge_count): + self.hinge(z0 + dz * j, verts) + + return verts + + @property + def faces(self): + + side, face, back = self.shutter + + faces = side.faces(self.curve_steps, path_type=self.shape) + faces_offset = side.n_verts(self.curve_steps, path_type=self.shape) + + for j in range(2): + if j < 1: + shape = 'RECTANGLE' + else: + shape = self.shape + faces += face.faces(self.curve_steps, path_type=shape, offset=faces_offset) + faces_offset += face.n_verts(self.curve_steps, path_type=shape) + faces += back.faces(self.curve_steps, path_type=shape, offset=faces_offset) + faces_offset += back.n_verts(self.curve_steps, path_type=shape) + + if self.hinge_enable: + seg = 12 + for j in range(self.hinge_count): + faces.append(tuple([faces_offset + i + seg for i in range(seg - 1, -1, -1)])) + faces.append(tuple([faces_offset + i for i in range(seg)])) + faces.extend([tuple([faces_offset + i + f for f in (1, 0, seg, seg + 1)]) for i in range(seg - 1)]) + faces.append(( + faces_offset, + faces_offset + seg - 1, + faces_offset + 2 * seg - 1, + faces_offset + seg + )) + + faces_offset += 2 * seg + + return faces + + @property + def uvs(self): + + side, face, back = self.shutter + + border = self.frame_x + spacing = 0.75 * self.frame_x + x1 = border - 0.5 * spacing + + uvs = side.uv(self.curve_steps, + self.center, + self.origin, + self.size, + self.radius, + self.angle_y, + self.pivot, + self.frame_x, 0, + path_type=self.shape) + + p_radius = self.radius.copy() + p_radius.x -= x1 + p_radius.y -= x1 + p_size = Vector((self.size.x - 2 * x1, (self.size.y - 2 * x1) / 2, 0)) + + for j in range(2): + if j < 1: + shape = 'RECTANGLE' + else: + shape = self.shape + origin = Vector(( + self.origin.x + self.pivot * x1, # p_size.x * 0.5 - 0.5 * self.size.x + x1, + p_size.y * j + x1, + 0)) + uvs += face.uv(self.curve_steps, self.center, origin, p_size, + p_radius, self.angle_y, self.pivot, 0, 0, path_type=shape) + uvs += back.uv(self.curve_steps, self.center, origin, p_size, + p_radius, self.angle_y, self.pivot, 0, 0, path_type=shape) + + if self.hinge_enable: + seg = 12 + deg = 2 * pi / seg + radius = 0.005 + x = 0 + y = 0 + z = 0 + size = 0.04 + tM = Matrix([ + [radius, 0, 0, x], + [0, radius, 0, y], + [0, 0, size, z], + [0, 0, 0, 1] + ]) + + for j in range(self.hinge_count): + uvs.append(tuple([(tM * Vector((sin(deg * a), cos(deg * a), 0))).to_2d() for a in range(seg)])) + uvs.append(tuple([(tM * Vector((sin(deg * a), cos(deg * a), 0))).to_2d() for a in range(seg)])) + uvs.extend([[(0,0), (0,1), (1,1), (1,0)] for i in range(seg)]) + + return uvs + + @property + def matids(self): + + side, face, back = self.shutter + mat = side.mat(self.curve_steps, 5, 5, path_type=self.shape) + for j in range(2): + if j < 1: + shape = 'RECTANGLE' + else: + shape = self.shape + mat += face.mat(self.curve_steps, 5, 5, path_type=shape) + mat += back.mat(self.curve_steps, 5, 5, path_type=shape) + + if self.hinge_enable: + for j in range(self.hinge_count): + seg = 12 + mat.extend([3, 3]) + mat.extend([3 for i in range(seg)]) + + return mat + + def update(self, context): + + o = self.find_in_selection(context) + + if o is None: + return + + bmed.buildmesh(context, o, self.verts, self.faces, self.matids, self.uvs) + + self.restore_context(context) + + class archipack_window(ArchipackObject, Manipulable, PropertyGroup): x = FloatProperty( name='Width', @@ -595,7 +956,7 @@ class archipack_window(ArchipackObject, Manipulable, PropertyGroup): description="Generate a blind outside", update=update ) - + # internal blind, keep for compatibility blind_enable = BoolProperty( name="Blind", default=False, update=update, @@ -723,7 +1084,20 @@ class archipack_window(ArchipackObject, Manipulable, PropertyGroup): description="Generate a portal", update=update ) - + shutter_enable = BoolProperty( + name="Shutters", + default=False, update=update, + ) + shutter_left = IntProperty( + name="#Left", + default=1, + update=update + ) + shutter_right = IntProperty( + name="#Right", + default=1, + update=update + ) @property def shape(self): if self.window_type == 'RAIL': @@ -919,7 +1293,7 @@ def in_tablet(self): @property def verts(self): - center, origin, size, radius = self.get_radius() + center, origin, size, radius = self.get_radius(self._x, self._z) is_not_circle = self.shape != 'CIRCLE' offset = Vector((0, self.altitude + self.frame_x - self.frame_overflow, 0)) verts = self.window.vertices(self.curve_steps, offset, center, origin, @@ -1004,7 +1378,7 @@ def matids(self): @property def uvs(self): - center, origin, size, radius = self.get_radius() + center, origin, size, radius = self.get_radius(self._x, self._z) uvs = self.window.uv(self.curve_steps, center, origin, size, radius, self.angle_y, 0, 0, self.frame_x, path_type=self.shape) is_not_circle = self.shape != 'CIRCLE' @@ -1030,17 +1404,22 @@ def find_blind(self, o, inside): return child return None - def update_blind(self, context, o, inside, enabled): + def update_blind(self, context, o, inside): blind = self.find_blind(o, inside) - + if inside: + enabled = self.blind_inside + overflow = 2 * self.frame_overflow + style = 'VENITIAN' + # half width + handle + y = 0.02 + 0.08 + else: + enabled = self.blind_outside + overflow = 0 + style = 'SLAT' + y = -0.5 * (self.y - self.offset) + if enabled: - if inside: - overflow = 2 * self.frame_overflow - style = 'VENITIAN' - else: - overflow = 0 - style = 'SLAT' x = self.x + overflow z = self.z + overflow @@ -1048,51 +1427,63 @@ def update_blind(self, context, o, inside, enabled): if blind is None: bpy.ops.archipack.blind( - width=x, - height=z, + x=x, + z=z, + offset_y=y, altitude=a, + frame_enable=inside, + frame_depth=2 * y, + frame_height=0.04, style=style, auto_manipulate=False ) blind = context.active_object blind.parent = o + blind.select = False else: + blind.select = True + context.scene.objects.active = blind d = blind.data.archipack_blind[0] - if (d.width != x or - d.height != z or + if (d.x != x or + d.z != z or + d.offset_y != y or d.altitude != a): d.auto_update = False - d.width = x - d.height = z + d.x = x + d.z = z + d.offset_y = y d.altitude = a d.auto_update = True - + blind.select = False + if inside: - # offset for handle + half width - w = 0.06 + 0.02 tM = Matrix([ [-1, 0, 0, 0], - [0, -1, 0, 0.5 * self.y + w], + [0, -1, 0, 0.5 * self.y], [0, 0, 1, 0], [0, 0, 0, 1] ]) else: tM = Matrix([ [1, 0, 0, 0], - [0, 1, 0, -0.5 * self.offset], + [0, 1, 0, -0.5 * self.y], [0, 0, 1, 0], [0, 0, 0, 1] ]) blind.matrix_world = o.matrix_world * tM elif blind is not None: + self.remove_blind(context, blind) + + context.scene.objects.active = o + + def remove_blind(self, context, blind): + if blind.type == "MESH" and "archipack_blind" in blind.data: d = blind.data context.scene.objects.unlink(blind) bpy.data.objects.remove(blind) bpy.data.meshes.remove(d) - - context.scene.objects.active = o - + def find_portal(self, o): for child in o.children: if child.type == 'LAMP': @@ -1158,6 +1549,14 @@ def remove_childs(self, context, o, to_remove): self.remove_handle(context, child) context.scene.objects.unlink(child) bpy.data.objects.remove(child, do_unlink=True) + + def remove_shutters(self, context, childs, to_remove): + for child in childs: + if to_remove < 1: + return + to_remove -= 1 + context.scene.objects.unlink(child) + bpy.data.objects.remove(child, do_unlink=True) def remove_handle(self, context, o): handle = self.find_handle(o) @@ -1192,6 +1591,18 @@ def update_rows(self, context, o): def get_childs_panels(self, context, o): return [child for child in o.children if archipack_window_panel.filter(child)] + + def get_childs_shutters(self, context, o, left_side): + if left_side: + return [child for child in o.children + if (archipack_window_shutter.filter(child) and + child.data.archipack_window_shutter[0].pivot > 0) + ] + else: + return [child for child in o.children + if (archipack_window_shutter.filter(child) and + child.data.archipack_window_shutter[0].pivot < 0) + ] def adjust_size_and_origin(self, size, origin, pivot, materials): if len(size) > 1: @@ -1218,8 +1629,8 @@ def _synch_portal(self, context, o, linked, childs): def _synch_blind(self, context, o, linked, childs): dl = archipack_window.datablock(linked) - dl.update_blind(context, linked, True, dl.blind_inside) - dl.update_blind(context, linked, False, dl.blind_outside) + dl.update_blind(context, linked, True) + dl.update_blind(context, linked, False) def _synch_childs(self, context, o, linked, childs): """ @@ -1285,6 +1696,59 @@ def _synch_childs(self, context, o, linked, childs): # restore context context.scene.objects.active = o + def _synch_shutters(self, context, o, linked, left_side): + """ + sub synch childs nodes of linked object + """ + childs = self.get_childs_shutters(context, o, left_side) + # remove childs not found on source + l_childs = self.get_childs_shutters(context, linked, left_side) + c_names = [c.data.name for c in childs] + for c in l_childs: + try: + id = c_names.index(c.data.name) + except: + context.scene.objects.unlink(c) + bpy.data.objects.remove(c, do_unlink=True) + + # children ordering may not be the same, so get the right l_childs order + l_childs = self.get_childs_shutters(context, linked, left_side) + l_names = [c.data.name for c in l_childs] + order = [] + for c in childs: + try: + id = l_names.index(c.data.name) + except: + id = -1 + order.append(id) + + # add missing childs and update other ones + for i, child in enumerate(childs): + if order[i] < 0: + p = bpy.data.objects.new("Shutter", child.data) + context.scene.objects.link(p) + p.lock_location[1] = True + p.lock_location[2] = True + p.lock_rotation[1] = True + p.lock_scale[0] = True + p.lock_scale[1] = True + p.lock_scale[2] = True + p.parent = linked + p.matrix_world = linked.matrix_world.copy() + m = p.archipack_material.add() + m.category = 'window' + m.material = o.archipack_material[0].material + else: + p = l_childs[order[i]] + + # self.synch_locks(p) + + p.location = child.location.copy() + p.rotation_euler = child.rotation_euler.copy() + + # restore context + context.scene.objects.active = o + def _synch_hole(self, context, linked, hole): l_hole = self.find_hole(linked) if l_hole is None: @@ -1312,6 +1776,7 @@ def synch_childs(self, context, o): o.select = True context.scene.objects.active = o childs = self.get_childs_panels(context, o) + hole = self.find_hole(o) bpy.ops.object.select_linked(type='OBDATA') for linked in context.selected_objects: @@ -1319,9 +1784,125 @@ def synch_childs(self, context, o): self._synch_portal(context, o, linked, childs) self._synch_blind(context, o, linked, childs) self._synch_childs(context, o, linked, childs) + self._synch_shutters(context, o, linked, True) + self._synch_shutters(context, o, linked, False) if hole is not None: self._synch_hole(context, linked, hole) + + def get_shutter_row(self, x, y, left_side): + n_shutters = self.shutter_left + self.shutter_right + size = Vector((x / n_shutters, y, 0)) + origin = [] + ttl = 0 + xh = x / 2 + # offset pivot + if left_side: + n_shutters = self.shutter_left + ttl -= size.x + else: + ttl += self.shutter_left * size.x + n_shutters = self.shutter_right + + for i in range(n_shutters): + ttl += size.x + origin.append(Vector((ttl - xh, 0))) + return size, origin + + def update_shutter(self, context, o, left_side): + # wanted childs + if self.shutter_enable: + if left_side: + side = 0 + pivot = 1 + n_shutters = self.shutter_left + else: + pivot = -1 + n_shutters = self.shutter_right + side = n_shutters - 1 + + else: + n_shutters = 0 + + # real childs + childs = self.get_childs_shutters(context, o, left_side) + n_childs = len(childs) + # remove child + if n_childs > n_shutters: + self.remove_shutters(context, childs, n_childs - n_shutters) + + if not self.shutter_enable or n_shutters == 0: + return + + childs = self.get_childs_shutters(context, o, left_side) + n_childs = len(childs) + + location_y = -0.5 * self.y - 0.25 * self.frame_y + if self.out_frame: + location_y -= self.out_frame_y2 + # Note: radius is slightly wrong: not taking overflow in account + center, origin, size, radius = self.get_radius(self.x, self.z) + offset = Vector((0.05, 0)) + size, origin = self.get_shutter_row(self.x, self.z, left_side) + + if self.z > 1.5: + hinge_count = 3 + else: + hinge_count = 2 + + for panel in range(n_shutters): + + if panel >= n_childs: + bpy.ops.archipack.window_shutter( + center=center, + origin=Vector((origin[panel].x, offset.y, 0)), + size=size, + radius=radius, + pivot=pivot, + shape=self.shape, + offset=pivot * offset.x, + curve_steps=self.curve_steps, + frame_x=self.frame_x, + frame_y=self.frame_y, + angle_y=self.angle_y, + hinge_enable=panel < 1, + hinge_count=hinge_count, + material=o.archipack_material[0].material + ) + child = context.active_object + # parenting at 0, 0, 0 before set object matrix_world + # so location remains local from frame + child.parent = o + child.matrix_world = o.matrix_world.copy() + child.rotation_euler.z = pi + else: + child = childs[panel] + child.select = True + context.scene.objects.active = child + props = archipack_window_shutter.datablock(child) + if props is not None: + props.origin = Vector((origin[panel].x, offset.y, 0)) + props.center = center + props.radius = radius + props.size = size + props.pivot = pivot + props.shape = self.shape + props.offset = pivot * offset.x + props.curve_steps = self.curve_steps + props.frame_x = self.frame_x + props.frame_y = self.frame_y + props.angle_y = self.angle_y + props.hinge_enable = panel < 1 + props.hinge_count = hinge_count + props.update(context) + + # location y + frame width. + child.location = Vector(( + origin[panel].x - pivot * offset.x + (side - panel) * size.x, + origin[panel].y + location_y + (side - panel) * pivot * 0.5 * self.frame_y, + self.altitude + offset.y + )) + def update_childs(self, context, o): """ pass params to childrens @@ -1332,7 +1913,7 @@ def update_childs(self, context, o): child_n = 0 row_n = 0 location_y = 0.5 * self.y - self.offset + 0.5 * self.frame_y - center, origin, size, radius = self.get_radius() + center, origin, size, radius = self.get_radius(self._x, self._z) offset = Vector((0, 0)) handle = 'NONE' if self.shape != 'CIRCLE': @@ -1460,69 +2041,69 @@ def _x(self): def _z(self): return self.z + 2 * (self.frame_overflow - self.frame_x) - def _get_tri_radius(self): + def _get_tri_radius(self, _x, _z): return Vector((0, self.y, 0)), Vector((0, 0, 0)), \ - Vector((self._x, self._z, 0)), Vector((self._x, 0, 0)) + Vector((_x, _z, 0)), Vector((_x, 0, 0)) - def _get_quad_radius(self): - fx_z = self._z / self._x - center_y = min(self._x / (self._x - self.frame_x) * self._z - self.frame_x * (1 + sqrt(1 + fx_z * fx_z)), - abs(tan(self.angle_y) * (self._x))) + def _get_quad_radius(self, _x, _z): + fx_z = _z / _x + center_y = min(_x / (_x - self.frame_x) * _z - self.frame_x * (1 + sqrt(1 + fx_z * fx_z)), + abs(tan(self.angle_y) * (_x))) if self.angle_y < 0: - center_x = 0.5 * self._x + center_x = 0.5 * _x else: - center_x = -0.5 * self._x + center_x = -0.5 * _x return Vector((center_x, center_y, 0)), Vector((0, 0, 0)), \ - Vector((self._x, self._z, 0)), Vector((self._x, 0, 0)) + Vector((_x, _z, 0)), Vector((_x, 0, 0)) - def _get_round_radius(self): + def _get_round_radius(self, _x, _z): """ bound radius to available space return center, origin, size, radius """ - x = 0.5 * self._x - self.frame_x + x = 0.5 * _x - self.frame_x # minimum space available - y = self._z - sum([row.height for row in self.rows[:self.n_rows - 1]]) - 2 * self.frame_x + y = _z - sum([row.height for row in self.rows[:self.n_rows - 1]]) - 2 * self.frame_x y = min(y, x) # minimum radius inside r = y + x * (x - (y * y / x)) / (2 * y) radius = max(self.radius, 0.001 + self.frame_x + r) - return Vector((0, self._z - radius, 0)), Vector((0, 0, 0)), \ - Vector((self._x, self._z, 0)), Vector((radius, 0, 0)) + return Vector((0, _z - radius, 0)), Vector((0, 0, 0)), \ + Vector((_x, _z, 0)), Vector((radius, 0, 0)) - def _get_circle_radius(self): + def _get_circle_radius(self, _x, _z): """ return center, origin, size, radius """ - return Vector((0, 0.5 * self._x, 0)), Vector((0, 0, 0)), \ - Vector((self._x, self._z, 0)), Vector((0.5 * self._x, 0, 0)) + return Vector((0, 0.5 * _x, 0)), Vector((0, 0, 0)), \ + Vector((_x, _z, 0)), Vector((0.5 * _x, 0, 0)) - def _get_ellipsis_radius(self): + def _get_ellipsis_radius(self, _x, _z): """ return center, origin, size, radius """ y = self.z - sum([row.height for row in self.rows[:self.n_rows - 1]]) radius_b = max(0, 0.001 - 2 * self.frame_x + min(y, self.elipsis_b)) - return Vector((0, self._z - radius_b, 0)), Vector((0, 0, 0)), \ - Vector((self._x, self._z, 0)), Vector((self._x / 2, radius_b, 0)) + return Vector((0, _z - radius_b, 0)), Vector((0, 0, 0)), \ + Vector((_x, _z, 0)), Vector((_x / 2, radius_b, 0)) - def get_radius(self): + def get_radius(self, _x, _z): """ return center, origin, size, radius """ if self.shape == 'ROUND': - return self._get_round_radius() + return self._get_round_radius(_x, _z) elif self.shape == 'ELLIPSIS': - return self._get_ellipsis_radius() + return self._get_ellipsis_radius(_x, _z) elif self.shape == 'CIRCLE': - return self._get_circle_radius() + return self._get_circle_radius(_x, _z) elif self.shape == 'QUADRI': - return self._get_quad_radius() + return self._get_quad_radius(_x, _z) elif self.shape in ['TRIANGLE', 'PENTAGON']: - return self._get_tri_radius() + return self._get_tri_radius(_x, _z) else: return Vector((0, 0, 0)), Vector((0, 0, 0)), \ - Vector((self._x, self._z, 0)), Vector((0, 0, 0)) + Vector((_x, _z, 0)), Vector((0, 0, 0)) def update(self, context, childs_only=False): # support for "copy to selected" @@ -1537,10 +2118,12 @@ def update(self, context, childs_only=False): bmed.buildmesh(context, o, self.verts, self.faces, self.matids, self.uvs) self.update_portal(context, o) - self.update_blind(context, o, True, self.blind_inside) - self.update_blind(context, o, False, self.blind_outside) + self.update_blind(context, o, True) + self.update_blind(context, o, False) self.update_childs(context, o) - + self.update_shutter(context, o, True) + self.update_shutter(context, o, False) + # update hole if childs_only is False and self.find_hole(o) is not None: self.interactive_hole(context, o) @@ -1584,7 +2167,7 @@ def interactive_hole(self, context, o): """ hole = self.hole - center, origin, size, radius = self.get_radius() + center, origin, size, radius = self.get_radius(self._x, self._z) x0 = 0 if self.out_frame: @@ -1727,6 +2310,13 @@ def draw(self, context): box.prop(prop, 'blind_y') box.prop(prop, 'blind_z') """ + box = layout.box() + row = box.row(align=True) + row.prop(prop, 'shutter_enable') + if prop.shutter_enable: + box.prop(prop, 'shutter_left') + box.prop(prop, 'shutter_right') + if prop.window_shape != 'CIRCLE': row = layout.row() if prop.display_panels: @@ -1747,7 +2337,9 @@ def draw(self, context): box = layout.box() row = prop.rows[0] row.draw(box, context, True) - + + + row = layout.row(align=True) if prop.display_materials: row.prop(prop, "display_materials", icon="TRIA_DOWN", icon_only=True, text="Materials", emboss=False) @@ -1762,7 +2354,6 @@ def draw(self, context): layout.prop(prop, 'portal', icon="LAMP_AREA") - class ARCHIPACK_PT_window_panel(Panel): bl_idname = "ARCHIPACK_PT_window_panel" bl_label = "Window panel" @@ -1778,6 +2369,22 @@ def draw(self, context): layout = self.layout layout.operator("archipack.select_parent") + +class ARCHIPACK_PT_window_shutter(Panel): + bl_idname = "ARCHIPACK_PT_window_shutter" + bl_label = "Shutter" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'ArchiPack' + + @classmethod + def poll(cls, context): + return archipack_window_shutter.filter(context.active_object) + + def draw(self, context): + layout = self.layout + layout.operator("archipack.select_parent") + # ------------------------------------------------------------------ # Define operator class to create object @@ -1853,19 +2460,20 @@ def delete(self, context): if archipack_window.filter(o): bpy.ops.archipack.disable_manipulate() for child in o.children: + d = child.data if child.type == 'LAMP': - d = child.data context.scene.objects.unlink(child) bpy.data.objects.remove(child) bpy.data.lamps.remove(d) elif 'archipack_hole' in child: context.scene.objects.unlink(child) bpy.data.objects.remove(child, do_unlink=True) - elif child.data is not None and 'archipack_window_panel' in child.data: - for handle in child.children: - if 'archipack_handle' in handle: - context.scene.objects.unlink(handle) - bpy.data.objects.remove(handle, do_unlink=True) + elif d is not None: + if 'archipack_window_panel' in d: + for handle in child.children: + if 'archipack_handle' in handle: + context.scene.objects.unlink(handle) + bpy.data.objects.remove(handle, do_unlink=True) context.scene.objects.unlink(child) bpy.data.objects.remove(child, do_unlink=True) context.scene.objects.unlink(o) @@ -1893,9 +2501,12 @@ def unique(self, context): if archipack_window.filter(o): o.select = True for child in o.children: + d = child.data if 'archipack_hole' in child or ( - child.data is not None and - 'archipack_window_panel' in child.data): + d is not None and ( + 'archipack_window_panel' in d or + 'archipack_window_shutter' in d + )): child.hide_select = False child.select = True if len(context.selected_objects) > 0: @@ -2292,6 +2903,136 @@ def execute(self, context): self.report({'WARNING'}, "Archipack: Option only valid in Object mode") return {'CANCELLED'} + +class ARCHIPACK_OT_window_shutter(Operator): + bl_idname = "archipack.window_shutter" + bl_label = "Window shutter" + bl_description = "Window shutter" + bl_category = 'Archipack' + bl_options = {'REGISTER', 'UNDO'} + center = FloatVectorProperty( + subtype='XYZ' + ) + origin = FloatVectorProperty( + subtype='XYZ' + ) + size = FloatVectorProperty( + subtype='XYZ' + ) + radius = FloatVectorProperty( + subtype='XYZ' + ) + angle_y = FloatProperty( + name='angle', + unit='ROTATION', + subtype='ANGLE', + min=-1.5, max=1.5, + default=0, precision=2, + description='angle' + ) + frame_y = FloatProperty( + name='Depth', + min=0, max=100, + default=0.06, precision=2, + description='frame depth' + ) + frame_x = FloatProperty( + name='Width', + min=0, max=100, + default=0.06, precision=2, + description='frame width' + ) + curve_steps = IntProperty( + name="curve steps", + min=1, + max=128, + default=16 + ) + shape = EnumProperty( + name='Shape', + items=( + ('RECTANGLE', 'Rectangle', '', 0), + ('ROUND', 'Top Round', '', 1), + ('ELLIPSIS', 'Top Elliptic', '', 2), + ('QUADRI', 'Top oblique', '', 3), + ('CIRCLE', 'Full circle', '', 4) + ), + default='RECTANGLE' + ) + pivot = FloatProperty( + name='pivot', + min=-1, max=1, + default=-1, precision=2, + description='pivot' + ) + material = StringProperty( + name="material", + default="" + ) + offset = FloatProperty( + name='offset', + default=0, precision=2, + description='x offset' + ) + hinge_enable = BoolProperty( + name="Hinge", + default=False + ) + hinge_count = IntProperty( + name="#Hinge", + min=0, + max=4, + default=2 + ) + def draw(self, context): + layout = self.layout + row = layout.row() + row.label("Use Properties panel (N) to define parms", icon='INFO') + + def create(self, context): + m = bpy.data.meshes.new("Shutter") + o = bpy.data.objects.new("Shutter", m) + d = m.archipack_window_shutter.add() + d.center = self.center + d.origin = self.origin + d.size = self.size + d.radius = self.radius + d.frame_y = self.frame_y + d.frame_x = self.frame_x + d.curve_steps = self.curve_steps + d.shape = self.shape + d.pivot = self.pivot + d.offset = self.offset + d.angle_y = self.angle_y + d.hinge_enable = self.hinge_enable + d.hinge_count = self.hinge_count + context.scene.objects.link(o) + o.select = True + context.scene.objects.active = o + m = o.archipack_material.add() + m.category = "window" + m.material = self.material + o.lock_location[1] = True + o.lock_location[2] = True + o.lock_rotation[1] = True + o.lock_scale[0] = True + o.lock_scale[1] = True + o.lock_scale[2] = True + o.show_transparent = True + d.update(context) + return o + + def execute(self, context): + if context.mode == "OBJECT": + o = self.create(context) + o.select = True + context.scene.objects.active = o + return {'FINISHED'} + else: + self.report({'WARNING'}, "Archipack: Option only valid in Object mode") + return {'CANCELLED'} + + # ------------------------------------------------------------------ # Define operator class to manipulate object # ------------------------------------------------------------------ @@ -2342,6 +3083,12 @@ def register(): Mesh.archipack_window_panel = CollectionProperty(type=archipack_window_panel) bpy.utils.register_class(ARCHIPACK_PT_window_panel) bpy.utils.register_class(ARCHIPACK_OT_window_panel) + + bpy.utils.register_class(archipack_window_shutter) + Mesh.archipack_window_shutter = CollectionProperty(type=archipack_window_shutter) + bpy.utils.register_class(ARCHIPACK_PT_window_shutter) + bpy.utils.register_class(ARCHIPACK_OT_window_shutter) + bpy.utils.register_class(archipack_window) Mesh.archipack_window = CollectionProperty(type=archipack_window) bpy.utils.register_class(ARCHIPACK_OT_window_preset_menu) @@ -2359,6 +3106,12 @@ def unregister(): bpy.utils.unregister_class(ARCHIPACK_PT_window_panel) del Mesh.archipack_window_panel bpy.utils.unregister_class(ARCHIPACK_OT_window_panel) + + bpy.utils.unregister_class(archipack_window_shutter) + bpy.utils.unregister_class(ARCHIPACK_PT_window_shutter) + del Mesh.archipack_window_shutter + bpy.utils.unregister_class(ARCHIPACK_OT_window_shutter) + bpy.utils.unregister_class(archipack_window) del Mesh.archipack_window bpy.utils.unregister_class(ARCHIPACK_OT_window_preset_menu) diff --git a/panel.py b/panel.py index 9efc538..c9f1ce8 100644 --- a/panel.py +++ b/panel.py @@ -136,10 +136,12 @@ def path_sections(self, steps, path_type): number of verts and faces sections along path """ n_path_verts = 2 - if path_type in ['QUADRI', 'RECTANGLE']: + if path_type == 'RECTANGLE': n_path_verts = 4 + self.subdiv_x + 2 * self.subdiv_y if self.closed_path: n_path_verts += self.subdiv_x + elif path_type == 'QUADRI': + n_path_verts = 4 elif path_type in ['ROUND', 'ELLIPSIS']: n_path_verts = steps + 3 elif path_type == 'CIRCLE': @@ -631,6 +633,7 @@ def uv(self, steps, center, origin, size, radius, angle_y, pivot, x, x_cap, path uv_r = 2 * pi * radius.x / steps uv_v = [uv_r for i in range(steps + 1)] elif path_type == 'QUADRI': + # dosent support subdiv dy = 0.5 * tan(angle_y) * size.x uv_v = [size.y - dy, size.x, size.y + dy, size.x] elif path_type == 'HORIZONTAL': From 6642a99f0350b24d152005aca1ae8d3162f33102 Mon Sep 17 00:00:00 2001 From: s-leger Date: Tue, 9 Jan 2018 16:12:25 +0100 Subject: [PATCH 4/5] Add blind icon --- icons/blind.png | Bin 0 -> 1448 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/blind.png diff --git a/icons/blind.png b/icons/blind.png new file mode 100644 index 0000000000000000000000000000000000000000..3001c94f4b17609cf7e1b712896b8cfd78733aa9 GIT binary patch literal 1448 zcmV;Z1y}lsP)9tlMmN&mtRI zi?Kjy8X=~(R!I>H2Jn)`_&`*i_$PeO2cBt2h>w5~2tgBDY`lcFgc_q;EHt&<0_{3$ z>)CU5&YVm4ob~0@XwzbM!6*GC^DviWzVrEAW(N4*faf3aYM z*G|~BT`ZT&U&LaujYPOA6|j*#wceoH?cM_5IHmM&$xjX(I8a_sgy-K3u5;(k!Q=6y0lZ(a3kV?}|FysNWScR!+g)`G zxBy^eWF#U<(zCj*A0dRCT5sVdg>9(B1)vwe&_=14%jIq}#u^O6ST2=HBa$S2H9R~# zno6a1`ThQ50|Nu^Q~s4LX-6v0;#M!fF11C~K;Mnx@fGsWfkz<}843Ip@P29UU`OxqwpI3g8I<%>cRq^ZC%FgZE7wY9aiP%VK`S(cj%g~Eg+N#E7GH z0Pu|Cmi+*{rfEXcG*ea8iDI#sak*UM(P(tKTrN+g(`l(10!aYd06Yg^H-NTP+>9|8 zhH*ci&)-s2bwt0n#FAxMQ=%y5s}UejO7{RbRmc6N5WySqD4TU*=gkV*k)AcQnN?1I`1fs8E6I~7G){rw#PIsxo)0ySz`me+BOCP~s|K@hHrqIj1vHkVGP<%$GQN?nAIawe0B zH#9U1IP0DUumwQvDuKDVxjUgy=;F-G%%{O%u+-Dj^P-ahy8#3VAx}OMfm|*(t0+oJ zk|eCGtbEQnKY#l4>1$`soN1}4sW}ATAb=(SKHIkaPI^Mu^_zh};JnAJT#zbnpP+j3|-eV0Is@Rt}E$unge(Pz*_)z6GAXIH|G;Y zaiv-U^O~kb6-7}v=fA6}dLtYT4=0n!Zvdp7&5i@u3IK*-*o-lmbFO$io?F3S@ap2? z;`Oesu3R+)!T?s=O8|~IxZzYorYK6jSS&8;x;_?-MsE^AesnV6Fo2f{A$8eowlwQ+%y6Ge5^m_q>Ez5$cs%2f*CoIeQp{}lORuF{ocs%~i z;}Eb}QKy{ypqUV|_hEJ5aXiai>%6GT)&7+Jd-exPUc?tQfCbY40000 Date: Tue, 9 Jan 2018 16:41:14 +0100 Subject: [PATCH 5/5] [BUGFIX] shutter hinge location --- archipack_window.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archipack_window.py b/archipack_window.py index 691def0..7c282e3 100644 --- a/archipack_window.py +++ b/archipack_window.py @@ -1865,7 +1865,7 @@ def update_shutter(self, context, o, left_side): frame_x=self.frame_x, frame_y=self.frame_y, angle_y=self.angle_y, - hinge_enable=panel < 1, + hinge_enable=panel == side, hinge_count=hinge_count, material=o.archipack_material[0].material ) @@ -1892,7 +1892,7 @@ def update_shutter(self, context, o, left_side): props.frame_x = self.frame_x props.frame_y = self.frame_y props.angle_y = self.angle_y - props.hinge_enable = panel < 1 + props.hinge_enable = panel == side props.hinge_count = hinge_count props.update(context)