Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Dinges <blender@dingto.org>2021-11-23 11:24:55 +0300
committerThomas Dinges <blender@dingto.org>2021-11-23 11:24:55 +0300
commitd7517a6f2a69071eab53c02a645f7651ccfffd45 (patch)
tree7a496b8ada3e764b7e6c438e30d8c2be49fb95cf /archipack/archipack_wall2.py
parent162cba016c8c11bcebea4d8d3cf80da9faf4ce76 (diff)
Remove Archipack to reflect new key requirements.
https://wiki.blender.org/wiki/Process/Addons
Diffstat (limited to 'archipack/archipack_wall2.py')
-rw-r--r--archipack/archipack_wall2.py2437
1 files changed, 0 insertions, 2437 deletions
diff --git a/archipack/archipack_wall2.py b/archipack/archipack_wall2.py
deleted file mode 100644
index 4469bc9a..00000000
--- a/archipack/archipack_wall2.py
+++ /dev/null
@@ -1,2437 +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)
-#
-# ----------------------------------------------------------
-import bpy
-import bmesh
-
-import time
-
-from bpy.types import Operator, PropertyGroup, Mesh, Panel
-from bpy.props import (
- FloatProperty, BoolProperty, IntProperty, StringProperty,
- FloatVectorProperty, CollectionProperty, EnumProperty
-)
-from .bmesh_utils import BmeshEdit as bmed
-from mathutils import Vector, Matrix
-from mathutils.geometry import (
- interpolate_bezier
- )
-from math import sin, cos, pi, atan2
-from .archipack_manipulator import (
- Manipulable, archipack_manipulator,
- GlPolygon, GlPolyline,
- GlLine, GlText, FeedbackPanel
- )
-from .archipack_object import ArchipackObject, ArchipackCreateTool, ArchipackDrawTool
-from .archipack_2d import Line, Arc
-from .archipack_snap import snap_point
-from .archipack_keymaps import Keymaps
-
-import logging
-logger = logging.getLogger("archipack")
-
-
-class Wall():
- def __init__(self, wall_z, z, t, flip):
- self.z = z
- self.wall_z = wall_z
- self.t = t
- self.flip = flip
- self.z_step = len(z)
-
- def set_offset(self, offset, last=None):
- """
- Offset line and compute intersection point
- between segments
- """
- self.line = self.make_offset(offset, last)
-
- def get_z(self, t):
- t0 = self.t[0]
- z0 = self.z[0]
- for i in range(1, self.z_step):
- t1 = self.t[i]
- z1 = self.z[i]
- if t <= t1:
- return z0 + (t - t0) / (t1 - t0) * (z1 - z0)
- t0, z0 = t1, z1
- return self.z[-1]
-
- def make_faces(self, i, f, faces):
- if i < self.n_step:
- # 1 3 5 7
- # 0 2 4 6
- if self.flip:
- faces.append((f + 2, f, f + 1, f + 3))
- else:
- faces.append((f, f + 2, f + 3, f + 1))
-
- def p3d(self, verts, t):
- x, y = self.lerp(t)
- z = self.wall_z + self.get_z(t)
- verts.append((x, y, 0))
- verts.append((x, y, z))
-
- def make_wall(self, i, verts, faces):
- t = self.t_step[i]
- f = len(verts)
- self.p3d(verts, t)
- self.make_faces(i, f, faces)
-
- def make_hole(self, i, verts, z0):
- t = self.t_step[i]
- x, y = self.line.lerp(t)
- verts.append((x, y, z0))
-
- def straight_wall(self, a0, length, wall_z, z, t):
- r = self.straight(length).rotate(a0)
- return StraightWall(r.p, r.v, wall_z, z, t, self.flip)
-
- def curved_wall(self, a0, da, radius, wall_z, z, t):
- n = self.normal(1).rotate(a0).scale(radius)
- if da < 0:
- n.v = -n.v
- a0 = n.angle
- c = n.p - n.v
- return CurvedWall(c, radius, a0, da, wall_z, z, t, self.flip)
-
-
-class StraightWall(Wall, Line):
- def __init__(self, p, v, wall_z, z, t, flip):
- Line.__init__(self, p, v)
- Wall.__init__(self, wall_z, z, t, flip)
-
- def param_t(self, step_angle):
- self.t_step = self.t
- self.n_step = len(self.t) - 1
-
-
-class CurvedWall(Wall, Arc):
- def __init__(self, c, radius, a0, da, wall_z, z, t, flip):
- Arc.__init__(self, c, radius, a0, da)
- Wall.__init__(self, wall_z, z, t, flip)
-
- def param_t(self, step_angle):
- t_step, n_step = self.steps_by_angle(step_angle)
- self.t_step = list(sorted([i * t_step for i in range(1, n_step)] + self.t))
- self.n_step = len(self.t_step) - 1
-
-
-class WallGenerator():
- def __init__(self, parts):
- self.last_type = 'NONE'
- self.segs = []
- self.parts = parts
- self.faces_type = 'NONE'
- self.closed = False
-
- def set_offset(self, offset):
- last = None
- for i, seg in enumerate(self.segs):
- seg.set_offset(offset, last)
- last = seg.line
-
- if self.closed:
- w = self.segs[-1]
- if len(self.segs) > 1:
- w.line = w.make_offset(offset, self.segs[-2].line)
-
- p1 = self.segs[0].line.p1
- self.segs[0].line = self.segs[0].make_offset(offset, w.line)
- self.segs[0].line.p1 = p1
-
- def add_part(self, part, wall_z, flip):
-
- # TODO:
- # refactor this part (height manipulators)
- manip_index = []
- if len(self.segs) < 1:
- s = None
- z = [part.z[0]]
- manip_index.append(0)
- else:
- s = self.segs[-1]
- z = [s.z[-1]]
-
- t_cur = 0
- z_last = part.n_splits - 1
- t = [0]
-
- for i in range(part.n_splits):
- t_try = t[-1] + part.t[i]
- if t_try == t_cur:
- continue
- if t_try <= 1:
- t_cur = t_try
- t.append(t_cur)
- z.append(part.z[i])
- manip_index.append(i)
- else:
- z_last = i
- break
-
- if t_cur < 1:
- t.append(1)
- manip_index.append(z_last)
- z.append(part.z[z_last])
-
- # start a new wall
- if s is None:
- if part.type == 'S_WALL':
- p = Vector((0, 0))
- v = part.length * Vector((cos(part.a0), sin(part.a0)))
- s = StraightWall(p, v, wall_z, z, t, flip)
- elif part.type == 'C_WALL':
- c = -part.radius * Vector((cos(part.a0), sin(part.a0)))
- s = CurvedWall(c, part.radius, part.a0, part.da, wall_z, z, t, flip)
- else:
- if part.type == 'S_WALL':
- s = s.straight_wall(part.a0, part.length, wall_z, z, t)
- elif part.type == 'C_WALL':
- s = s.curved_wall(part.a0, part.da, part.radius, wall_z, z, t)
-
- self.segs.append(s)
- self.last_type = part.type
-
- return manip_index
-
- def close(self, closed):
- # Make last segment implicit closing one
- if closed:
- part = self.parts[-1]
- w = self.segs[-1]
- dp = self.segs[0].p0 - self.segs[-1].p0
- if "C_" in part.type:
- dw = (w.p1 - w.p0)
- w.r = part.radius / dw.length * dp.length
- # angle pt - p0 - angle p0 p1
- da = atan2(dp.y, dp.x) - atan2(dw.y, dw.x)
- a0 = w.a0 + da
- if a0 > pi:
- a0 -= 2 * pi
- if a0 < -pi:
- a0 += 2 * pi
- w.a0 = a0
- else:
- w.v = dp
-
- def locate_manipulators(self, side):
-
- for i, wall in enumerate(self.segs):
-
- manipulators = self.parts[i].manipulators
-
- p0 = wall.p0.to_3d()
- p1 = wall.p1.to_3d()
-
- # angle from last to current segment
- if i > 0:
-
- if i < len(self.segs) - 1:
- manipulators[0].type_key = 'ANGLE'
- else:
- manipulators[0].type_key = 'DUMB_ANGLE'
-
- v0 = self.segs[i - 1].straight(-side, 1).v.to_3d()
- v1 = wall.straight(side, 0).v.to_3d()
- manipulators[0].set_pts([p0, v0, v1])
-
- if type(wall).__name__ == "StraightWall":
- # segment length
- manipulators[1].type_key = 'SIZE'
- manipulators[1].prop1_name = "length"
- manipulators[1].set_pts([p0, p1, (side, 0, 0)])
- else:
- # segment radius + angle
- # scale to fix overlap with drag
- v0 = side * (wall.p0 - wall.c).to_3d()
- v1 = side * (wall.p1 - wall.c).to_3d()
- scale = 1.0 + (0.5 / v0.length)
- manipulators[1].type_key = 'ARC_ANGLE_RADIUS'
- manipulators[1].prop1_name = "da"
- manipulators[1].prop2_name = "radius"
- manipulators[1].set_pts([wall.c.to_3d(), scale * v0, scale * v1])
-
- # snap manipulator, don't change index !
- manipulators[2].set_pts([p0, p1, (1, 0, 0)])
-
- # dumb, segment index
- z = Vector((0, 0, 0.75 * wall.wall_z))
- manipulators[3].set_pts([p0 + z, p1 + z, (1, 0, 0)])
-
- def make_wall(self, step_angle, flip, closed, verts, faces):
-
- # swap manipulators so they always face outside
- side = 1
- if flip:
- side = -1
-
- # Make last segment implicit closing one
-
- nb_segs = len(self.segs) - 1
- if closed:
- nb_segs += 1
-
- for i, wall in enumerate(self.segs):
-
- wall.param_t(step_angle)
- if i < nb_segs:
- for j in range(wall.n_step + 1):
- wall.make_wall(j, verts, faces)
- else:
- # last segment
- for j in range(wall.n_step):
- continue
- # print("%s" % (wall.n_step))
- # wall.make_wall(j, verts, faces)
-
- self.locate_manipulators(side)
-
- def rotate(self, idx_from, a):
- """
- apply rotation to all following segs
- """
- self.segs[idx_from].rotate(a)
- ca = cos(a)
- sa = sin(a)
- rM = Matrix([
- [ca, -sa],
- [sa, ca]
- ])
- # rotation center
- p0 = self.segs[idx_from].p0
- for i in range(idx_from + 1, len(self.segs)):
- seg = self.segs[i]
- seg.rotate(a)
- dp = rM @ (seg.p0 - p0)
- seg.translate(dp)
-
- def translate(self, idx_from, dp):
- """
- apply translation to all following segs
- """
- self.segs[idx_from].p1 += dp
- for i in range(idx_from + 1, len(self.segs)):
- self.segs[i].translate(dp)
-
- def change_coordsys(self, fromTM, toTM):
- """
- move shape fromTM into toTM coordsys
- """
- dp = (toTM.inverted() @ fromTM.translation).to_2d()
- da = toTM.row[1].to_2d().angle_signed(fromTM.row[1].to_2d())
- ca = cos(da)
- sa = sin(da)
- rM = Matrix([
- [ca, -sa],
- [sa, ca]
- ])
- for s in self.segs:
- tp = (rM @ s.p0) - s.p0 + dp
- s.rotate(da)
- s.translate(tp)
-
- def draw(self, context):
- for seg in self.segs:
- seg.draw(context, render=False)
-
- def debug(self, verts):
- for wall in self.segs:
- for i in range(33):
- x, y = wall.lerp(i / 32)
- verts.append((x, y, 0))
-
- def make_surface(self, o, verts, height):
- bm = bmesh.new()
- for v in verts:
- bm.verts.new(v)
- bm.verts.ensure_lookup_table()
- for i in range(1, len(verts)):
- bm.edges.new((bm.verts[i - 1], bm.verts[i]))
- bm.edges.new((bm.verts[-1], bm.verts[0]))
- bm.edges.ensure_lookup_table()
- bmesh.ops.contextual_create(bm, geom=bm.edges)
- geom = bm.faces[:]
- bmesh.ops.solidify(bm, geom=geom, thickness=height)
- bm.to_mesh(o.data)
- bm.free()
-
- def make_hole(self, context, hole_obj, d):
-
- offset = -0.5 * (1 - d.x_offset) * d.width
-
- z0 = 0.1
- self.set_offset(offset)
-
- nb_segs = len(self.segs) - 1
- if d.closed:
- nb_segs += 1
-
- verts = []
- for i, wall in enumerate(self.segs):
- wall.param_t(d.step_angle)
- if i < nb_segs:
- for j in range(wall.n_step + 1):
- wall.make_hole(j, verts, -z0)
-
- self.make_surface(hole_obj, verts, d.z + z0)
-
-
-def update(self, context):
- self.update(context)
-
-
-def update_childs(self, context):
- self.update(context, update_childs=True, manipulable_refresh=True)
-
-
-def update_manipulators(self, context):
- self.update(context, manipulable_refresh=True)
-
-
-def update_t_part(self, context):
- """
- Make this wall a T child of parent wall
- orient child so y points inside wall and x follow wall segment
- set child a0 according
- """
- o = self.find_in_selection(context)
- if o is not None:
-
- # w is parent wall
- w = context.scene.objects.get(self.t_part.strip())
- wd = archipack_wall2.datablock(w)
-
- if wd is not None:
- og = self.get_generator()
- self.setup_childs(o, og)
-
- bpy.ops.object.select_all(action="DESELECT")
-
- # 5 cases here:
- # 1 No parents at all
- # 2 o has parent
- # 3 w has parent
- # 4 o and w share same parent already
- # 5 o and w doesn't share parent
- link_to_parent = False
-
- # when both walls do have a reference point, we may delete one of them
- to_delete = None
-
- # select childs and make parent reference point active
- if w.parent is None:
- # Either link to o.parent or create new parent
- link_to_parent = True
- if o.parent is None:
- # create a reference point and make it active
- x, y, z = w.bound_box[0]
- context.scene.cursor.location = w.matrix_world @ Vector((x, y, z))
- # fix issue #9
- context.view_layer.objects.active = o
- bpy.ops.archipack.reference_point()
- o.select_set(state=True)
- else:
- context.view_layer.objects.active = o.parent
- w.select_set(state=True)
- else:
- # w has parent
- if o.parent is not w.parent:
- link_to_parent = True
- context.view_layer.objects.active = w.parent
- o.select_set(state=True)
- if o.parent is not None:
- # store o.parent to delete it
- to_delete = o.parent
- for c in o.parent.children:
- if c is not o:
- c.hide_select = False
- c.select_set(state=True)
-
- parent = context.active_object
-
- dmax = 2 * wd.width
-
- wg = wd.get_generator()
-
- otM = o.matrix_world
- orM = Matrix([
- otM[0].to_2d(),
- otM[1].to_2d()
- ])
-
- wtM = w.matrix_world
- wrM = Matrix([
- wtM[0].to_2d(),
- wtM[1].to_2d()
- ])
-
- # dir in absolute world coordsys
- dir = orM @ og.segs[0].straight(1, 0).v
-
- # pt in w coordsys
- pos = otM.translation
- pt = (wtM.inverted() @ pos).to_2d()
-
- for wall_idx, wall in enumerate(wg.segs):
- res, dist, t = wall.point_sur_segment(pt)
- # outside is on the right side of the wall
- # p1
- # |-- x
- # p0
-
- # NOTE:
- # rotation here is wrong when w has not parent while o has parent
-
- if res and t > 0 and t < 1 and abs(dist) < dmax:
- x = wrM @ wall.straight(1, t).v
- y = wrM @ wall.normal(t).v.normalized()
- self.parts[0].a0 = dir.angle_signed(x)
- o.matrix_world = Matrix([
- [x.x, -y.x, 0, pos.x],
- [x.y, -y.y, 0, pos.y],
- [0, 0, 1, pos.z],
- [0, 0, 0, 1]
- ])
- break
-
- if link_to_parent and bpy.ops.archipack.parent_to_reference.poll():
- bpy.ops.archipack.parent_to_reference('INVOKE_DEFAULT')
-
- # update generator to take new rotation in account
- # use this to relocate windows on wall after reparenting
- g = self.get_generator()
- self.relocate_childs(context, o, g)
-
- # hide holes from select
- for c in parent.children:
- if "archipack_hybridhole" in c:
- c.hide_select = True
-
- # delete unneeded reference point
- if to_delete is not None:
- bpy.ops.object.select_all(action="DESELECT")
- to_delete.select_set(state=True)
- context.view_layer.objects.active = to_delete
- if bpy.ops.object.delete.poll():
- bpy.ops.object.delete(use_global=False)
-
- elif self.t_part != "":
- self.t_part = ""
-
- self.restore_context(context)
-
-
-def set_splits(self, value):
- if self.n_splits != value:
- self.auto_update = False
- self._set_t(value)
- self.auto_update = True
- self.n_splits = value
- return None
-
-
-def get_splits(self):
- return self.n_splits
-
-
-def update_type(self, context):
-
- d = self.find_datablock_in_selection(context)
-
- if d is not None and d.auto_update:
-
- d.auto_update = False
- idx = 0
- for i, part in enumerate(d.parts):
- if part == self:
- idx = i
- break
- a0 = 0
- if idx > 0:
- g = d.get_generator()
- w0 = g.segs[idx - 1]
- a0 = w0.straight(1).angle
- if "C_" in self.type:
- w = w0.straight_wall(self.a0, self.length, d.z, self.z, self.t)
- else:
- w = w0.curved_wall(self.a0, self.da, self.radius, d.z, self.z, self.t)
- else:
- if "C_" in self.type:
- p = Vector((0, 0))
- v = self.length * Vector((cos(self.a0), sin(self.a0)))
- w = StraightWall(p, v, d.z, self.z, self.t, d.flip)
- a0 = pi / 2
- else:
- c = -self.radius * Vector((cos(self.a0), sin(self.a0)))
- w = CurvedWall(c, self.radius, self.a0, pi, d.z, self.z, self.t, d.flip)
-
- # w0 - w - w1
- if d.closed and idx == d.n_parts:
- dp = - w.p0
- else:
- dp = w.p1 - w.p0
-
- if "C_" in self.type:
- self.radius = 0.5 * dp.length
- self.da = pi
- a0 = atan2(dp.y, dp.x) - pi / 2 - a0
- else:
- self.length = dp.length
- a0 = atan2(dp.y, dp.x) - a0
-
- if a0 > pi:
- a0 -= 2 * pi
- if a0 < -pi:
- a0 += 2 * pi
- self.a0 = a0
-
- if idx + 1 < d.n_parts:
- # adjust rotation of next part
- part1 = d.parts[idx + 1]
- if "C_" in self.type:
- a0 = part1.a0 - pi / 2
- else:
- a0 = part1.a0 + w.straight(1).angle - atan2(dp.y, dp.x)
-
- if a0 > pi:
- a0 -= 2 * pi
- if a0 < -pi:
- a0 += 2 * pi
- part1.a0 = a0
-
- d.auto_update = True
-
-
-class archipack_wall2_part(PropertyGroup):
- type : EnumProperty(
- items=(
- ('S_WALL', 'Straight', '', 0),
- ('C_WALL', 'Curved', '', 1)
- ),
- default='S_WALL',
- update=update_type
- )
- length : FloatProperty(
- name="Length",
- min=0.01,
- default=2.0,
- unit='LENGTH', subtype='DISTANCE',
- update=update
- )
- radius : FloatProperty(
- name="Radius",
- min=0.5,
- default=0.7,
- unit='LENGTH', subtype='DISTANCE',
- update=update
- )
- a0 : FloatProperty(
- name="Start angle",
- min=-pi,
- max=pi,
- default=pi / 2,
- subtype='ANGLE', unit='ROTATION',
- update=update
- )
- da : FloatProperty(
- name="Angle",
- min=-pi,
- max=pi,
- default=pi / 2,
- subtype='ANGLE', unit='ROTATION',
- update=update
- )
- z : FloatVectorProperty(
- name="Height",
- 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,
- update=update
- )
- t : FloatVectorProperty(
- name="Position",
- min=0,
- max=1,
- default=[
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1
- ],
- size=31,
- update=update
- )
- splits : IntProperty(
- name="Splits",
- default=1,
- min=1,
- max=31,
- get=get_splits, set=set_splits
- )
- n_splits : IntProperty(
- name="Splits",
- default=1,
- min=1,
- max=31,
- update=update
- )
- auto_update : BoolProperty(default=True)
- manipulators : CollectionProperty(type=archipack_manipulator)
- # ui related
- expand : BoolProperty(default=False)
-
- def _set_t(self, splits):
- t = 1 / splits
- for i in range(splits):
- self.t[i] = t
-
- 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_wall2.datablock(o)
- if props:
- for part in props.parts:
- if part == self:
- return props
- return None
-
- def update(self, context, manipulable_refresh=False):
- if not self.auto_update:
- return
- props = self.find_datablock_in_selection(context)
- if props is not None:
- props.update(context, manipulable_refresh)
-
- def draw(self, layout, context, index):
-
- row = layout.row(align=True)
- if self.expand:
- row.prop(self, 'expand', icon="TRIA_DOWN", text="Part " + str(index + 1), emboss=False)
- else:
- row.prop(self, 'expand', icon="TRIA_RIGHT", text="Part " + str(index + 1), emboss=False)
-
- row.prop(self, "type", text="")
-
- if self.expand:
- row = layout.row(align=True)
- row.operator("archipack.wall2_insert", text="Split").index = index
- row.operator("archipack.wall2_remove", text="Remove").index = index
- if self.type == 'C_WALL':
- row = layout.row()
- row.prop(self, "radius")
- row = layout.row()
- row.prop(self, "da")
- else:
- row = layout.row()
- row.prop(self, "length")
- row = layout.row()
- row.prop(self, "a0")
- row = layout.row()
- row.prop(self, "splits")
- for split in range(self.n_splits):
- row = layout.row()
- row.prop(self, "z", text="alt", index=split)
- row.prop(self, "t", text="pos", index=split)
-
-
-class archipack_wall2_child(PropertyGroup):
- # Size Loc
- # Delta Loc
- manipulators : CollectionProperty(type=archipack_manipulator)
- child_name : StringProperty()
- wall_idx : IntProperty()
- pos : FloatVectorProperty(subtype='XYZ')
- flip : BoolProperty(default=False)
-
- def get_child(self, context):
- d = None
- child = context.scene.objects.get(self.child_name.strip())
- if child is not None and child.data is not None:
- cd = child.data
- if 'archipack_window' in cd:
- d = cd.archipack_window[0]
- elif 'archipack_door' in cd:
- d = cd.archipack_door[0]
- return child, d
-
-
-class archipack_wall2(ArchipackObject, Manipulable, PropertyGroup):
- parts : CollectionProperty(type=archipack_wall2_part)
- n_parts : IntProperty(
- name="Parts",
- min=1,
- max=1024,
- default=1, update=update_manipulators
- )
- step_angle : FloatProperty(
- description="Curved parts segmentation",
- name="Step angle",
- min=1 / 180 * pi,
- max=pi,
- default=6 / 180 * pi,
- subtype='ANGLE', unit='ROTATION',
- update=update
- )
- width : FloatProperty(
- name="Width",
- min=0.01,
- default=0.2,
- unit='LENGTH', subtype='DISTANCE',
- update=update
- )
- z : FloatProperty(
- name='Height',
- min=0.1,
- default=2.7, precision=2,
- unit='LENGTH', subtype='DISTANCE',
- description='height', update=update,
- )
- x_offset : FloatProperty(
- name="Offset",
- min=-1, max=1,
- default=-1, precision=2, step=1,
- 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
- )
- flip : BoolProperty(
- name="Flip",
- default=False,
- update=update_childs
- )
- closed : BoolProperty(
- default=False,
- name="Close",
- update=update_manipulators
- )
- auto_update : BoolProperty(
- options={'SKIP_SAVE'},
- default=True,
- update=update_manipulators
- )
- realtime : BoolProperty(
- options={'SKIP_SAVE'},
- default=True,
- name="Real Time",
- description="Relocate childs in realtime"
- )
- # dumb manipulators to show sizes between childs
- childs_manipulators : CollectionProperty(type=archipack_manipulator)
- # store to manipulate windows and doors
- childs : CollectionProperty(type=archipack_wall2_child)
- t_part : StringProperty(
- name="Parent wall",
- description="This part will follow parent when set",
- default="",
- update=update_t_part
- )
-
- def insert_part(self, context, o, where):
- self.manipulable_disable(context)
- self.auto_update = False
- # the part we do split
- part_0 = self.parts[where]
- part_0.length /= 2
- part_0.da /= 2
- self.parts.add()
- part_1 = self.parts[len(self.parts) - 1]
- part_1.type = part_0.type
- part_1.length = part_0.length
- part_1.da = part_0.da
- part_1.a0 = 0
- # move after current one
- self.parts.move(len(self.parts) - 1, where + 1)
- self.n_parts += 1
- # re-eval childs location
- g = self.get_generator()
- self.setup_childs(o, g)
-
- self.setup_manipulators()
- self.auto_update = True
-
- def add_part(self, context, length):
- self.manipulable_disable(context)
- self.auto_update = False
- p = self.parts.add()
- p.length = length
- self.parts.move(len(self.parts) - 1, self.n_parts)
- self.n_parts += 1
- self.setup_manipulators()
- self.auto_update = True
- return self.parts[self.n_parts - 1]
-
- def remove_part(self, context, o, where):
- self.manipulable_disable(context)
- self.auto_update = False
- # preserve shape
- # using generator
- if where > 0:
- g = self.get_generator()
- w = g.segs[where - 1]
- w.p1 = g.segs[where].p1
-
- if where + 1 < self.n_parts:
- self.parts[where + 1].a0 = g.segs[where + 1].delta_angle(w)
-
- part = self.parts[where - 1]
-
- if "C_" in part.type:
- part.radius = w.r
- else:
- part.length = w.length
-
- if where > 1:
- part.a0 = w.delta_angle(g.segs[where - 2])
- else:
- part.a0 = w.straight(1, 0).angle
-
- self.parts.remove(where)
- self.n_parts -= 1
-
- # re-eval child location
- g = self.get_generator()
- self.setup_childs(o, g)
-
- # fix snap manipulators index
- self.setup_manipulators()
- self.auto_update = True
-
- def get_generator(self):
- # print("get_generator")
- g = WallGenerator(self.parts)
- for part in self.parts:
- g.add_part(part, self.z, self.flip)
- g.close(self.closed)
- return g
-
- def update_parts(self, o, update_childs=False):
- # print("update_parts")
- # remove rows
- # NOTE:
- # n_parts+1
- # as last one is end point of last segment or closing one
- row_change = False
- for i in range(len(self.parts), self.n_parts + 1, -1):
- row_change = True
- self.parts.remove(i - 1)
-
- # add rows
- for i in range(len(self.parts), self.n_parts + 1):
- row_change = True
- self.parts.add()
-
- self.setup_manipulators()
-
- g = self.get_generator()
-
- if o is not None and (row_change or update_childs):
- self.setup_childs(o, g)
-
- return g
-
- def setup_manipulators(self):
-
- if len(self.manipulators) == 0:
- # make manipulators selectable
- s = self.manipulators.add()
- s.prop1_name = "width"
- s = self.manipulators.add()
- s.prop1_name = "n_parts"
- s.type_key = 'COUNTER'
- s = self.manipulators.add()
- s.prop1_name = "z"
- s.normal = (0, 1, 0)
-
- if self.t_part != "" and len(self.manipulators) < 4:
- s = self.manipulators.add()
- s.prop1_name = "x"
- s.type_key = 'DELTA_LOC'
-
- for i in range(self.n_parts + 1):
- p = self.parts[i]
- n_manips = len(p.manipulators)
- if n_manips < 1:
- s = p.manipulators.add()
- s.type_key = "ANGLE"
- s.prop1_name = "a0"
- if n_manips < 2:
- s = p.manipulators.add()
- s.type_key = "SIZE"
- s.prop1_name = "length"
- if n_manips < 3:
- s = p.manipulators.add()
- s.type_key = 'WALL_SNAP'
- s.prop1_name = str(i)
- s.prop2_name = 'z'
- if n_manips < 4:
- s = p.manipulators.add()
- s.type_key = 'DUMB_STRING'
- s.prop1_name = str(i + 1)
- p.manipulators[2].prop1_name = str(i)
- p.manipulators[3].prop1_name = str(i + 1)
-
- def interpolate_bezier(self, pts, wM, p0, p1, resolution):
- if resolution == 0:
- pts.append(wM @ p0.co.to_3d())
- else:
- v = (p1.co - p0.co).normalized()
- d1 = (p0.handle_right - p0.co).normalized()
- d2 = (p1.co - p1.handle_left).normalized()
- if d1 == v and d2 == v:
- pts.append(wM @ p0.co.to_3d())
- else:
- seg = interpolate_bezier(wM @ p0.co,
- wM @ p0.handle_right,
- wM @ p1.handle_left,
- wM @ p1.co,
- resolution + 1)
- for i in range(resolution):
- pts.append(seg[i].to_3d())
-
- def is_cw(self, pts):
- p0 = pts[0]
- d = 0
- for p in pts[1:]:
- d += (p.x * p0.y - p.y * p0.x)
- p0 = p
- return d > 0
-
- def from_spline(self, wM, resolution, spline):
- pts = []
- if spline.type == 'POLY':
- pts = [wM @ p.co.to_3d() for p in spline.points]
- if spline.use_cyclic_u:
- pts.append(pts[0])
- elif spline.type == 'BEZIER':
- points = spline.bezier_points
- for i in range(1, len(points)):
- p0 = points[i - 1]
- p1 = points[i]
- self.interpolate_bezier(pts, wM, p0, p1, resolution)
- if spline.use_cyclic_u:
- p0 = points[-1]
- p1 = points[0]
- self.interpolate_bezier(pts, wM, p0, p1, resolution)
- pts.append(pts[0])
- else:
- pts.append(wM @ points[-1].co)
-
- if self.is_cw(pts):
- pts = list(reversed(pts))
-
- self.auto_update = False
- self.from_points(pts, spline.use_cyclic_u)
- self.auto_update = True
-
- def from_points(self, pts, closed):
-
- self.n_parts = len(pts) - 1
-
- if closed:
- self.n_parts -= 1
-
- self.update_parts(None)
-
- p0 = pts.pop(0)
- a0 = 0
- for i, p1 in enumerate(pts):
- dp = p1 - p0
- da = atan2(dp.y, dp.x) - a0
- if da > pi:
- da -= 2 * pi
- if da < -pi:
- da += 2 * pi
- if i >= len(self.parts):
- print("Too many pts for parts")
- break
- p = self.parts[i]
- p.length = dp.to_2d().length
- p.dz = dp.z
- p.a0 = da
- a0 += da
- p0 = p1
-
- self.closed = closed
-
- def reverse(self, context, o):
-
- g = self.get_generator()
-
- self.auto_update = False
-
- pts = [seg.p0.to_3d() for seg in g.segs]
-
- if not self.closed:
- g.segs.pop()
-
- g_segs = list(reversed(g.segs))
-
- last_seg = None
-
- for i, seg in enumerate(g_segs):
-
- s = seg.oposite
- if "Curved" in type(seg).__name__:
- self.parts[i].type = "C_WALL"
- self.parts[i].radius = s.r
- self.parts[i].da = s.da
- else:
- self.parts[i].type = "S_WALL"
- self.parts[i].length = s.length
-
- self.parts[i].a0 = s.delta_angle(last_seg)
-
- last_seg = s
-
- if self.closed:
- pts.append(pts[0])
-
- pts = list(reversed(pts))
-
- # location wont change for closed walls
- if not self.closed:
- dp = pts[0] - pts[-1]
- # pre-translate as dp is in local coordsys
- o.matrix_world = o.matrix_world @ Matrix([
- [1, 0, 0, dp.x],
- [0, 1, 0, dp.y],
- [0, 0, 1, 0],
- [0, 0, 0, 1],
- ])
-
- # self.from_points(pts, self.closed)
-
- g = self.get_generator()
-
- self.setup_childs(o, g)
- self.auto_update = True
-
- # flip does trigger relocate and keep childs orientation
- self.flip = not self.flip
-
- def update(self, context, manipulable_refresh=False, update_childs=False):
-
- o = self.find_in_selection(context, self.auto_update)
-
- if o is None:
- return
-
- if manipulable_refresh:
- # prevent crash by removing all manipulators refs to datablock before changes
- self.manipulable_disable(context)
-
- verts = []
- faces = []
-
- g = self.update_parts(o, update_childs)
- # print("make_wall")
- g.make_wall(self.step_angle, self.flip, self.closed, verts, faces)
-
- if self.closed:
- f = len(verts)
- if self.flip:
- faces.append((0, f - 2, f - 1, 1))
- else:
- faces.append((f - 2, 0, 1, f - 1))
-
- # print("buildmesh")
- bmed.buildmesh(context, o, verts, faces, matids=None, uvs=None, weld=True)
-
- side = 1
- if self.flip:
- side = -1
- # Width
- offset = side * (0.5 * self.x_offset) * self.width
- self.manipulators[0].set_pts([
- g.segs[0].sized_normal(0, offset + 0.5 * side * self.width).v.to_3d(),
- g.segs[0].sized_normal(0, offset - 0.5 * side * self.width).v.to_3d(),
- (-side, 0, 0)
- ])
-
- # Parts COUNTER
- self.manipulators[1].set_pts([g.segs[-2].lerp(1.1).to_3d(),
- g.segs[-2].lerp(1.1 + 0.5 / g.segs[-2].length).to_3d(),
- (-side, 0, 0)
- ])
-
- # Height
- self.manipulators[2].set_pts([
- (0, 0, 0),
- (0, 0, self.z),
- (-1, 0, 0)
- ], normal=g.segs[0].straight(side, 0).v.to_3d())
-
- if self.t_part != "":
- t = 0.3 / g.segs[0].length
- self.manipulators[3].set_pts([
- g.segs[0].sized_normal(t, 0.1).p1.to_3d(),
- g.segs[0].sized_normal(t, -0.1).p1.to_3d(),
- (1, 0, 0)
- ])
-
- if self.realtime:
- # update child location and size
- self.relocate_childs(context, o, g)
- # store gl points
- self.update_childs(context, o, g)
- else:
- bpy.ops.archipack.wall2_throttle_update(name=o.name)
-
- modif = o.modifiers.get('Wall')
- if modif is None:
- modif = o.modifiers.new('Wall', 'SOLIDIFY')
- modif.use_quality_normals = True
- modif.use_even_offset = True
- modif.material_offset_rim = 2
- modif.material_offset = 1
-
- modif.thickness = self.width
- modif.offset = self.x_offset
-
- if manipulable_refresh:
- # print("manipulable_refresh=True")
- self.manipulable_refresh = True
-
- self.restore_context(context)
-
- # manipulable children objects like windows and doors
- def child_partition(self, array, begin, end):
- pivot = begin
- for i in range(begin + 1, end + 1):
- # wall idx
- if array[i][1] < array[begin][1]:
- pivot += 1
- array[i], array[pivot] = array[pivot], array[i]
- # param t on the wall
- elif array[i][1] == array[begin][1] and array[i][4] <= array[begin][4]:
- pivot += 1
- array[i], array[pivot] = array[pivot], array[i]
- array[pivot], array[begin] = array[begin], array[pivot]
- return pivot
-
- def sort_child(self, array, begin=0, end=None):
- # print("sort_child")
- if end is None:
- end = len(array) - 1
-
- def _quicksort(array, begin, end):
- if begin >= end:
- return
- pivot = self.child_partition(array, begin, end)
- _quicksort(array, begin, pivot - 1)
- _quicksort(array, pivot + 1, end)
- return _quicksort(array, begin, end)
-
- def add_child(self, name, wall_idx, pos, flip):
- # print("add_child %s %s" % (name, wall_idx))
- c = self.childs.add()
- c.child_name = name
- c.wall_idx = wall_idx
- c.pos = pos
- c.flip = flip
- m = c.manipulators.add()
- m.type_key = 'DELTA_LOC'
- m.prop1_name = "x"
- m = c.manipulators.add()
- m.type_key = 'SNAP_SIZE_LOC'
- m.prop1_name = "x"
- m.prop2_name = "x"
-
- def setup_childs(self, o, g):
- """
- Store childs
- create manipulators
- call after a boolean oop
- """
- # tim = time.time()
- self.childs.clear()
- self.childs_manipulators.clear()
- if o.parent is None:
- return
- wall_with_childs = [0 for i in range(self.n_parts + 1)]
- relocate = []
- dmax = 2 * self.width
-
- wtM = o.matrix_world
- wrM = Matrix([
- wtM[0].to_2d(),
- wtM[1].to_2d()
- ])
- witM = wtM.inverted()
-
- for child in o.parent.children:
- # filter allowed childs
- cd = child.data
- wd = archipack_wall2.datablock(child)
- if (child != o and cd is not None and (
- 'archipack_window' in cd or
- 'archipack_door' in cd or (
- wd is not None and
- o.name in wd.t_part
- )
- )):
-
- # setup on T linked walls
- if wd is not None:
- wg = wd.get_generator()
- wd.setup_childs(child, wg)
-
- ctM = child.matrix_world
- crM = Matrix([
- ctM[0].to_2d(),
- ctM[1].to_2d()
- ])
-
- # pt in w coordsys
- pos = ctM.translation
- pt = (witM @ pos).to_2d()
-
- for wall_idx, wall in enumerate(g.segs):
- # may be optimized with a bound check
- res, dist, t = wall.point_sur_segment(pt)
- # outside is on the right side of the wall
- # p1
- # |-- x
- # p0
- if res and t > 0 and t < 1 and abs(dist) < dmax:
- # dir in world coordsys
- dir = wrM @ wall.sized_normal(t, 1).v
- wall_with_childs[wall_idx] = 1
- m = self.childs_manipulators.add()
- m.type_key = 'DUMB_SIZE'
- # always make window points outside
- if "archipack_window" in cd:
- flip = self.flip
- else:
- dir_y = crM @ Vector((0, -1))
- # let door orient where user want
- flip = (dir_y - dir).length > 0.5
- # store z in wall space
- relocate.append((
- child.name,
- wall_idx,
- (t * wall.length, dist, (witM @ pos).z),
- flip,
- t))
- break
-
- self.sort_child(relocate)
- for child in relocate:
- name, wall_idx, pos, flip, t = child
- self.add_child(name, wall_idx, pos, flip)
-
- # add a dumb size from last child to end of wall segment
- for i in range(sum(wall_with_childs)):
- m = self.childs_manipulators.add()
- m.type_key = 'DUMB_SIZE'
- # print("setup_childs:%1.4f" % (time.time()-tim))
-
- def relocate_childs(self, context, o, g):
- """
- Move and resize childs after wall edition
- """
- # print("relocate_childs")
- # tim = time.time()
- w = -self.x_offset * self.width
- if self.flip:
- w = -w
- tM = o.matrix_world
- for child in self.childs:
- c, d = child.get_child(context)
- if c is None:
- continue
- t = child.pos.x / g.segs[child.wall_idx].length
- n = g.segs[child.wall_idx].sized_normal(t, 1)
- rx, ry = -n.v
- rx, ry = ry, -rx
- if child.flip:
- rx, ry = -rx, -ry
-
- if d is not None:
- # print("change flip:%s width:%s" % (d.flip != child.flip, d.y != self.width))
- if d.y != self.width or d.flip != child.flip:
- c.select_set(state=True)
- d.auto_update = False
- d.flip = child.flip
- d.y = self.width
- d.auto_update = True
- c.select_set(state=False)
- x, y = n.p - (0.5 * w * n.v)
- else:
- x, y = n.p - (child.pos.y * n.v)
-
- context.view_layer.objects.active = o
- # preTranslate
- c.matrix_world = tM @ Matrix([
- [rx, -ry, 0, x],
- [ry, rx, 0, y],
- [0, 0, 1, child.pos.z],
- [0, 0, 0, 1]
- ])
-
- # Update T linked wall's childs
- if archipack_wall2.filter(c):
- d = archipack_wall2.datablock(c)
- cg = d.get_generator()
- d.relocate_childs(context, c, cg)
-
- # print("relocate_childs:%1.4f" % (time.time()-tim))
-
- def update_childs(self, context, o, g):
- """
- setup gl points for childs
- """
- # print("update_childs")
-
- if o.parent is None:
- return
-
- # swap manipulators so they always face outside
- manip_side = 1
- if self.flip:
- manip_side = -1
-
- itM = o.matrix_world.inverted()
- m_idx = 0
- for wall_idx, wall in enumerate(g.segs):
- p0 = wall.lerp(0)
- wall_has_childs = False
- for child in self.childs:
- if child.wall_idx == wall_idx:
- c, d = child.get_child(context)
- if d is not None:
- # child is either a window or a door
- wall_has_childs = True
- dt = 0.5 * d.x / wall.length
- pt = (itM @ c.matrix_world.translation).to_2d()
- res, y, t = wall.point_sur_segment(pt)
- child.pos = (wall.length * t, y, child.pos.z)
- p1 = wall.lerp(t - dt)
- # dumb size between childs
- self.childs_manipulators[m_idx].set_pts([
- (p0.x, p0.y, 0),
- (p1.x, p1.y, 0),
- (manip_side * 0.5, 0, 0)])
- m_idx += 1
- x, y = 0.5 * d.x, -self.x_offset * 0.5 * d.y
-
- if child.flip:
- side = -manip_side
- else:
- side = manip_side
-
- # delta loc
- child.manipulators[0].set_pts([(-x, side * -y, 0), (x, side * -y, 0), (side, 0, 0)])
- # loc size
- child.manipulators[1].set_pts([
- (-x, side * -y, 0),
- (x, side * -y, 0),
- (0.5 * side, 0, 0)])
- p0 = wall.lerp(t + dt)
- p1 = wall.lerp(1)
- if wall_has_childs:
- # dub size after all childs
- self.childs_manipulators[m_idx].set_pts([
- (p0.x, p0.y, 0),
- (p1.x, p1.y, 0),
- (manip_side * 0.5, 0, 0)])
- m_idx += 1
-
- def manipulate_childs(self, context):
- """
- setup child manipulators
- """
- # print("manipulate_childs")
- n_parts = self.n_parts
- if self.closed:
- n_parts += 1
-
- for wall_idx in range(n_parts):
- for child in self.childs:
- if child.wall_idx == wall_idx:
- c, d = child.get_child(context)
- if d is not None:
- # delta loc
- self.manip_stack.append(child.manipulators[0].setup(context, c, d, self.manipulate_callback))
- # loc size
- self.manip_stack.append(child.manipulators[1].setup(context, c, d, self.manipulate_callback))
-
- def manipulate_callback(self, context, o=None, manipulator=None):
- found = False
- if o.parent is not None:
- for c in o.parent.children:
- if (archipack_wall2.datablock(c) == self):
- context.view_layer.objects.active = c
- found = True
- break
- if found:
- self.manipulable_manipulate(context, manipulator=manipulator)
-
- def manipulable_manipulate(self, context, event=None, manipulator=None):
- type_name = type(manipulator).__name__
- # print("manipulable_manipulate %s" % (type_name))
- if type_name in [
- 'DeltaLocationManipulator',
- 'SizeLocationManipulator',
- 'SnapSizeLocationManipulator'
- ]:
- # update manipulators pos of childs
- o = context.active_object
- if o.parent is None:
- return
- g = self.get_generator()
- itM = o.matrix_world.inverted() @ o.parent.matrix_world
- for child in self.childs:
- c, d = child.get_child(context)
- if d is not None:
- wall = g.segs[child.wall_idx]
- pt = (itM @ c.location).to_2d()
- res, d, t = wall.point_sur_segment(pt)
- child.pos = (t * wall.length, d, child.pos.z)
- # update childs manipulators
- self.update_childs(context, o, g)
-
- def manipulable_move_t_part(self, context, o=None, manipulator=None):
- """
- Callback for t_parts childs
- """
- type_name = type(manipulator).__name__
- # print("manipulable_manipulate %s" % (type_name))
- if type_name in [
- 'DeltaLocationManipulator'
- ]:
- # update manipulators pos of childs
- if archipack_wall2.datablock(o) != self:
- return
- g = self.get_generator()
- # update childs
- self.relocate_childs(context, o, g)
-
- def manipulable_release(self, context):
- """
- Override with action to do on mouse release
- eg: big update
- """
- return
-
- def manipulable_setup(self, context):
- # print("manipulable_setup")
- self.manipulable_disable(context)
- o = context.active_object
-
- # setup childs manipulators
- self.manipulate_childs(context)
- n_parts = self.n_parts
- if self.closed:
- n_parts += 1
-
- # update manipulators on version change
- self.setup_manipulators()
-
- for i, part in enumerate(self.parts):
-
- if i < n_parts:
- if i > 0:
- # start angle
- self.manip_stack.append(part.manipulators[0].setup(context, o, part))
-
- # length / radius + angle
- self.manip_stack.append(part.manipulators[1].setup(context, o, part))
- # segment index
- self.manip_stack.append(part.manipulators[3].setup(context, o, self))
-
- # snap point
- self.manip_stack.append(part.manipulators[2].setup(context, o, self))
-
- # height as per segment will be here when done
-
- # width + counter
- for m in self.manipulators:
- self.manip_stack.append(m.setup(context, o, self, self.manipulable_move_t_part))
-
- # dumb between childs
- for m in self.childs_manipulators:
- self.manip_stack.append(m.setup(context, o, self))
-
- def manipulable_exit(self, context):
- """
- Override with action to do when modal exit
- """
- return
-
- def manipulable_invoke(self, context):
- """
- call this in operator invoke()
- """
- # print("manipulable_invoke")
- if self.manipulate_mode:
- self.manipulable_disable(context)
- return False
-
- # self.manip_stack = []
- o = context.active_object
- g = self.get_generator()
- # setup childs manipulators
- self.setup_childs(o, g)
- # store gl points
- self.update_childs(context, o, g)
- # don't do anything ..
- # self.manipulable_release(context)
- # self.manipulate_mode = True
- self.manipulable_setup(context)
- self.manipulate_mode = True
-
- self._manipulable_invoke(context)
-
- return True
-
- def find_roof(self, context, o, g):
- tM = o.matrix_world
- up = Vector((0, 0, 1))
- for seg in g.segs:
- p = tM @ seg.p0.to_3d()
- p.z = 0.01
- # prevent self intersect
- o.hide_viewport = True
- res, pos, normal, face_index, r, matrix_world = context.scene.ray_cast(
- depsgraph=context.view_layer.depsgraph,
- origin=p,
- direction=up)
-
- o.hide_viewport = False
- # print("res:%s" % res)
- if res and r.data is not None and "archipack_roof" in r.data:
- return r, r.data.archipack_roof[0]
-
- return None, None
-
-
-# Update throttle (hack)
-# use 2 globals to store a timer and state of update_action
-# Use to update floor boolean on edit
-update_timer = None
-update_timer_updating = False
-throttle_delay = 0.5
-throttle_start = 0
-
-
-class ARCHIPACK_OT_wall2_throttle_update(Operator):
- bl_idname = "archipack.wall2_throttle_update"
- bl_label = "Update childs with a delay"
-
- name : StringProperty()
-
- def modal(self, context, event):
- global update_timer_updating
- if event.type == 'TIMER' and not update_timer_updating:
- # can't rely on TIMER event as another timer may run
- if time.time() - throttle_start > throttle_delay:
- update_timer_updating = True
- o = context.scene.objects.get(self.name.strip())
- if o is not None:
- m = o.modifiers.get("AutoBoolean")
- if m is not None:
- o.hide_viewport = False
- # o.display_type = 'TEXTURED'
- # m.show_viewport = True
-
- return self.cancel(context)
- return {'PASS_THROUGH'}
-
- def execute(self, context):
- global update_timer
- global update_timer_updating
- global throttle_delay
- global throttle_start
- if update_timer is not None:
- context.window_manager.event_timer_remove(update_timer)
- if update_timer_updating:
- return {'CANCELLED'}
- # reset update_timer so it only occurs once 0.1s after last action
- throttle_start = time.time()
- update_timer = context.window_manager.event_timer_add(throttle_delay, context.window)
- return {'CANCELLED'}
- throttle_start = time.time()
- update_timer_updating = False
- context.window_manager.modal_handler_add(self)
- update_timer = context.window_manager.event_timer_add(throttle_delay, context.window)
- return {'RUNNING_MODAL'}
-
- def cancel(self, context):
- global update_timer
- context.window_manager.event_timer_remove(update_timer)
- update_timer = None
- return {'CANCELLED'}
-
-
-class ARCHIPACK_PT_wall2(Panel):
- bl_idname = "ARCHIPACK_PT_wall2"
- bl_label = "Wall"
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'UI'
- bl_category = 'Archipack'
-
- def draw(self, context):
- prop = archipack_wall2.datablock(context.object)
- if prop is None:
- return
- layout = self.layout
- row = layout.row(align=True)
- row.operator("archipack.wall2_manipulate", icon='VIEW_PAN')
- # row = layout.row(align=True)
- # row.prop(prop, 'realtime')
- box = layout.box()
- box.prop(prop, 'n_parts')
- box.prop(prop, 'step_angle')
- box.prop(prop, 'width')
- box.prop(prop, 'z')
- box.prop(prop, 'flip')
- box.prop(prop, 'x_offset')
- row = layout.row()
- row.prop(prop, "closed")
- row = layout.row()
- row.prop_search(prop, "t_part", context.scene, "objects", text="T link", icon='OBJECT_DATAMODE')
- layout.operator("archipack.wall2_reverse", icon='FILE_REFRESH')
- row = layout.row(align=True)
- row.operator("archipack.wall2_fit_roof")
- # row.operator("archipack.wall2_fit_roof", text="Inside").inside = True
- n_parts = prop.n_parts
- if prop.closed:
- n_parts += 1
- for i, part in enumerate(prop.parts):
- if i < n_parts:
- box = layout.box()
- part.draw(box, context, i)
-
- @classmethod
- def poll(cls, context):
- return archipack_wall2.filter(context.active_object)
-
-
-# ------------------------------------------------------------------
-# Define operator class to create object
-# ------------------------------------------------------------------
-
-
-class ARCHIPACK_OT_wall2(ArchipackCreateTool, Operator):
- bl_idname = "archipack.wall2"
- bl_label = "Wall"
- bl_description = "Create a Wall"
- bl_category = 'Archipack'
- bl_options = {'REGISTER', 'UNDO'}
-
- def create(self, context):
- m = bpy.data.meshes.new("Wall")
- o = bpy.data.objects.new("Wall", m)
- d = m.archipack_wall2.add()
- d.manipulable_selectable = True
- self.link_object_to_scene(context, o)
- o.select_set(state=True)
- # around 12 degree
- m.auto_smooth_angle = 0.20944
- context.view_layer.objects.active = o
- self.load_preset(d)
- 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_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'}
-
-
-class ARCHIPACK_OT_wall2_from_curve(Operator):
- bl_idname = "archipack.wall2_from_curve"
- bl_label = "Wall curve"
- bl_description = "Create a wall from a curve"
- bl_category = 'Archipack'
- bl_options = {'REGISTER', 'UNDO'}
-
- auto_manipulate : BoolProperty(default=True)
-
- @classmethod
- def poll(self, context):
- return context.active_object is not None and context.active_object.type == 'CURVE'
-
- def create(self, context):
- curve = context.active_object
- for spline in curve.data.splines:
- bpy.ops.archipack.wall2(auto_manipulate=self.auto_manipulate)
- o = context.view_layer.objects.active
- d = archipack_wall2.datablock(o)
- d.from_spline(curve.matrix_world, 12, spline)
- if spline.type == 'POLY':
- pt = spline.points[0].co
- elif spline.type == 'BEZIER':
- pt = spline.bezier_points[0].co
- else:
- pt = Vector((0, 0, 0))
- # pretranslate
- o.matrix_world = curve.matrix_world @ Matrix([
- [1, 0, 0, pt.x],
- [0, 1, 0, pt.y],
- [0, 0, 1, pt.z],
- [0, 0, 0, 1]
- ])
- return o
-
- # -----------------------------------------------------
- # Execute
- # -----------------------------------------------------
- def execute(self, context):
- if context.mode == "OBJECT":
- bpy.ops.object.select_all(action="DESELECT")
- o = self.create(context)
- if o is not None:
- o.select_set(state=True)
- context.view_layer.objects.active = o
- return {'FINISHED'}
- else:
- self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
- return {'CANCELLED'}
-
-
-class ARCHIPACK_OT_wall2_from_slab(Operator):
- bl_idname = "archipack.wall2_from_slab"
- bl_label = "->Wall"
- bl_description = "Create a wall from a slab"
- bl_category = 'Archipack'
- bl_options = {'REGISTER', 'UNDO'}
-
- auto_manipulate : BoolProperty(default=True)
-
- @classmethod
- def poll(self, context):
- o = context.active_object
- return o is not None and o.data is not None and 'archipack_slab' in o.data
-
- def create(self, context):
- slab = context.active_object
- wd = slab.data.archipack_slab[0]
- bpy.ops.archipack.wall2(auto_manipulate=self.auto_manipulate)
- o = context.view_layer.objects.active
- d = archipack_wall2.datablock(o)
- d.auto_update = False
- d.parts.clear()
- d.n_parts = wd.n_parts - 1
- d.closed = True
- for part in wd.parts:
- p = d.parts.add()
- if "S_" in part.type:
- p.type = "S_WALL"
- else:
- p.type = "C_WALL"
- p.length = part.length
- p.radius = part.radius
- p.da = part.da
- p.a0 = part.a0
- o.select_set(state=True)
- context.view_layer.objects.active = o
- d.auto_update = True
- # pretranslate
- o.matrix_world = slab.matrix_world.copy()
-
- bpy.ops.object.select_all(action='DESELECT')
- # parenting childs to wall reference point
- if o.parent is None:
- x, y, z = o.bound_box[0]
- context.scene.cursor.location = o.matrix_world @ Vector((x, y, z))
- # fix issue #9
- context.view_layer.objects.active = o
- bpy.ops.archipack.reference_point()
- else:
- o.parent.select_set(state=True)
- context.view_layer.objects.active = o.parent
- o.select_set(state=True)
- slab.select_set(state=True)
- bpy.ops.archipack.parent_to_reference()
- o.parent.select_set(state=False)
- return o
-
- # -----------------------------------------------------
- # Execute
- # -----------------------------------------------------
- def execute(self, context):
- if context.mode == "OBJECT":
- bpy.ops.object.select_all(action="DESELECT")
- o = self.create(context)
- o.select_set(state=True)
- context.view_layer.objects.active = o
- return {'FINISHED'}
- else:
- self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
- return {'CANCELLED'}
-
-
-class ARCHIPACK_OT_wall2_fit_roof(Operator):
- bl_idname = "archipack.wall2_fit_roof"
- bl_label = "Fit roof"
- bl_description = "Fit roof"
- bl_category = 'Archipack'
- bl_options = {'REGISTER', 'UNDO'}
-
- inside : BoolProperty(default=False)
-
- @classmethod
- def poll(self, context):
- return archipack_wall2.filter(context.active_object)
-
- def execute(self, context):
- o = context.active_object
- d = archipack_wall2.datablock(o)
- g = d.get_generator()
- r, rd = d.find_roof(context, o, g)
- if rd is not None:
- d.setup_childs(o, g)
- rd.make_wall_fit(context, r, o, self.inside)
- return {'FINISHED'}
-
-# ------------------------------------------------------------------
-# Define operator class to draw a wall
-# ------------------------------------------------------------------
-
-
-class ARCHIPACK_OT_wall2_draw(ArchipackDrawTool, Operator):
- bl_idname = "archipack.wall2_draw"
- bl_label = "Draw a Wall"
- bl_description = "Create a wall by drawing its baseline in 3D view"
- bl_category = 'Archipack'
-
- o = None
- state = 'RUNNING'
- flag_create = False
- flag_next = False
- wall_part1 = None
- wall_line1 = None
- line = None
- label = None
- feedback = None
- takeloc = Vector((0, 0, 0))
- sel = []
- act = None
-
- # constraint to other wall and make a T child
- parent = None
- takemat = None
-
- @classmethod
- def poll(cls, context):
- return True
-
- def draw_callback(self, _self, context):
- self.feedback.draw(context)
-
- def sp_draw(self, sp, context):
- z = 2.7
- if self.state == 'CREATE':
- p0 = self.takeloc
- else:
- p0 = sp.takeloc
-
- p1 = sp.placeloc
- delta = p1 - p0
- # print("sp_draw state:%s delta:%s p0:%s p1:%s" % (self.state, delta.length, p0, p1))
- if delta.length == 0:
- return
- self.wall_part1.set_pos([p0, p1, Vector((p1.x, p1.y, p1.z + z)), Vector((p0.x, p0.y, p0.z + z))])
- self.wall_line1.set_pos([p0, p1, Vector((p1.x, p1.y, p1.z + z)), Vector((p0.x, p0.y, p0.z + z))])
- self.wall_part1.draw(context)
- self.wall_line1.draw(context)
- self.line.p = p0
- self.line.v = delta
- self.label.set_pos(context, self.line.length, self.line.lerp(0.5), self.line.v, normal=Vector((0, 0, 1)))
- self.label.draw(context)
- self.line.draw(context)
-
- def sp_callback(self, context, event, state, sp):
- logger.debug("ARCHIPACK_OT_wall2_draw.sp_callback event %s %s state:%s", event.type, event.value, state)
-
- if state == 'SUCCESS':
-
- if self.state == 'CREATE':
- takeloc = self.takeloc
- delta = sp.placeloc - self.takeloc
- else:
- takeloc = sp.takeloc
- delta = sp.delta
-
- old = context.object
- if self.o is None:
- bpy.ops.archipack.wall2(auto_manipulate=False)
- o = context.object
- o.location = takeloc
- self.o = o
- d = archipack_wall2.datablock(o)
-
- part = d.parts[0]
- part.length = delta.length
- else:
- o = self.o
- # select and make active
- o.select_set(state=True)
- context.view_layer.objects.active = o
- d = archipack_wall2.datablock(o)
- # Check for end close to start and close when applicable
- dp = sp.placeloc - o.location
- if dp.length < 0.01:
- d.closed = True
- self.state = 'CANCEL'
- return
-
- part = d.add_part(context, delta.length)
-
- # print("self.o :%s" % o.name)
- rM = o.matrix_world.inverted().to_3x3()
- g = d.get_generator()
- w = g.segs[-2]
- dp = rM @ delta
- da = atan2(dp.y, dp.x) - w.straight(1).angle
- a0 = part.a0 + da
- if a0 > pi:
- a0 -= 2 * pi
- if a0 < -pi:
- a0 += 2 * pi
- part.a0 = a0
- d.update(context)
-
- old.select_set(state=True)
- context.view_layer.objects.active = old
- self.flag_next = True
- context.area.tag_redraw()
- # print("feedback.on:%s" % self.feedback.on)
-
- self.state = state
-
- def sp_init(self, context, event, state, sp):
- # print("sp_init event %s %s %s" % (event.type, event.value, state))
- if state == 'SUCCESS':
- # point placed, check if a wall was under mouse
- res, tM, wall, width, y, z_offset = self.mouse_hover_wall(context, event)
- if res:
- d = archipack_wall2.datablock(wall)
- if event.ctrl:
- # user snap, use direction as constraint
- tM.translation = sp.placeloc.copy()
- else:
- # without snap, use wall's bottom
- tM.translation -= y.normalized() * (0.5 * d.width)
- self.takeloc = tM.translation
- self.parent = wall.name
- self.takemat = tM
- else:
- self.takeloc = sp.placeloc.copy()
-
- self.state = 'RUNNING'
- # print("feedback.on:%s" % self.feedback.on)
- elif state == 'CANCEL':
- self.state = state
- return
-
- def ensure_ccw(self):
- """
- Wall to slab expect wall vertex order to be ccw
- so reverse order here when needed
- """
- d = archipack_wall2.datablock(self.o)
- g = d.get_generator(axis=False)
- pts = [seg.p0 for seg in g.segs]
-
- if d.closed:
- pts.append(pts[0])
-
- if d.is_cw(pts):
- d.x_offset = 1
- pts = list(reversed(pts))
- self.o.location += pts[0] - pts[-1]
-
- d.from_points(pts, d.closed)
-
- def modal(self, context, event):
-
- context.area.tag_redraw()
- if event.type in {'NONE', 'TIMER', 'TIMER_REPORT', 'EVT_TWEAK_L', 'WINDOW_DEACTIVATE'}:
- return {'PASS_THROUGH'}
-
- if self.keymap.check(event, self.keymap.delete):
- self.feedback.disable()
- bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
- self.o = None
- return {'FINISHED', 'PASS_THROUGH'}
-
- if self.state == 'STARTING' and event.type not in {'ESC', 'RIGHTMOUSE'}:
- # wait for takeloc being visible when button is over horizon
- takeloc = self.mouse_to_plane(context, event)
- if takeloc is not None:
- logger.debug("ARCHIPACK_OT_wall2_draw.modal(STARTING) location:%s", takeloc)
- snap_point(takeloc=takeloc,
- callback=self.sp_init,
- constraint_axis=(True, True, False),
- release_confirm=True)
- return {'RUNNING_MODAL'}
-
- elif self.state == 'RUNNING':
- # print("RUNNING")
- logger.debug("ARCHIPACK_OT_wall2_draw.modal(RUNNING) location:%s", self.takeloc)
- self.state = 'CREATE'
- snap_point(takeloc=self.takeloc,
- draw=self.sp_draw,
- takemat=self.takemat,
- transform_orientation=context.scene.transform_orientation_slots[0].type,
- callback=self.sp_callback,
- constraint_axis=(True, True, False),
- release_confirm=self.max_style_draw_tool)
- return {'RUNNING_MODAL'}
-
- elif self.state != 'CANCEL' and event.type in {'C', 'c'}:
-
- logger.debug("ARCHIPACK_OT_wall2_draw.modal(%s) C pressed", self.state)
- self.feedback.disable()
- bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
-
- o = self.o
- # select and make active
- o.select_set(state=True)
- context.view_layer.objects.active = o
-
- d = archipack_wall2.datablock(o)
- d.closed = True
-
- if bpy.ops.archipack.manipulate.poll():
- bpy.ops.archipack.manipulate('INVOKE_DEFAULT')
-
- return {'FINISHED'}
-
- elif self.state != 'CANCEL' and event.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER', 'SPACE'}:
-
- # print('LEFTMOUSE %s' % (event.value))
- self.feedback.instructions(context, "Draw a wall", "Click & Drag to add a segment", [
- ('ENTER', 'Add part'),
- ('BACK_SPACE', 'Remove part'),
- ('CTRL', 'Snap'),
- ('C', 'Close wall and exit'),
- ('MMBTN', 'Constraint to axis'),
- ('X Y', 'Constraint to axis'),
- ('RIGHTCLICK or ESC', 'exit')
- ])
-
- # press with max mode release with blender mode
- if self.max_style_draw_tool:
- evt_value = 'PRESS'
- else:
- evt_value = 'RELEASE'
-
- if event.value == evt_value:
-
- if self.flag_next:
- self.flag_next = False
- o = self.o
-
- # select and make active
- o.select_set(state=True)
- context.view_layer.objects.active = o
-
- d = archipack_wall2.datablock(o)
- g = d.get_generator()
- p0 = g.segs[-2].p0
- p1 = g.segs[-2].p1
- dp = p1 - p0
- takemat = o.matrix_world @ Matrix([
- [dp.x, dp.y, 0, p1.x],
- [dp.y, -dp.x, 0, p1.y],
- [0, 0, 1, 0],
- [0, 0, 0, 1]
- ])
- takeloc = o.matrix_world @ p1.to_3d()
- o.select_set(state=False)
- else:
- takemat = None
- takeloc = self.mouse_to_plane(context, event)
-
- if takeloc is not None:
- logger.debug("ARCHIPACK_OT_wall2_draw.modal(CREATE) location:%s", takeloc)
-
- snap_point(takeloc=takeloc,
- takemat=takemat,
- draw=self.sp_draw,
- callback=self.sp_callback,
- constraint_axis=(True, True, False),
- release_confirm=self.max_style_draw_tool)
-
- return {'RUNNING_MODAL'}
-
- if self.keymap.check(event, self.keymap.undo) or (
- event.type in {'BACK_SPACE'} and event.value == 'RELEASE'
- ):
- if self.o is not None:
- o = self.o
-
- # select and make active
- o.select_set(state=True)
- context.view_layer.objects.active = o
- d = archipack_wall2.datablock(o)
- if d.n_parts > 1:
- d.n_parts -= 1
- return {'RUNNING_MODAL'}
-
- if self.state == 'CANCEL' or (event.type in {'ESC', 'RIGHTMOUSE'} and
- event.value == 'RELEASE'):
-
- self.feedback.disable()
- bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
- logger.debug("ARCHIPACK_OT_wall2_draw.modal(CANCEL) %s", event.type)
- if self.o is None:
- for o in self.sel:
- o.select_set(state=True)
- # select and make active
- if self.act is not None:
- self.act.select_set(state=True)
- context.view_layer.objects.active = self.act
-
- else:
- o = self.o
- o.select_set(state=True)
- context.view_layer.objects.active = o
-
- # remove last segment with blender mode
- d = archipack_wall2.datablock(o)
- if not self.max_style_draw_tool:
- if not d.closed and d.n_parts > 1:
- d.n_parts -= 1
- o.select_set(state=True)
- context.view_layer.objects.active = o
- # make T child
- if self.parent is not None:
- d.t_part = self.parent
-
- if bpy.ops.archipack.manipulate.poll():
- bpy.ops.archipack.manipulate('INVOKE_DEFAULT', object_name=o.name)
-
- return {'FINISHED'}
-
- return {'PASS_THROUGH'}
-
- def invoke(self, context, event):
-
- if context.mode == "OBJECT":
- prefs = context.preferences.addons[__name__.split('.')[0]].preferences
- self.max_style_draw_tool = prefs.max_style_draw_tool
- self.keymap = Keymaps(context)
- self.wall_part1 = GlPolygon((0.5, 0, 0, 0.2))
- self.wall_line1 = GlPolyline((0.5, 0, 0, 0.8))
- self.line = GlLine()
- self.label = GlText()
- self.feedback = FeedbackPanel()
- self.feedback.instructions(context, "Draw a wall", "Click & Drag to start", [
- ('CTRL', 'Snap'),
- ('MMBTN', 'Constraint to axis'),
- ('X Y', 'Constraint to axis'),
- ('SHIFT+CTRL+TAB', 'Switch snap mode'),
- ('RIGHTCLICK or ESC', 'exit without change')
- ])
- self.feedback.enable()
- args = (self, context)
-
- self.sel = context.selected_objects[:]
- self.act = context.active_object
- bpy.ops.object.select_all(action="DESELECT")
-
- self.state = 'STARTING'
-
- self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback, args, 'WINDOW', 'POST_PIXEL')
- context.window_manager.modal_handler_add(self)
- return {'RUNNING_MODAL'}
- else:
- self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
- return {'CANCELLED'}
-
-
-# ------------------------------------------------------------------
-# Define operator class to manage parts
-# ------------------------------------------------------------------
-
-
-class ARCHIPACK_OT_wall2_insert(Operator):
- bl_idname = "archipack.wall2_insert"
- bl_label = "Insert"
- bl_description = "Insert part"
- bl_category = 'Archipack'
- bl_options = {'REGISTER', 'UNDO'}
- index : IntProperty(default=0)
-
- def execute(self, context):
- if context.mode == "OBJECT":
- o = context.active_object
- d = archipack_wall2.datablock(o)
- if d is None:
- return {'CANCELLED'}
- d.insert_part(context, o, self.index)
- return {'FINISHED'}
- else:
- self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
- return {'CANCELLED'}
-
-
-class ARCHIPACK_OT_wall2_remove(Operator):
- bl_idname = "archipack.wall2_remove"
- bl_label = "Remove"
- bl_description = "Remove part"
- bl_category = 'Archipack'
- bl_options = {'REGISTER', 'UNDO'}
- index : IntProperty(default=0)
-
- def execute(self, context):
- if context.mode == "OBJECT":
- o = context.active_object
- d = archipack_wall2.datablock(o)
- if d is None:
- return {'CANCELLED'}
- d.remove_part(context, o, self.index)
- return {'FINISHED'}
- else:
- self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
- return {'CANCELLED'}
-
-
-class ARCHIPACK_OT_wall2_reverse(Operator):
- bl_idname = "archipack.wall2_reverse"
- bl_label = "Reverse"
- bl_description = "Reverse parts order"
- bl_category = 'Archipack'
- bl_options = {'REGISTER', 'UNDO'}
-
- def execute(self, context):
- if context.mode == "OBJECT":
- o = context.active_object
- d = archipack_wall2.datablock(o)
- if d is None:
- return {'CANCELLED'}
- d.reverse(context, o)
- return {'FINISHED'}
- else:
- self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
- return {'CANCELLED'}
-
-
-# ------------------------------------------------------------------
-# Define operator class to manipulate object
-# ------------------------------------------------------------------
-
-
-class ARCHIPACK_OT_wall2_manipulate(Operator):
- bl_idname = "archipack.wall2_manipulate"
- bl_label = "Manipulate"
- bl_description = "Manipulate"
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(self, context):
- return archipack_wall2.filter(context.active_object)
-
- def invoke(self, context, event):
- d = archipack_wall2.datablock(context.active_object)
- d.manipulable_invoke(context)
- return {'FINISHED'}
-
- def execute(self, context):
- """
- For use in boolean ops
- """
- if archipack_wall2.filter(context.active_object):
- o = context.active_object
- d = archipack_wall2.datablock(o)
- g = d.get_generator()
- d.setup_childs(o, g)
- d.update_childs(context, o, g)
- d.update(context)
- o.select_set(state=True)
- context.view_layer.objects.active = o
- return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_class(archipack_wall2_part)
- bpy.utils.register_class(archipack_wall2_child)
- bpy.utils.register_class(archipack_wall2)
- Mesh.archipack_wall2 = CollectionProperty(type=archipack_wall2)
- bpy.utils.register_class(ARCHIPACK_PT_wall2)
- bpy.utils.register_class(ARCHIPACK_OT_wall2)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_draw)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_insert)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_remove)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_reverse)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_manipulate)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_from_curve)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_from_slab)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_throttle_update)
- bpy.utils.register_class(ARCHIPACK_OT_wall2_fit_roof)
-
-
-def unregister():
- bpy.utils.unregister_class(archipack_wall2_part)
- bpy.utils.unregister_class(archipack_wall2_child)
- bpy.utils.unregister_class(archipack_wall2)
- del Mesh.archipack_wall2
- bpy.utils.unregister_class(ARCHIPACK_PT_wall2)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_draw)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_insert)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_remove)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_reverse)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_manipulate)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_from_curve)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_from_slab)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_throttle_update)
- bpy.utils.unregister_class(ARCHIPACK_OT_wall2_fit_roof)