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:
Diffstat (limited to 'archipack/archipack_floor.py')
-rw-r--r--archipack/archipack_floor.py2720
1 files changed, 1805 insertions, 915 deletions
diff --git a/archipack/archipack_floor.py b/archipack/archipack_floor.py
index 7f02c2dd..29957716 100644
--- a/archipack/archipack_floor.py
+++ b/archipack/archipack_floor.py
@@ -14,826 +14,1221 @@
#
# 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.
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
# ----------------------------------------------------------
-# Base code inspired by JARCH Vis
-# Original Author: Jacob Morris
-# Author : Stephen Leger (s-leger)
+# Author: Jacob Morris - Stephen Leger (s-leger)
# ----------------------------------------------------------
import bpy
from bpy.types import Operator, PropertyGroup, Mesh, Panel
from bpy.props import (
- BoolProperty, EnumProperty, FloatProperty,
- IntProperty, CollectionProperty
+ FloatProperty, CollectionProperty, StringProperty,
+ BoolProperty, IntProperty, EnumProperty
)
-from random import uniform, randint
-from math import tan, pi, sqrt
-from mathutils import Vector
+from mathutils import Vector, Matrix
+from mathutils.geometry import interpolate_bezier
+from random import uniform
+from math import radians, cos, sin, pi, atan2, sqrt
+import bmesh
from .bmesh_utils import BmeshEdit as bmed
-from .archipack_manipulator import Manipulable
+from .archipack_2d import Line, Arc
+from .archipack_manipulator import Manipulable, archipack_manipulator
from .archipack_preset import ArchipackPreset, PresetMenuOperator
from .archipack_object import ArchipackCreateTool, ArchipackObject
+from .archipack_cutter import (
+ CutAblePolygon, CutAbleGenerator,
+ ArchipackCutter,
+ ArchipackCutterPart
+ )
-def create_flooring(if_tile, over_width, over_length, b_width, b_length, b_length2, is_length_vary,
- length_vary, num_boards, space_l, space_w, spacing, t_width, t_length, is_offset, offset,
- is_ran_offset, offset_vary, t_width2, is_width_vary, width_vary, max_boards, is_ran_thickness,
- ran_thickness, th, hb_dir):
-
- # create siding
- if if_tile == "1": # Tiles Regular
- return tile_regular(over_width, over_length, t_width, t_length, spacing, is_offset, offset,
- is_ran_offset, offset_vary, th)
- elif if_tile == "2": # Large + Small
- return tile_ls(over_width, over_length, t_width, t_length, spacing, th)
- elif if_tile == "3": # Large + Many Small
- return tile_lms(over_width, over_length, t_width, spacing, th)
- elif if_tile == "4": # Hexagonal
- return tile_hexagon(over_width, over_length, t_width2, spacing, th)
- elif if_tile == "21": # Planks
- return wood_regular(over_width, over_length, b_width, b_length, space_l, space_w,
- is_length_vary, length_vary,
- is_width_vary, width_vary,
- is_offset, offset,
- is_ran_offset, offset_vary,
- max_boards, is_ran_thickness,
- ran_thickness, th)
- elif if_tile == "22": # Parquet
- return wood_parquet(over_width, over_length, b_width, spacing, num_boards, th)
- elif if_tile == "23": # Herringbone Parquet
- return wood_herringbone(over_width, over_length, b_width, b_length2, spacing, th, hb_dir, True)
- elif if_tile == "24": # Herringbone
- return wood_herringbone(over_width, over_length, b_width, b_length2, spacing, th, hb_dir, False)
-
- return [], []
-
-
-def wood_herringbone(ow, ol, bw, bl, s, th, hb_dir, stepped):
- verts = []
- faces = []
- an_45 = 0.5 * sqrt(2)
- x, y, z = 0.0, 0.0, th
- x_off, y_off = 0.0, 0.0 # used for finding farther forwards points when stepped
- ang_s = s * an_45
- s45 = s / an_45
-
- # step variables
- if stepped:
- x_off = an_45 * bw
- y_off = an_45 * bw
-
- wid_off = an_45 * bl # offset from one end of the board to the other inline with width
- len_off = an_45 * bl # offset from one end of the board to the other inline with length
- w = bw / an_45 # width adjusted for 45 degree rotation
-
- # figure out starting position
- if hb_dir == "1":
- y = -wid_off
-
- elif hb_dir == "2":
- x = ow
- y = ol + wid_off
-
- elif hb_dir == "3":
- x = -wid_off
- y = ol
-
- elif hb_dir == "4":
- x = ow + wid_off
-
- # loop going forwards
- while (hb_dir == "1" and y < ol + wid_off) or (hb_dir == "2" and y > 0 - wid_off) or \
- (hb_dir == "3" and x < ow + wid_off) or (hb_dir == "4" and x > 0 - wid_off):
- going_forwards = True
-
- # loop going right
- while (hb_dir == "1" and x < ow) or (hb_dir == "2" and x > 0) or (hb_dir == "3" and y > 0 - y_off) or \
- (hb_dir == "4" and y < ol + y_off):
- p = len(verts)
-
- # add verts
- # forwards
- verts.append((x, y, z))
-
- if hb_dir == "1":
-
- if stepped and x != 0:
- verts.append((x - x_off, y + y_off, z))
- else:
- verts.append((x, y + w, z))
-
- if going_forwards:
- y += wid_off
- else:
- y -= wid_off
- x += len_off
-
- verts.append((x, y, z))
- if stepped:
- verts.append((x - x_off, y + y_off, z))
- x -= x_off - ang_s
- if going_forwards:
- y += y_off + ang_s
- else:
- y -= y_off + ang_s
- else:
- verts.append((x, y + w, z))
- x += s
-
- # backwards
- elif hb_dir == "2":
-
- if stepped and x != ow:
- verts.append((x + x_off, y - y_off, z))
- else:
- verts.append((x, y - w, z))
-
- if going_forwards:
- y -= wid_off
- else:
- y += wid_off
- x -= len_off
-
- verts.append((x, y, z))
- if stepped:
- verts.append((x + x_off, y - y_off, z))
- x += x_off - ang_s
- if going_forwards:
- y -= y_off + ang_s
- else:
- y += y_off + ang_s
- else:
- verts.append((x, y - w, z))
- x -= s
- # right
- elif hb_dir == "3":
-
- if stepped and y != ol:
- verts.append((x + y_off, y + x_off, z))
- else:
- verts.append((x + w, y, z))
-
- if going_forwards:
- x += wid_off
- else:
- x -= wid_off
- y -= len_off
-
- verts.append((x, y, z))
- if stepped:
- verts.append((x + y_off, y + x_off, z))
- y += x_off - ang_s
- if going_forwards:
- x += y_off + ang_s
- else:
- x -= y_off + ang_s
- else:
- verts.append((x + w, y, z))
- y -= s
- # left
- else:
-
- if stepped and y != 0:
- verts.append((x - y_off, y - x_off, z))
- else:
- verts.append((x - w, y, z))
-
- if going_forwards:
- x -= wid_off
- else:
- x += wid_off
- y += len_off
-
- verts.append((x, y, z))
- if stepped:
- verts.append((x - y_off, y - x_off, z))
- y -= x_off - ang_s
- if going_forwards:
- x -= y_off + ang_s
- else:
- x += y_off + ang_s
- else:
- verts.append((x - w, y, z))
- y += s
-
- # faces
- faces.append((p, p + 2, p + 3, p + 1))
-
- # flip going_right
- going_forwards = not going_forwards
- x_off *= -1
-
- # if not in forwards position, then move back before adjusting values for next row
- if not going_forwards:
- x_off = abs(x_off)
- if hb_dir == "1":
- y -= wid_off
- if stepped:
- y -= y_off + ang_s
- elif hb_dir == "2":
- y += wid_off
- if stepped:
- y += y_off + ang_s
- elif hb_dir == "3":
- x -= wid_off
- if stepped:
- x -= y_off + ang_s
- else:
- x += wid_off
- if stepped:
- x += y_off + ang_s
-
- # adjust forwards
- if hb_dir == "1":
- y += w + s45
- x = 0
- elif hb_dir == "2":
- y -= w + s45
- x = ow
- elif hb_dir == "3":
- x += w + s45
- y = ol
- else:
- x -= w + s45
- y = 0
-
- return verts, faces
-
+# ------------------------------------------------------------------
+# Define property class to store object parameters and update mesh
+# ------------------------------------------------------------------
-def tile_ls(ow, ol, tw, tl, s, z):
- """
- pattern:
- _____
- | |_|
- |___|
- x and y are axis of big one
- """
+class Floor():
- verts = []
- faces = []
+ def __init__(self):
+ # self.colour_inactive = (1, 1, 1, 1)
+ pass
- # big half size
- hw = (tw / 2) - (s / 2)
- hl = (tl / 2) - (s / 2)
- # small half size
- hws = (tw / 4) - (s / 2)
- hls = (tl / 4) - (s / 2)
+ def set_offset(self, offset, last=None):
+ """
+ Offset line and compute intersection point
+ between segments
+ """
+ self.line = self.make_offset(offset, last)
- # small, offset from big x,y
- xo = 0.75 * tw
- yo = 0.25 * tl
+ def straight_floor(self, a0, length):
+ s = self.straight(length).rotate(a0)
+ return StraightFloor(s.p, s.v)
- # offset for pattern
- rx = 2.5 * tw
- ry = 0.5 * tl
+ def curved_floor(self, a0, da, radius):
+ n = self.normal(1).rotate(a0).scale(radius)
+ if da < 0:
+ n.v = -n.v
+ a0 = n.angle
+ c = n.p - n.v
+ return CurvedFloor(c, radius, a0, da)
- # width and a half of big
- ow_x = ow + 0.5 * tw
- ol_y = ol + 0.5 * tl
- # start pattern with big one
- x = tw
- y = -tl
+class StraightFloor(Floor, Line):
- while y < ol_y:
+ def __init__(self, p, v):
+ Line.__init__(self, p, v)
+ Floor.__init__(self)
- while x < ow_x:
- p = len(verts)
+class CurvedFloor(Floor, Arc):
- # Large
- x0 = max(0, x - hw)
- y0 = max(0, y - hl)
- x1 = min(ow, x + hw)
- y1 = min(ol, y + hl)
- if y1 > 0:
- if x1 > 0 and x0 < ow and y0 < ol:
+ def __init__(self, c, radius, a0, da):
+ Arc.__init__(self, c, radius, a0, da)
+ Floor.__init__(self)
- verts.extend([(x0, y1, z), (x1, y1, z), (x1, y0, z), (x0, y0, z)])
- faces.append((p, p + 1, p + 2, p + 3))
- p = len(verts)
- # Small
- x0 = x + xo - hws
- y0 = y + yo - hls
- x1 = min(ow, x + xo + hws)
+class FloorGenerator(CutAblePolygon, CutAbleGenerator):
- if x1 > 0 and x0 < ow and y0 < ol:
+ def __init__(self, parts):
+ self.parts = parts
+ self.segs = []
+ self.holes = []
+ self.convex = True
+ self.xsize = 0
- y1 = min(ol, y + yo + hls)
- verts.extend([(x0, y1, z), (x1, y1, z), (x1, y0, z), (x0, y0, z)])
- faces.append((p, p + 1, p + 2, p + 3))
+ def add_part(self, part):
- x += rx
-
- y += ry
- x = x % rx - tw
- if x < -tw:
- x += rx
-
- return verts, faces
+ if len(self.segs) < 1:
+ s = None
+ else:
+ s = self.segs[-1]
+ # start a new floor
+ if s is None:
+ if part.type == 'S_SEG':
+ p = Vector((0, 0))
+ v = part.length * Vector((cos(part.a0), sin(part.a0)))
+ s = StraightFloor(p, v)
+ elif part.type == 'C_SEG':
+ c = -part.radius * Vector((cos(part.a0), sin(part.a0)))
+ s = CurvedFloor(c, part.radius, part.a0, part.da)
+ else:
+ if part.type == 'S_SEG':
+ s = s.straight_floor(part.a0, part.length)
+ elif part.type == 'C_SEG':
+ s = s.curved_floor(part.a0, part.da, part.radius)
+
+ self.segs.append(s)
+ self.last_type = part.type
+
+ def set_offset(self):
+ last = None
+ for i, seg in enumerate(self.segs):
+ seg.set_offset(self.parts[i].offset, last)
+ last = seg.line
+
+ 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
+
+ if len(self.segs) > 1:
+ w.line = w.make_offset(self.parts[-1].offset, self.segs[-2].line)
+
+ p1 = self.segs[0].line.p1
+ self.segs[0].line = self.segs[0].make_offset(self.parts[0].offset, w.line)
+ self.segs[0].line.p1 = p1
+
+ def locate_manipulators(self):
+ """
+ setup manipulators
+ """
+ for i, f in enumerate(self.segs):
+
+ manipulators = self.parts[i].manipulators
+ p0 = f.p0.to_3d()
+ p1 = f.p1.to_3d()
+ # angle from last to current segment
+ if i > 0:
+ v0 = self.segs[i - 1].straight(-1, 1).v.to_3d()
+ v1 = f.straight(1, 0).v.to_3d()
+ manipulators[0].set_pts([p0, v0, v1])
+
+ if type(f).__name__ == "StraightFloor":
+ # segment length
+ manipulators[1].type_key = 'SIZE'
+ manipulators[1].prop1_name = "length"
+ manipulators[1].set_pts([p0, p1, (1, 0, 0)])
+ else:
+ # segment radius + angle
+ v0 = (f.p0 - f.c).to_3d()
+ v1 = (f.p1 - f.c).to_3d()
+ manipulators[1].type_key = 'ARC_ANGLE_RADIUS'
+ manipulators[1].prop1_name = "da"
+ manipulators[1].prop2_name = "radius"
+ manipulators[1].set_pts([f.c.to_3d(), v0, v1])
+
+ # snap manipulator, dont change index !
+ manipulators[2].set_pts([p0, p1, (1, 0, 0)])
+ # dumb segment id
+ manipulators[3].set_pts([p0, p1, (1, 0, 0)])
+
+ def get_verts(self, verts):
+ for s in self.segs:
+ if "Curved" in type(s).__name__:
+ for i in range(16):
+ # x, y = floor.line.lerp(i / 16)
+ verts.append(s.lerp(i / 16).to_3d())
+ else:
+ # x, y = s.line.p0
+ verts.append(s.p0.to_3d())
+ """
+ for i in range(33):
+ x, y = floor.line.lerp(i / 32)
+ verts.append((x, y, 0))
+ """
+
+ 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]
+ # rotate seg
+ seg.rotate(a)
+ # rotate delta from rotation center to segment start
+ 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 draw(self, context):
+ """
+ draw generator using gl
+ """
+ for seg in self.segs:
+ seg.draw(context, render=False)
+
+ def limits(self):
+ x_size = [s.p0.x for s in self.segs]
+ y_size = [s.p0.y for s in self.segs]
+ self.xmin = min(x_size)
+ self.xmax = max(x_size)
+ self.xsize = self.xmax - self.xmin
+ self.ymin = min(y_size)
+ self.ymax = max(y_size)
+ self.ysize = self.ymax - self.ymin
+
+ def cut(self, context, o):
+ """
+ either external or holes cuts
+ """
+ self.limits()
+ self.is_convex()
+ for b in o.children:
+ d = archipack_floor_cutter.datablock(b)
+ if d is not None:
+ g = d.ensure_direction()
+ g.change_coordsys(b.matrix_world, o.matrix_world)
+ self.slice(g)
+
+ def floor(self, context, o, d):
+
+ verts, faces, matids, uvs = [], [], [], []
+
+ if d.bevel:
+ bevel = d.bevel_amount
+ else:
+ bevel = 0
-def tile_hexagon(ow, ol, tw, s, z):
- verts = []
- faces = []
- offset = False
+ if d.add_grout:
+ thickness = min(d.thickness - d.mortar_depth, d.thickness - 0.0001)
+ bottom = min(d.thickness - (d.mortar_depth + bevel), d.thickness - 0.0001)
+ else:
+ thickness = d.thickness
+ bottom = 0
- w = tw / 2
- y = 0.0
- h = w * tan(pi / 6)
- r = sqrt((w * w) + (h * h))
+ self.top = d.thickness
- while y < ol + tw:
- if not offset:
- x = 0.0
- else:
- x = w + (s / 2)
+ self.generate_pattern(d, verts, faces, matids, uvs)
+ bm = bmed.buildmesh(
+ context, o, verts, faces, matids=matids, uvs=uvs,
+ weld=False, clean=False, auto_smooth=True, temporary=True)
- while x < ow + tw:
- p = len(verts)
+ self.cut_holes(bm, self)
+ self.cut_boundary(bm, self)
- verts.extend([(x + w, y + h, z), (x, y + r, z), (x - w, y + h, z),
- (x - w, y - h, z), (x, y - r, z), (x + w, y - h, z)])
- faces.extend([(p, p + 1, p + 2, p + 3), (p + 3, p + 4, p + 5, p)])
+ bmesh.ops.dissolve_limit(bm,
+ angle_limit=0.01,
+ use_dissolve_boundaries=False,
+ verts=bm.verts,
+ edges=bm.edges,
+ delimit=1)
- x += tw + s
+ bm.verts.ensure_lookup_table()
- y += r + h + s
- offset = not offset
+ # solidify and floor bottom
+ geom = bm.faces[:]
+ verts = bm.verts[:]
+ edges = bm.edges[:]
+ bmesh.ops.solidify(bm, geom=geom, thickness=0.0001)
+ for v in verts:
+ v.co.z = bottom
- return verts, faces
+ # bevel
+ if d.bevel:
+ for v in bm.verts:
+ v.select = True
+ for v in verts:
+ v.select = False
+ for v in bm.edges:
+ v.select = True
+ for v in edges:
+ v.select = False
+ geom = [v for v in bm.verts if v.select]
+ geom.extend([v for v in bm.edges if v.select])
+ bmesh.ops.bevel(bm,
+ geom=geom,
+ offset=d.bevel_amount,
+ offset_type=0,
+ segments=1, # d.bevel_res
+ profile=0.5,
+ vertex_only=False,
+ clamp_overlap=False,
+ material=-1)
+
+ bm.to_mesh(o.data)
+ bm.free()
+ # Grout
+ if d.add_grout:
+ verts = []
+ self.get_verts(verts)
+ #
+ 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)
+
+ self.cut_holes(bm, self)
+ self.cut_boundary(bm, self)
+
+ bmesh.ops.dissolve_limit(bm,
+ angle_limit=0.01,
+ use_dissolve_boundaries=False,
+ verts=bm.verts,
+ edges=bm.edges,
+ delimit=1)
+
+ bm.verts.ensure_lookup_table()
+
+ geom = bm.faces[:]
+ bmesh.ops.solidify(bm, geom=geom, thickness=thickness)
+ bmed.bmesh_join(context, o, [bm], normal_update=True)
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # ---------------------------------------------------
+ # Patterns
+ # ---------------------------------------------------
+
+ def regular_tile(self, d, verts, faces, matids, uvs):
+ """
+ ____ ____ ____
+ | || || | Regular tile, rows can be offset, either manually or randomly
+ |____||____||____|
+ ____ ____ ____
+ | || || |
+ |____||____||____|
+ """
+ off = False
+ o = 1 / (100 / d.offset) if d.offset != 0 else 0
+ y = self.ymin
+
+ while y < self.ymax:
+ x = self.xmin
+ tl2 = d.tile_length
+ if y < self.ymax < y + d.tile_length:
+ tl2 = self.ymax - y
+
+ while x < self.xmax:
+ tw2 = d.tile_width
+
+ if x < self.xmax < x + d.tile_width:
+ tw2 = self.xmax - x
+ elif x == self.xmin and off and not d.random_offset:
+ tw2 = d.tile_width * o
+ elif x == self.xmin and d.random_offset:
+ v = d.tile_width * d.offset_variance * 0.0049
+ tw2 = (d.tile_width / 2) + uniform(-v, v)
+
+ self.add_plane(d, verts, faces, matids, uvs, x, y, tw2, tl2)
+ x += tw2 + d.spacing
+
+ y += tl2 + d.spacing
+ off = not off
+
+ def hopscotch(self, d, verts, faces, matids, uvs):
+ """
+ ____ _ Large tile, plus small one on top right corner
+ | ||_|
+ |____| ____ _ But shifted up so next large one is right below previous small one
+ | ||_|
+ |____|
+ """
+ sp = d.spacing
+
+ # movement variables
+ row = 0
+
+ tw = d.tile_width
+ tl = d.tile_length
+ s_tw = (tw - sp) / 2 # small tile width
+ s_tl = (tl - sp) / 2 # small tile length
+ y = self.ymin - s_tl
+
+ pre_y = y
+ while y < self.ymax + s_tl or (row == 2 and y - sp < self.ymax):
+ x = self.xmin
+ step_back = True
+
+ if row == 1: # row start indented slightly
+ x = self.xmin + s_tw + sp
+
+ while x < self.xmax:
+ if row == 0 or row == 1:
+ # adjust for if there is a need to cut off the bottom of the tile
+ if y < self.ymin - s_tl:
+ self.add_plane(d, verts, faces, matids, uvs, x, y, tw, tl + y - self.ymin) # large one
+ else:
+ self.add_plane(d, verts, faces, matids, uvs, x, y, tw, tl) # large one
-def tile_lms(ow, ol, tw, s, z):
- verts = []
- faces = []
- small = True
+ self.add_plane(d, verts, faces, matids, uvs, x + tw + sp, y + s_tl + sp, s_tw, s_tl) # small one
- y = 0.0
- ref = (tw - s) / 2
+ if step_back:
+ x += tw + sp
+ y -= s_tl + sp
+ else:
+ x += tw + s_tw + 2 * sp
+ y += s_tl + sp
- while y < ol:
- x = 0.0
- large = False
- while x < ow:
- if small:
- x1 = min(x + ref, ow)
- y1 = min(y + ref, ol)
- p = len(verts)
- verts.extend([(x, y1, z), (x, y, z)])
- verts.extend([(x1, y1, z), (x1, y, z)])
- faces.append((p, p + 1, p + 3, p + 2))
- x += ref
- else:
- if not large:
- x1 = min(x + ref, ow)
- for i in range(2):
- y0 = y + i * (ref + s)
- if x < ow and y0 < ol:
- y1 = min(y0 + ref, ol)
- p = len(verts)
- verts.extend([(x, y1, z), (x, y0, z)])
- verts.extend([(x1, y1, z), (x1, y0, z)])
- faces.append((p, p + 1, p + 3, p + 2))
- x += ref
+ step_back = not step_back
else:
- x1 = min(x + tw, ow)
- y1 = min(y + tw, ol)
- p = len(verts)
- verts.extend([(x, y1, z), (x, y, z)])
- verts.extend([(x1, y1, z), (x1, y, z)])
- faces.append((p, p + 1, p + 3, p + 2))
- x += tw
- large = not large
- x += s
- if small:
- y += ref + s
- else:
- y += tw + s
- small = not small
-
- return verts, faces
+ if x == self.xmin: # half width for starting position
+ self.add_plane(d, verts, faces, matids, uvs, x, y, s_tw, tl) # large one
+ # small one on right
+ self.add_plane(d, verts, faces, matids, uvs, x + s_tw + sp, y + s_tl + sp, s_tw, s_tl)
+ # small one on bottom
+ self.add_plane(d, verts, faces, matids, uvs, x, y - sp - s_tl, s_tw, s_tl)
+ x += (2 * s_tw) + tw + (3 * sp)
+ else:
+ self.add_plane(d, verts, faces, matids, uvs, x, y, tw, tl) # large one
+ # small one on right
+ self.add_plane(d, verts, faces, matids, uvs, x + tw + sp, y + s_tl + sp, s_tw, s_tl)
+ x += (2 * tw) + (3 * sp) + s_tw
+ if row == 0 or row == 2:
+ y = pre_y + tl + sp
+ else:
+ y = pre_y + s_tl + sp
+ pre_y = y
+
+ row = (row + 1) % 3 # keep wrapping rows
+
+ def stepping_stone(self, d, verts, faces, matids, uvs):
+ """
+ ____ __ ____
+ | ||__|| | Row of large one, then two small ones stacked beside it
+ | | __ | |
+ |____||__||____|
+ __ __ __ __
+ |__||__||__||__| Row of smalls
+ """
+ sp = d.spacing
+ y = self.ymin
+ row = 0
+
+ tw = d.tile_width
+ tl = d.tile_length
+ s_tw = (tw - sp) / 2
+ s_tl = (tl - sp) / 2
+
+ while y < self.ymax:
+ x = self.xmin
+
+ while x < self.xmax:
+ if row == 0: # large one then two small ones stacked beside it
+ self.add_plane(d, verts, faces, matids, uvs, x, y, tw, tl)
+ self.add_plane(d, verts, faces, matids, uvs, x + tw + sp, y, s_tw, s_tl,)
+ self.add_plane(d, verts, faces, matids, uvs, x + tw + sp, y + s_tl + sp, s_tw, s_tl)
+ x += tw + s_tw + (2 * sp)
+ else: # row of small ones
+ self.add_plane(d, verts, faces, matids, uvs, x, y, s_tw, s_tl)
+ self.add_plane(d, verts, faces, matids, uvs, x + s_tw + sp, y, s_tw, s_tl)
+ x += tw + sp
+
+ if row == 0:
+ y += tl + sp
+ else:
+ y += s_tl + sp
+
+ row = (row + 1) % 2
+
+ def hexagon(self, d, verts, faces, matids, uvs):
+ """
+ __ Hexagon tiles
+ / \
+ \___/
+ """
+ sp = d.spacing
+ width = d.tile_width
+ dia = (width / 2) / cos(radians(30))
+ # top of current, half way up next, vertical spacing component
+ vertical_spacing = dia * (1 + sin(radians(30))) + (sp * sin(radians(60))) # center of one row to next row
+ da = pi / 3
+ base_points = [(sin(i * da), cos(i * da)) for i in range(6)]
+
+ y = self.ymin
+ offset = False
+ while y - width / 2 < self.ymax: # place tile as long as bottom is still within bounds
+ if offset:
+ x = self.xmin + width / 2
+ else:
+ x = self.xmin - sp / 2
-def tile_regular(ow, ol, tw, tl, s, is_offset, offset, is_ran_offset, offset_vary, z):
- verts = []
- faces = []
- off = False
- o = 1 / (100 / offset)
- y = 0.0
+ while x - width / 2 < self.xmax: # place tile as long as left is still within bounds
+ f = len(verts)
- while y < ol:
+ if d.vary_thickness and d.thickness_variance > 0:
+ v = d.thickness / 100 * d.thickness_variance
+ z = uniform(self.top, self.top + v)
+ else:
+ z = self.top
+
+ for pt in base_points:
+ verts.append((dia * pt[0] + x, dia * pt[1] + y, z))
+
+ faces.append([f] + [i for i in range(f + 1, len(verts))])
+ uvs.append(base_points)
+ self.add_matid(d, matids)
+
+ x += width + sp
+
+ y += vertical_spacing
+ offset = not offset
+
+ def windmill(self, d, verts, faces, matids, uvs):
+ """
+ __ ____
+ | ||____| This also has a square one in the middle, totaling 5 tiles per pattern
+ |__| __
+ ____ | |
+ |____||__|
+ """
+ sp = d.spacing
+
+ tw = d.tile_width
+ tl = d.tile_length
+ s_tw = (tw - sp) / 2
+ s_tl = (tl - sp) / 2
+
+ y = self.ymin
+ while y < self.ymax:
+ x = self.xmin
+
+ while x < self.xmax:
+ self.add_plane(d, verts, faces, matids, uvs, x, y, tw, s_tl) # bottom
+ self.add_plane(d, verts, faces, matids, uvs, x + tw + sp, y, s_tw, tl, rotate_uv=True) # right
+ self.add_plane(d, verts, faces, matids, uvs, x + s_tw + sp, y + tl + sp, tw, s_tl) # top
+ self.add_plane(d, verts, faces, matids, uvs, x, y + s_tl + sp, s_tw, tl, rotate_uv=True) # left
+ self.add_plane(d, verts, faces, matids, uvs, x + s_tw + sp, y + s_tl + sp, s_tw, s_tl) # center
+
+ x += tw + s_tw + (2 * sp)
+ y += tl + s_tl + (2 * sp)
+
+ def boards(self, d, verts, faces, matids, uvs):
+ """
+ ||| Typical wood boards
+ |||
+ """
+ x = self.xmin
+ bw, bl = d.board_width, d.board_length
+ off = False
+ o = 1 / (100 / d.offset) if d.offset != 0 else 0
+
+ while x < self.xmax:
+ if d.vary_width:
+ v = bw * (d.width_variance / 100) * 0.99
+ bw2 = bw + uniform(-v, v)
+ else:
+ bw2 = bw
+
+ if bw2 + x > self.xmax:
+ bw2 = self.xmax - x
+ y = self.ymin
+
+ counter = 1
+ while y < self.ymax:
+ bl2 = bl
+ if d.vary_length:
+ v = bl * (d.length_variance / 100) * 0.99
+ bl2 = bl + uniform(-v, v)
+ elif y == self.ymin and off and not d.random_offset:
+ bl2 = bl * o
+ elif y == self.ymin and d.random_offset:
+ v = bl * d.offset_variance * 0.0049
+ bl2 = (bl / 2) + uniform(-v, v)
+
+ if (counter >= d.max_boards and d.vary_length) or y + bl2 > self.ymax:
+ bl2 = self.ymax - y
+
+ self.add_plane(d, verts, faces, matids, uvs, x, y, bw2, bl2, rotate_uv=True)
+ y += bl2 + d.length_spacing
+ counter += 1
+ off = not off
+ x += bw2 + d.width_spacing
+
+ def square_parquet(self, d, verts, faces, matids, uvs):
+ """
+ ||--||-- Alternating groups oriented either horizontally, or forwards and backwards.
+ ||--||-- self.spacing is used because it is the same spacing for width and length
+ --||--|| Board width is calculated using number of boards and the length.
+ --||--||
+ """
+ x = self.xmin
+ start_orient_length = True
+
+ # figure board width
+ bl = d.short_board_length
+ bw = (bl - (d.boards_in_group - 1) * d.spacing) / d.boards_in_group
+ while x < self.xmax:
+ y = self.ymin
+ orient_length = start_orient_length
+ while y < self.ymax:
+
+ if orient_length:
+ start_x = x
+
+ for i in range(d.boards_in_group):
+ if x < self.xmax and y < self.ymax:
+ self.add_plane(d, verts, faces, matids, uvs, x, y, bw, bl, rotate_uv=True)
+ x += bw + d.spacing
+
+ x = start_x
+ y += bl + d.spacing
- tw2 = 0
- if is_offset:
- if is_ran_offset:
- v = tw * 0.0049 * offset_vary
- tw2 = uniform((tw / 2) - v, (tw / 2) + v)
- elif off:
- tw2 = o * tw
- x = -tw2
- y1 = min(ol, y + tl)
+ else:
+ for i in range(d.boards_in_group):
+ if x < self.xmax and y < self.ymax:
+ self.add_plane(d, verts, faces, matids, uvs, x, y, bl, bw)
+ y += bw + d.spacing
- while x < ow:
- p = len(verts)
- x0 = max(0, x)
- x1 = min(ow, x + tw)
+ orient_length = not orient_length
- verts.extend([(x0, y1, z), (x0, y, z), (x1, y, z), (x1, y1, z)])
- faces.append((p, p + 1, p + 2, p + 3))
- x = x1 + s
+ start_orient_length = not start_orient_length
+ x += bl + d.spacing
- y += tl + s
- off = not off
+ def herringbone(self, d, verts, faces, matids, uvs):
+ """
+ Boards are at 45 degree angle, in chevron pattern, ends are angled
+ """
+ width_dif = d.board_width / cos(radians(45))
+ x_dif = d.short_board_length * cos(radians(45))
+ y_dif = d.short_board_length * sin(radians(45))
+ total_y_dif = width_dif + y_dif
+ sp_dif = d.spacing / cos(radians(45))
- return verts, faces
+ y = self.ymin - y_dif
+ while y < self.ymax:
+ x = self.xmin
+ while x < self.xmax:
+ # left side
-def wood_parquet(ow, ol, bw, s, num_boards, z):
- verts = []
- faces = []
- x = 0.0
+ self.add_face(d, verts, faces, matids, uvs,
+ (x, y, 0), (x + x_dif, y + y_dif, 0),
+ (x + x_dif, y + total_y_dif, 0), (x, y + width_dif, 0))
- start_orient_length = True
+ x += x_dif + d.spacing
- # figure board length
- bl = (bw * num_boards) + (s * (num_boards - 1))
- while x < ow:
+ # right side
+ if x < self.xmax:
+ self.add_face(d, verts, faces, matids, uvs,
+ (x, y + y_dif, 0), (x + x_dif, y, 0),
+ (x + x_dif, y + width_dif, 0), (x, y + total_y_dif, 0))
+ x += x_dif + d.spacing
- y = 0.0
+ y += width_dif + sp_dif # adjust spacing amount for 45 degree angle
- orient_length = start_orient_length
+ def herringbone_parquet(self, d, verts, faces, matids, uvs):
+ """
+ Boards are at 45 degree angle, in chevron pattern, ends are square, not angled
+ """
- while y < ol:
+ an_45 = 0.5 * sqrt(2)
- if orient_length:
- y0 = y
- y1 = min(y + bl, ol)
+ x_dif = d.short_board_length * an_45
+ y_dif = d.short_board_length * an_45
+ y_dif_45 = d.board_width * an_45
+ x_dif_45 = d.board_width * an_45
+ total_y_dif = y_dif + y_dif_45
- for i in range(num_boards):
+ sp_dif = (d.spacing / an_45) / 2 # divide by two since it is used for both x and y
+ width_dif = d.board_width / an_45
- bx = x + i * (bw + s)
+ y = self.ymin - y_dif
+ while y - y_dif_45 < self.ymax: # continue as long as bottom left corner is still good
+ x = self.xmin
- if bx < ow and y < ol:
+ while x - x_dif_45 < self.xmax: # continue as long as top left corner is still good
+ # left side
- # make sure board should be placed
- x0 = bx
- x1 = min(bx + bw, ow)
+ self.add_face(d, verts, faces, matids, uvs,
+ (x, y, 0),
+ (x + x_dif, y + y_dif, 0),
+ (x + x_dif - x_dif_45, y + total_y_dif, 0),
+ (x - x_dif_45, y + y_dif_45, 0))
- p = len(verts)
- verts.extend([(x0, y0, z), (x1, y0, z), (x1, y1, z), (x0, y1, z)])
- faces.append((p, p + 1, p + 2, p + 3))
+ x += x_dif - x_dif_45 + sp_dif
+ y0 = y + y_dif - y_dif_45 - sp_dif
- else:
- x0 = x
- x1 = min(x + bl, ow)
+ if x < self.xmax:
+ self.add_face(d, verts, faces, matids, uvs,
+ (x, y0, 0),
+ (x + x_dif, y0 - y_dif, 0),
+ (x + x_dif + x_dif_45, y0 - y_dif + y_dif_45, 0),
+ (x + x_dif_45, y0 + y_dif_45, 0))
- for i in range(num_boards):
+ x += x_dif + x_dif_45 + sp_dif
- by = y + i * (bw + s)
+ else: # we didn't place the right board, so step ahead far enough the the while loop for x breaks
+ break
- if x < ow and by < ol:
- y0 = by
- y1 = min(by + bw, ol)
- p = len(verts)
+ y += width_dif + (2 * sp_dif)
- verts.extend([(x0, y0, z), (x1, y0, z), (x1, y1, z), (x0, y1, z)])
- faces.append((p, p + 1, p + 2, p + 3))
+ def add_matid(self, d, matids):
+ if d.vary_materials:
+ matid = uniform(1, d.matid)
+ else:
+ matid = d.matid
+ matids.append(matid)
+
+ def add_plane(self, d, verts, faces, matids, uvs, x, y, w, l, rotate_uv=False):
+ """
+ Adds vertices and faces for a place, clip to outer boundaries if clip is True
+ :param x: start x position
+ :param y: start y position
+ :param w: width (in x direction)
+ :param l: length (in y direction)
+ """
+
+ x1 = x + w
+ y1 = y + l
+
+ if d.vary_thickness and d.thickness_variance > 0:
+ v = d.thickness / 100 * d.thickness_variance
+ z = uniform(self.top, self.top + v)
+ else:
+ z = self.top
- y += bl + s
+ p = len(verts)
+ verts.extend([(x, y, z), (x1, y, z), (x1, y1, z), (x, y1, z)])
+ faces.append([p + 3, p + 2, p + 1, p])
+ if rotate_uv:
+ uvs.append([(0, 0), (0, 1), (1, 1), (1, 0)])
+ else:
+ uvs.append([(0, 0), (1, 0), (1, 1), (0, 1)])
+ self.add_matid(d, matids)
+
+ def add_face(self, d, verts, faces, matids, uvs, p0, p1, p2, p3):
+ """
+ Adds vertices and faces for a place, clip to outer boundaries if clip is True
+ :param x: start x position
+ :param y: start y position
+ :param w: width (in x direction)
+ :param l: length (in y direction)
+ """
+
+ if d.vary_thickness and d.thickness_variance > 0:
+ v = d.thickness / 100 * d.thickness_variance
+ z = uniform(self.top, self.top + v)
+ else:
+ z = self.top
+
+ p = len(verts)
+ verts.extend([(v[0], v[1], z) for v in [p0, p1, p2, p3]])
+ faces.append([p + 3, p + 2, p + 1, p])
+ uvs.append([(0, 0), (1, 0), (1, 1), (0, 1)])
+ self.add_matid(d, matids)
+
+ def add_manipulator(self, name, pt1, pt2, pt3):
+ m = self.manipulators.add()
+ m.prop1_name = name
+ m.set_pts([pt1, pt2, pt3])
+
+ def generate_pattern(self, d, verts, faces, matids, uvs):
+ # clear data before refreshing it
+
+ self.uv_factor = 1 / max(self.xmax, self.ymax) # automatically scale to keep within reasonable bounds
+
+ if d.pattern == "boards":
+ self.boards(d, verts, faces, matids, uvs)
+ elif d.pattern == "square_parquet":
+ self.square_parquet(d, verts, faces, matids, uvs)
+ elif d.pattern == "herringbone":
+ self.herringbone(d, verts, faces, matids, uvs)
+ elif d.pattern == "herringbone_parquet":
+ self.herringbone_parquet(d, verts, faces, matids, uvs)
+ elif d.pattern == "regular_tile":
+ self.regular_tile(d, verts, faces, matids, uvs)
+ elif d.pattern == "hopscotch":
+ self.hopscotch(d, verts, faces, matids, uvs)
+ elif d.pattern == "stepping_stone":
+ self.stepping_stone(d, verts, faces, matids, uvs)
+ elif d.pattern == "hexagon":
+ self.hexagon(d, verts, faces, matids, uvs)
+ elif d.pattern == "windmill":
+ self.windmill(d, verts, faces, matids, uvs)
- orient_length = not orient_length
- start_orient_length = not start_orient_length
+def update(self, context):
+ self.update(context)
- x += bl + s
- return verts, faces
+def update_type(self, context):
+ d = self.find_in_selection(context)
-def wood_regular(ow, ol, bw, bl, s_l, s_w,
- is_length_vary, length_vary,
- is_width_vary, width_vary,
- is_offset, offset,
- is_ran_offset, offset_vary,
- max_boards, is_r_h,
- r_h, th):
- verts = []
- faces = []
- x = 0.0
- row = 0
- while x < ow:
+ if d is not None and d.auto_update:
- if is_width_vary:
- v = bw * (width_vary / 100) * 0.499
- bw2 = uniform(bw / 2 - v, bw / 2 + v)
- else:
- bw2 = bw
+ d.auto_update = False
+ # find part index
+ idx = 0
+ for i, part in enumerate(d.parts):
+ if part == self:
+ idx = i
+ break
- x1 = min(x + bw2, ow)
- if is_offset:
- if is_ran_offset:
- v = bl * (offset_vary / 100) * 0.5
- y = -uniform(bl / 2 - v, bl / 2 + v)
+ part = d.parts[idx]
+ 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_floor(part.a0, part.length)
else:
- y = -(row % 2) * bl * (offset / 100)
+ w = w0.curved_floor(part.a0, part.da, part.radius)
+ else:
+ g = FloorGenerator(None)
+ g.add_part(self)
+ w = g.segs[0]
+
+ # w0 - w - w1
+ dp = w.p1 - w.p0
+ if "C_" in self.type:
+ part.radius = 0.5 * dp.length
+ part.da = pi
+ a0 = atan2(dp.y, dp.x) - pi / 2 - a0
else:
- y = 0
+ part.length = dp.length
+ a0 = atan2(dp.y, dp.x) - a0
+
+ if a0 > pi:
+ a0 -= 2 * pi
+ if a0 < -pi:
+ a0 += 2 * pi
+ part.a0 = a0
+
+ if idx + 1 < d.n_parts:
+ # adjust rotation of next part
+ part1 = d.parts[idx + 1]
+ if "C_" in part.type:
+ a0 = part1.a0 - pi / 2
+ else:
+ a0 = part1.a0 + w.straight(1).angle - atan2(dp.y, dp.x)
- row += 1
- counter = 1
+ if a0 > pi:
+ a0 -= 2 * pi
+ if a0 < -pi:
+ a0 += 2 * pi
+ part1.a0 = a0
- while y < ol:
+ d.auto_update = True
- z = th
- if is_r_h:
- v = z * 0.5 * (r_h / 100)
- z = uniform(z / 2 - v, z / 2 + v)
+def update_manipulators(self, context):
+ self.update(context, manipulable_refresh=True)
- bl2 = bl
- if is_length_vary:
- if (counter >= max_boards):
- bl2 = ol
- else:
- v = bl * (length_vary / 100) * 0.5
- bl2 = uniform(bl / 2 - v, bl / 2 + v)
+def update_path(self, context):
+ self.update_path(context)
- y0 = max(0, y)
- y1 = min(y + bl2, ol)
- if y1 > y0:
- p = len(verts)
+class archipack_floor_part(PropertyGroup):
- verts.extend([(x, y0, z), (x1, y0, z), (x1, y1, z), (x, y1, z)])
- faces.append((p, p + 1, p + 2, p + 3))
+ """
+ A single manipulable polyline like segment
+ polyline like segment line or arc based
+ """
+ type = EnumProperty(
+ items=(
+ ('S_SEG', 'Straight', '', 0),
+ ('C_SEG', 'Curved', '', 1),
+ ),
+ default='S_SEG',
+ update=update_type
+ )
+ length = FloatProperty(
+ name="length",
+ min=0.01,
+ default=2.0,
+ update=update
+ )
+ radius = FloatProperty(
+ name="radius",
+ min=0.5,
+ default=0.7,
+ update=update
+ )
+ da = FloatProperty(
+ name="angle",
+ min=-pi,
+ max=pi,
+ default=pi / 2,
+ subtype='ANGLE', unit='ROTATION',
+ update=update
+ )
+ a0 = FloatProperty(
+ name="start angle",
+ min=-2 * pi,
+ max=2 * pi,
+ default=0,
+ subtype='ANGLE', unit='ROTATION',
+ update=update
+ )
+ offset = FloatProperty(
+ name="Offset",
+ description="Add to current segment offset",
+ default=0,
+ unit='LENGTH', subtype='DISTANCE',
+ update=update
+ )
+ manipulators = CollectionProperty(type=archipack_manipulator)
+
+ def find_in_selection(self, context):
+ """
+ find witch selected object this instance belongs to
+ provide support for "copy to selected"
+ """
+ selected = [o for o in context.selected_objects]
+ for o in selected:
+ props = archipack_floor.datablock(o)
+ if props:
+ for part in props.parts:
+ if part == self:
+ return props
+ return None
+
+ def update(self, context, manipulable_refresh=False):
+ props = self.find_in_selection(context)
+ if props is not None:
+ props.update(context, manipulable_refresh)
+
+ def draw(self, context, layout, index):
+ box = layout.box()
+ box.prop(self, "type", text=str(index + 1))
- y += bl2 + s_l
+ if self.type in ['C_SEG']:
+ box.prop(self, "radius")
+ box.prop(self, "da")
+ else:
+ box.prop(self, "length")
+ box.prop(self, "a0")
- counter += 1
- x += bw2 + s_w
+class archipack_floor(ArchipackObject, Manipulable, PropertyGroup):
+ n_parts = IntProperty(
+ name="parts",
+ min=1,
+ default=1, update=update_manipulators
+ )
+ parts = CollectionProperty(type=archipack_floor_part)
+ user_defined_path = StringProperty(
+ name="user defined",
+ update=update_path
+ )
+ user_defined_resolution = IntProperty(
+ name="resolution",
+ min=1,
+ max=128,
+ default=12, update=update_path
+ )
+ closed = BoolProperty(
+ default=True,
+ name="Close",
+ options={'SKIP_SAVE'},
+ update=update_manipulators
+ )
+ # UI layout related
+ parts_expand = BoolProperty(
+ options={'SKIP_SAVE'},
+ default=False
+ )
- return verts, faces
+ pattern = EnumProperty(
+ name='Floor Pattern', items=(("boards", "Boards", ""), ("square_parquet", "Square Parquet", ""),
+ ("herringbone_parquet", "Herringbone Parquet", ""),
+ ("herringbone", "Herringbone", ""), ("regular_tile", "Regular Tile", ""),
+ ("hopscotch", "Hopscotch", ""), ("stepping_stone", "Stepping Stone", ""),
+ ("hexagon", "Hexagon", ""), ("windmill", "Windmill", "")),
+ default="boards", update=update
+ )
+ spacing = FloatProperty(
+ name='Spacing',
+ description='The amount of space between boards or tiles in both directions',
+ unit='LENGTH', subtype='DISTANCE',
+ min=0,
+ default=0.005,
+ precision=2,
+ update=update
+ )
+ thickness = FloatProperty(
+ name='Thickness',
+ description='Thickness',
+ unit='LENGTH', subtype='DISTANCE',
+ min=0.0,
+ default=0.005,
+ precision=2,
+ update=update
+ )
+ vary_thickness = BoolProperty(
+ name='Vary Thickness',
+ description='Vary board thickness?',
+ default=False,
+ update=update
+ )
+ thickness_variance = FloatProperty(
+ name='Thickness Variance',
+ description='How much board thickness can vary by',
+ min=0, max=100,
+ default=25,
+ precision=2,
+ subtype='PERCENTAGE',
+ update=update
+ )
+ board_width = FloatProperty(
+ name='Board Width',
+ description='The width of the boards',
+ unit='LENGTH', subtype='DISTANCE',
+ min=0.02,
+ default=0.2,
+ precision=2,
+ update=update
+ )
+ vary_width = BoolProperty(
+ name='Vary Width',
+ description='Vary board width',
+ default=False,
+ update=update
+ )
+ width_variance = FloatProperty(
+ name='Width Variance',
+ description='How much board width can vary by',
+ subtype='PERCENTAGE',
+ min=1, max=100, default=50,
+ precision=2,
+ update=update
+ )
+ width_spacing = FloatProperty(
+ name='Width Spacing',
+ description='The amount of space between boards in the width direction',
+ unit='LENGTH', subtype='DISTANCE',
+ min=0,
+ default=0.002,
+ precision=2,
+ update=update
+ )
-def tile_grout(ow, ol, depth, th):
- z = min(th - 0.001, max(0.001, th - depth))
- x = ow
- y = ol
+ board_length = FloatProperty(
+ name='Board Length',
+ description='The length of the boards',
+ unit='LENGTH', subtype='DISTANCE',
+ precision=2,
+ min=0.02,
+ default=2,
+ update=update
+ )
+ short_board_length = FloatProperty(
+ name='Board Length',
+ description='The length of the boards',
+ unit='LENGTH', subtype='DISTANCE',
+ precision=2,
+ min=0.02,
+ default=2,
+ update=update
+ )
+ vary_length = BoolProperty(
+ name='Vary Length',
+ description='Vary board length',
+ default=False,
+ update=update
+ )
+ length_variance = FloatProperty(
+ name='Length Variance',
+ description='How much board length can vary by',
+ subtype='PERCENTAGE',
+ min=1, max=100, default=50,
+ precision=2, update=update
+ )
+ max_boards = IntProperty(
+ name='Max Boards',
+ description='Max number of boards in one row',
+ min=1,
+ default=20,
+ update=update
+ )
+ length_spacing = FloatProperty(
+ name='Length Spacing',
+ description='The amount of space between boards in the length direction',
+ unit='LENGTH', subtype='DISTANCE',
+ min=0,
+ default=0.002,
+ precision=2,
+ update=update
+ )
- verts = [(0.0, 0.0, 0.0), (0.0, 0.0, z), (x, 0.0, z), (x, 0.0, 0.0),
- (0.0, y, 0.0), (0.0, y, z), (x, y, z), (x, y, 0.0)]
+ # parquet specific
+ boards_in_group = IntProperty(
+ name='Boards in Group',
+ description='Number of boards in a group',
+ min=1, default=4,
+ update=update
+ )
- faces = [(0, 3, 2, 1), (4, 5, 6, 7), (0, 1, 5, 4),
- (1, 2, 6, 5), (3, 7, 6, 2), (0, 4, 7, 3)]
+ # tile specific
+ tile_width = FloatProperty(
+ name='Tile Width',
+ description='Width of the tiles',
+ unit='LENGTH', subtype='DISTANCE',
+ min=0.002,
+ default=0.2,
+ precision=2,
+ update=update
+ )
+ tile_length = FloatProperty(
+ name='Tile Length',
+ description='Length of the tiles',
+ unit='LENGTH', subtype='DISTANCE',
+ precision=2,
+ min=0.02,
+ default=0.3,
+ update=update
+ )
- return verts, faces
+ # grout
+ add_grout = BoolProperty(
+ name='Add Grout',
+ description='Add grout',
+ default=False,
+ update=update
+ )
+ mortar_depth = FloatProperty(
+ name='Mortar Depth',
+ description='The depth of the mortar from the surface of the tile',
+ unit='LENGTH', subtype='DISTANCE',
+ precision=2,
+ step=0.005,
+ min=0,
+ default=0.001,
+ update=update
+ )
+ # regular tile
+ random_offset = BoolProperty(
+ name='Random Offset',
+ description='Random amount of offset for each row of tiles',
+ update=update, default=False
+ )
+ offset = FloatProperty(
+ name='Offset',
+ description='How much to offset each row of tiles',
+ min=0, max=100, default=0,
+ precision=2,
+ update=update
+ )
+ offset_variance = FloatProperty(
+ name='Offset Variance',
+ description='How much to vary the offset each row of tiles',
+ min=0.001, max=100, default=50,
+ precision=2,
+ update=update
+ )
-def update(self, context):
- self.update(context)
+ # UV stuff
+ random_uvs = BoolProperty(
+ name='Random UV\'s', update=update, default=True, description='Random UV positions for the faces'
+ )
+ # bevel
+ bevel = BoolProperty(
+ name='Bevel', update=update, default=False, description='Bevel upper faces'
+ )
+ bevel_amount = FloatProperty(
+ name='Bevel Amount',
+ description='Bevel amount',
+ unit='LENGTH', subtype='DISTANCE',
+ min=0.0001, default=0.001,
+ precision=2, step=0.05,
+ update=update
+ )
-class archipack_floor(ArchipackObject, Manipulable, PropertyGroup):
- tile_types = EnumProperty(
- items=(
- ("1", "Tiles", ""),
- ("2", "Large + Small", ""),
- ("3", "Large + Many Small", ""),
- ("4", "Hexagonal", ""),
- ("21", "Planks", ""),
- ("22", "Parquet", ""),
- ("23", "Herringbone Parquet", ""),
- ("24", "Herringbone", "")
- ),
- default="1",
- description="Tile Type",
- update=update,
- name="")
- b_length_s = FloatProperty(
- name="Board Length",
- min=0.01,
- default=2.0,
- unit='LENGTH', subtype='DISTANCE',
- description="Board Length",
- update=update)
- hb_direction = EnumProperty(
- items=(
- ("1", "Forwards (+y)", ""),
- ("2", "Backwards (-y)", ""),
- ("3", "Right (+x)", ""),
- ("4", "Left (-x)", "")
- ),
- name="Direction",
- description="Herringbone Direction",
- update=update)
- thickness = FloatProperty(
- name="Floor Thickness",
- min=0.01,
- default=0.1,
- unit='LENGTH', subtype='DISTANCE',
- description="Thickness Of Flooring",
- update=update)
- num_boards = IntProperty(
- name="# Of Boards",
- min=2,
- max=6,
- default=4,
- description="Number Of Boards In Square",
- update=update)
- space_l = FloatProperty(
- name="Length Spacing",
- min=0.001,
- default=0.005,
- step=0.01,
- unit='LENGTH', subtype='DISTANCE',
- description="Space Between Boards Length Ways",
- update=update)
- space_w = FloatProperty(
- name="Width Spacing",
- min=0.001,
- default=0.005,
- step=0.01,
- unit='LENGTH', subtype='DISTANCE',
- description="Space Between Boards Width Ways",
- update=update)
- spacing = FloatProperty(
- name="Spacing",
- min=0.001,
- default=0.005,
- step=0.01,
- unit='LENGTH', subtype='DISTANCE',
- description="Space Between Tiles/Boards",
- update=update)
- is_bevel = BoolProperty(
- name="Bevel?",
- default=False,
- update=update)
- bevel_res = IntProperty(
- name="Bevel Resolution",
- min=1,
- max=10,
- default=1,
- update=update)
- bevel_amo = FloatProperty(
- name="Bevel Amount",
- min=0.001,
- default=0.0015,
- step=0.01,
- unit='LENGTH', subtype='DISTANCE',
- description="Bevel Amount",
- update=update)
- is_ran_thickness = BoolProperty(
- name="Random Thickness?",
- default=False,
- update=update)
- ran_thickness = FloatProperty(
- name="Thickness Variance",
- min=0.1,
- max=100.0,
- default=50.0,
- subtype="PERCENTAGE",
- update=update)
- is_floor_bottom = BoolProperty(
- name="Floor bottom",
- default=True,
- update=update)
- t_width = FloatProperty(
- name="Tile Width",
- min=0.01,
- default=0.3,
- unit='LENGTH', subtype='DISTANCE',
- description="Tile Width",
- update=update)
- t_length = FloatProperty(
- name="Tile Length",
- min=0.01,
- default=0.3,
- unit='LENGTH', subtype='DISTANCE',
- description="Tile Length",
- update=update)
- is_grout = BoolProperty(
- name="Grout",
- default=False,
- description="Enable grout",
- update=update)
- grout_depth = FloatProperty(
- name="Grout Depth",
- min=0.001,
- default=0.005,
- step=0.01,
- unit='LENGTH', subtype='DISTANCE',
- description="Grout Depth",
- update=update)
- is_offset = BoolProperty(
- name="Offset ?",
- default=False,
- description="Offset Rows",
- update=update)
- offset = FloatProperty(
- name="Offset",
- min=0.001,
- max=100.0,
- default=50.0,
- subtype="PERCENTAGE",
- description="Tile Offset Amount",
- update=update)
- is_random_offset = BoolProperty(
- name="Random Offset?",
- default=False,
- description="Offset Tile Rows Randomly",
- update=update)
- offset_vary = FloatProperty(
- name="Offset Variance",
- min=0.001,
- max=100.0,
- default=50.0,
- subtype="PERCENTAGE",
- description="Offset Variance",
- update=update)
- t_width_s = FloatProperty(
- name="Small Tile Width",
- min=0.02,
- default=0.2,
- unit='LENGTH', subtype='DISTANCE',
- description="Small Tile Width",
- update=update)
- over_width = FloatProperty(
- name="Overall Width",
- min=0.02,
- default=4,
- unit='LENGTH', subtype='DISTANCE',
- description="Overall Width",
- update=update)
- over_length = FloatProperty(
- name="Overall Length",
- min=0.02,
- default=4,
- unit='LENGTH', subtype='DISTANCE',
- description="Overall Length",
- update=update)
- b_width = FloatProperty(
- name="Board Width",
- min=0.01,
- default=0.2,
- unit='LENGTH', subtype='DISTANCE',
- description="Board Width",
- update=update)
- b_length = FloatProperty(
- name="Board Length",
- min=0.01,
- default=0.8,
- unit='LENGTH', subtype='DISTANCE',
- description="Board Length",
- update=update)
- is_length_vary = BoolProperty(
- name="Vary Length?",
- default=False,
- description="Vary Lengths?",
- update=update)
- length_vary = FloatProperty(
- name="Length Variance",
- min=1.00,
- max=100.0,
- default=50.0,
- subtype="PERCENTAGE",
- description="Length Variance",
- update=update)
- max_boards = IntProperty(
- name="Max # Of Boards",
- min=2,
- default=2,
- description="Maximum Number Of Boards Possible In One Length",
- update=update)
- is_width_vary = BoolProperty(
- name="Vary Width?",
- default=False,
- description="Vary Widths?",
- update=update)
- width_vary = FloatProperty(
- name="Width Variance",
- min=1.00,
- max=100.0,
- default=50.0,
- subtype="PERCENTAGE",
- description="Width Variance",
- update=update)
- is_mat_vary = BoolProperty(
+ vary_materials = BoolProperty(
name="Vary Material?",
default=False,
description="Vary Material indexes",
update=update)
- mat_vary = IntProperty(
+ matid = IntProperty(
name="#variations",
min=1,
max=10,
@@ -841,127 +1236,373 @@ class archipack_floor(ArchipackObject, Manipulable, PropertyGroup):
description="Material index maxi",
update=update)
auto_update = BoolProperty(
- options={'SKIP_SAVE'},
- default=True,
- update=update
+ options={'SKIP_SAVE'},
+ default=True,
+ update=update_manipulators
+ )
+ z = FloatProperty(
+ name="dumb z",
+ description="Dumb z for manipulator placeholder",
+ default=0.01,
+ options={'SKIP_SAVE'}
)
+ def get_generator(self):
+ g = FloorGenerator(self.parts)
+ for part in self.parts:
+ # type, radius, da, length
+ g.add_part(part)
+
+ g.set_offset()
+
+ g.close(self.closed)
+ g.locate_manipulators()
+ return g
+
+ def update_parts(self, o):
+
+ for i in range(len(self.parts), self.n_parts, -1):
+ self.parts.remove(i - 1)
+
+ # add rows
+ for i in range(len(self.parts), self.n_parts):
+ self.parts.add()
+
+ self.setup_manipulators()
+
+ g = self.get_generator()
+
+ return g
+
+ @staticmethod
+ def create_uv_seams(bm):
+ handled = set()
+ for edge in bm.edges:
+ if edge.verts[0].co.z == 0 and edge.verts[1].co.z == 0: # bottom
+ # make sure both vertices on the edge haven't been handled, this forces one edge to not be made a seam
+ # leaving the bottom face still attached
+ if not (edge.verts[0].index in handled and edge.verts[1].index in handled):
+ edge.seam = True
+ handled.add(edge.verts[0].index)
+ handled.add(edge.verts[1].index)
+ elif edge.verts[0].co.z != edge.verts[1].co.z: # not horizontal, so they are vertical seams
+ edge.seam = True
+
+ 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 interpolate_bezier(self, pts, wM, p0, p1, resolution):
+ # straight segment, worth testing here
+ # since this can lower points count by a resolution factor
+ # use normalized to handle non linear t
+ 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 from_spline(self, context, 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)
+
+ pt = wM.inverted() * pts[0]
+
+ # pretranslate
+ o = self.find_in_selection(context, self.auto_update)
+ o.matrix_world = wM * Matrix([
+ [1, 0, 0, pt.x],
+ [0, 1, 0, pt.y],
+ [0, 0, 1, pt.z],
+ [0, 0, 0, 1]
+ ])
+ self.from_points(pts)
+
+ def from_points(self, pts):
+
+ if self.is_cw(pts):
+ pts = list(reversed(pts))
+
+ self.auto_update = False
+
+ self.n_parts = len(pts) - 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):
+ break
+ p = self.parts[i]
+ p.length = dp.to_2d().length
+ p.dz = dp.z
+ p.a0 = da
+ a0 += da
+ p0 = p1
+
+ self.closed = True
+ self.auto_update = True
+
+ def update_path(self, context):
+ user_def_path = context.scene.objects.get(self.user_defined_path)
+ if user_def_path is not None and user_def_path.type == 'CURVE':
+ self.from_spline(
+ context,
+ user_def_path.matrix_world,
+ self.user_defined_resolution,
+ user_def_path.data.splines[0])
+
+ def add_manipulator(self, name, pt1, pt2, pt3):
+ m = self.manipulators.add()
+ m.prop1_name = name
+ m.set_pts([pt1, pt2, pt3])
+
+ def update_manipulators(self):
+ self.manipulators.clear() # clear every time, add new ones
+ self.add_manipulator("length", (0, 0, 0), (0, self.length, 0), (-0.4, 0, 0))
+ self.add_manipulator("width", (0, 0, 0), (self.width, 0, 0), (0.4, 0, 0))
+
+ z = self.thickness
+
+ if self.pattern == "boards":
+ self.add_manipulator("board_length", (0, 0, z), (0, self.board_length, z), (0.1, 0, z))
+ self.add_manipulator("board_width", (0, 0, z), (self.board_width, 0, z), (-0.2, 0, z))
+ elif self.pattern == "square_parquet":
+ self.add_manipulator("short_board_length", (0, 0, z), (0, self.short_board_length, z), (-0.2, 0, z))
+ elif self.pattern in ("herringbone", "herringbone_parquet"):
+ dia = self.short_board_length * cos(radians(45))
+ dia2 = self.board_width * cos(radians(45))
+ self.add_manipulator("short_board_length", (0, 0, z), (dia, dia, z), (0, 0, z))
+ self.add_manipulator("board_width", (dia, 0, z), (dia - dia2, dia2, z), (0, 0, z))
+ else:
+ tl = self.tile_length
+ tw = self.tile_width
+
+ if self.pattern in ("regular_tile", "hopscotch", "stepping_stone"):
+ self.add_manipulator("tile_width", (0, tl, z), (tw, tl, z), (0, 0, z))
+ self.add_manipulator("tile_length", (0, 0, z), (0, tl, z), (0, 0, z))
+ elif self.pattern == "hexagon":
+ self.add_manipulator("tile_width", (tw / 2 + self.spacing, 0, z), (tw * 1.5 + self.spacing, 0, z),
+ (0, 0, 0))
+ elif self.pattern == "windmill":
+ self.add_manipulator("tile_width", (0, 0, z), (tw, 0, 0), (0, 0, z))
+ self.add_manipulator("tile_length", (0, tl / 2 + self.spacing, z), (0, tl * 1.5 + self.spacing, z),
+ (0, 0, z))
+
def setup_manipulators(self):
- if len(self.manipulators) < 1:
- # add manipulator for x property
- s = self.manipulators.add()
- s.prop1_name = "over_width"
- # s.prop2_name = "x"
- s.type_key = 'SIZE'
- # add manipulator for y property
+ if len(self.manipulators) < 1:
s = self.manipulators.add()
- s.prop1_name = "over_length"
- # s.prop2_name = "y"
- s.type_key = 'SIZE'
-
- def update(self, context):
+ s.type_key = "SIZE"
+ s.prop1_name = "z"
+ s.normal = Vector((0, 1, 0))
+
+ for i in range(self.n_parts):
+ p = self.parts[i]
+ n_manips = len(p.manipulators)
+ if n_manips < 1:
+ s = p.manipulators.add()
+ s.type_key = "ANGLE"
+ s.prop1_name = "a0"
+ p.manipulators[0].type_key = 'ANGLE'
+ 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)
+
+ self.parts[-1].manipulators[0].type_key = 'DUMB_ANGLE'
+
+ def update(self, context, manipulable_refresh=False):
o = self.find_in_selection(context, self.auto_update)
if o is None:
return
- self.setup_manipulators()
+ # clean up manipulators before any data model change
+ if manipulable_refresh:
+ self.manipulable_disable(context)
- verts, faces = create_flooring(self.tile_types, self.over_width,
- self.over_length, self.b_width, self.b_length, self.b_length_s,
- self.is_length_vary, self.length_vary, self.num_boards, self.space_l,
- self.space_w, self.spacing, self.t_width, self.t_length, self.is_offset,
- self.offset, self.is_random_offset, self.offset_vary, self.t_width_s,
- self.is_width_vary, self.width_vary, self.max_boards, self.is_ran_thickness,
- self.ran_thickness, self.thickness, self.hb_direction)
-
- if self.is_mat_vary:
- # hexagon made of 2 faces
- if self.tile_types == '4':
- matids = []
- for i in range(int(len(faces) / 2)):
- id = randint(1, self.mat_vary)
- matids.extend([id, id])
- else:
- matids = [randint(1, self.mat_vary) for i in faces]
- else:
- matids = [1 for i in faces]
-
- uvs = [[(0, 0), (0, 1), (1, 1), (1, 0)] for i in faces]
-
- bmed.buildmesh(context,
- o,
- verts,
- faces,
- matids=matids,
- uvs=uvs,
- weld=False,
- auto_smooth=False)
-
- # cut hexa and herringbone wood
- # disable when boolean modifier is found
- enable_bissect = True
- for m in o.modifiers:
- if m.type == 'BOOLEAN':
- enable_bissect = False
-
- if enable_bissect and self.tile_types in ('4', '23', '24'):
- bmed.bissect(context, o, Vector((0, 0, 0)), Vector((0, -1, 0)))
- # Up
- bmed.bissect(context, o, Vector((0, self.over_length, 0)), Vector((0, 1, 0)))
- # left
- bmed.bissect(context, o, Vector((0, 0, 0)), Vector((-1, 0, 0)))
- # right
- bmed.bissect(context, o, Vector((self.over_width, 0, 0)), Vector((1, 0, 0)))
-
- if self.is_bevel:
- bevel = self.bevel_amo
- else:
- bevel = 0
+ g = self.update_parts(o)
- if self.is_grout:
- th = min(self.grout_depth + bevel, self.thickness - 0.001)
- bottom = th
- else:
- th = self.thickness
- bottom = 0
+ g.cut(context, o)
+ g.floor(context, o, self)
- bmed.solidify(context,
- o,
- th,
- floor_bottom=(
- self.is_floor_bottom and
- self.is_ran_thickness and
- self.tile_types in ('21')
- ),
- altitude=bottom)
-
- # bevel mesh
- if self.is_bevel:
- bmed.bevel(context, o, self.bevel_amo, segments=self.bevel_res)
-
- # create grout
- if self.is_grout:
- verts, faces = tile_grout(self.over_width, self.over_length, self.grout_depth, self.thickness)
- matids = [0 for i in faces]
- uvs = [[(0, 0), (0, 1), (1, 1), (1, 0)] for i in faces]
- bmed.addmesh(context,
- o,
- verts,
- faces,
- matids=matids,
- uvs=uvs,
- weld=False,
- auto_smooth=False)
-
- x, y = self.over_width, self.over_length
- self.manipulators[0].set_pts([(0, 0, 0), (x, 0, 0), (1, 0, 0)])
- self.manipulators[1].set_pts([(0, 0, 0), (0, y, 0), (-1, 0, 0)])
+ # enable manipulators rebuild
+ if manipulable_refresh:
+ self.manipulable_refresh = True
+ # restore context
self.restore_context(context)
+ def manipulable_setup(self, context):
+ """
+ NOTE:
+ this one assume context.active_object is the instance this
+ data belongs to, failing to do so will result in wrong
+ manipulators set on active object
+ """
+ self.manipulable_disable(context)
+
+ o = context.active_object
+
+ self.setup_manipulators()
+
+ for i, part in enumerate(self.parts):
+ if i >= self.n_parts:
+ break
+
+ 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))
+
+ # snap point
+ self.manip_stack.append(part.manipulators[2].setup(context, o, self))
+ # index
+ self.manip_stack.append(part.manipulators[3].setup(context, o, self))
+
+ for m in self.manipulators:
+ self.manip_stack.append(m.setup(context, o, self))
+
+ def manipulable_invoke(self, context):
+ """
+ call this in operator invoke()
+ """
+ # print("manipulable_invoke")
+ if self.manipulate_mode:
+ self.manipulable_disable(context)
+ return False
+
+ self.manipulable_setup(context)
+ self.manipulate_mode = True
+
+ self._manipulable_invoke(context)
+
+ return True
+
+
+def update_hole(self, context):
+ self.update(context, update_parent=True)
+
+
+def update_operation(self, context):
+ self.reverse(context, make_ccw=(self.operation == 'INTERSECTION'))
+
+
+class archipack_floor_cutter_segment(ArchipackCutterPart, PropertyGroup):
+ manipulators = CollectionProperty(type=archipack_manipulator)
+ type = EnumProperty(
+ name="Type",
+ items=(
+ ('DEFAULT', 'Side', 'Side with rake', 0),
+ ('BOTTOM', 'Bottom', 'Bottom with gutter', 1),
+ ('LINK', 'Side link', 'Side witout decoration', 2),
+ ('AXIS', 'Top', 'Top part with hip and beam', 3)
+ # ('LINK_VALLEY', 'Side valley', 'Side with valley', 3),
+ # ('LINK_HIP', 'Side hip', 'Side with hip', 4)
+ ),
+ default='DEFAULT',
+ update=update_hole
+ )
+
+ def find_in_selection(self, context):
+ selected = [o for o in context.selected_objects]
+ for o in selected:
+ d = archipack_floor_cutter.datablock(o)
+ if d:
+ for part in d.parts:
+ if part == self:
+ return d
+ return None
+
+ def draw(self, layout, context, index):
+ box = layout.box()
+ box.label(text="Part:" + str(index + 1))
+ # box.prop(self, "type", text=str(index + 1))
+ box.prop(self, "length")
+ box.prop(self, "a0")
+
+
+class archipack_floor_cutter(ArchipackCutter, ArchipackObject, Manipulable, PropertyGroup):
+ parts = CollectionProperty(type=archipack_floor_cutter_segment)
+
+ def update_points(self, context, o, pts, update_parent=False):
+ """
+ Create boundary from roof
+ """
+ self.auto_update = False
+ self.from_points(pts)
+ self.auto_update = True
+ if update_parent:
+ self.update_parent(context, o)
+
+ def update_parent(self, context, o):
+
+ d = archipack_floor.datablock(o.parent)
+ if d is not None:
+ o.parent.select = True
+ context.scene.objects.active = o.parent
+ d.update(context)
+ o.parent.select = False
+ context.scene.objects.active = o
+
+
+# ------------------------------------------------------------------
+# Define panel class to show object parameters in ui panel (N)
+# ------------------------------------------------------------------
+
class ARCHIPACK_PT_floor(Panel):
bl_idname = "ARCHIPACK_PT_floor"
@@ -980,13 +1621,12 @@ class ARCHIPACK_PT_floor(Panel):
if not archipack_floor.filter(o):
return
layout = self.layout
-
+ scene = context.scene
# retrieve datablock of your object
props = archipack_floor.datablock(o)
-
- # Manipulate mode operator
- layout.operator('archipack.floor_manipulate', icon='HAND')
-
+ # manipulate
+ layout.operator("archipack.floor_manipulate", icon="HAND")
+ layout.separator()
box = layout.box()
row = box.row(align=True)
@@ -1000,124 +1640,170 @@ class ARCHIPACK_PT_floor(Panel):
text="",
icon='ZOOMOUT').remove_active = True
- layout.prop(props, "tile_types", icon="OBJECT_DATA")
+ box = layout.box()
+ box.operator('archipack.floor_cutter').parent = o.name
- layout.separator()
+ box = layout.box()
+ box.label(text="From curve")
+ box.prop_search(props, "user_defined_path", scene, "objects", text="", icon='OUTLINER_OB_CURVE')
+ if props.user_defined_path != "":
+ box.prop(props, 'user_defined_resolution')
- layout.prop(props, "over_width")
- layout.prop(props, "over_length")
+ box = layout.box()
+ row = box.row()
+ if props.parts_expand:
+ row.prop(props, 'parts_expand', icon="TRIA_DOWN", icon_only=True, text="Parts", emboss=False)
+ box.prop(props, 'n_parts')
+ # box.prop(prop, 'closed')
+ for i, part in enumerate(props.parts):
+ part.draw(context, layout, i)
+ else:
+ row.prop(props, 'parts_expand', icon="TRIA_RIGHT", icon_only=True, text="Parts", emboss=False)
layout.separator()
+ box = layout.box()
+ box.prop(props, 'pattern', text="")
+ # thickness
+ box.separator()
+ box.prop(props, 'thickness')
+ box.prop(props, 'vary_thickness', icon='RNDCURVE')
+ if props.vary_thickness:
+ box.prop(props, 'thickness_variance')
+
+ box.separator()
+ if props.pattern == 'boards':
+ box.prop(props, 'board_length')
+ box.prop(props, 'vary_length', icon='RNDCURVE')
+ if props.vary_length:
+ box.prop(props, 'length_variance')
+ box.prop(props, 'max_boards')
+ box.separator()
+
+ # width
+ box.prop(props, 'board_width')
+ # vary width
+ box.prop(props, 'vary_width', icon='RNDCURVE')
+ if props.vary_width:
+ box.prop(props, 'width_variance')
+ box.separator()
+ box.prop(props, 'length_spacing')
+ box.prop(props, 'width_spacing')
+
+ elif props.pattern in {'square_parquet', 'herringbone_parquet', 'herringbone'}:
+ box.prop(props, 'short_board_length')
+
+ if props.pattern != "square_parquet":
+ box.prop(props, "board_width")
+ box.prop(props, "spacing")
+
+ if props.pattern == 'square_parquet':
+ box.prop(props, 'boards_in_group')
+ elif props.pattern in {'regular_tile', 'hopscotch', 'stepping_stone', 'hexagon', 'windmill'}:
+ # width and length and mortar
+ if props.pattern != "hexagon":
+ box.prop(props, "tile_length")
+ box.prop(props, "tile_width")
+ box.prop(props, "spacing")
+
+ if props.pattern in {"regular_tile", "boards"}:
+ box.separator()
+ box.prop(props, "random_offset", icon="RNDCURVE")
+ if props.random_offset:
+ box.prop(props, "offset_variance")
+ else:
+ box.prop(props, "offset")
- # width and lengths
- layout.prop(props, "thickness")
+ # grout
+ box.separator()
+ box.prop(props, 'add_grout', icon='MESH_GRID')
+ if props.add_grout:
+ box.prop(props, 'mortar_depth')
- type = int(props.tile_types)
+ # bevel
+ box.separator()
+ box.prop(props, 'bevel', icon='MOD_BEVEL')
+ if props.bevel:
+ box.prop(props, 'bevel_amount')
- if type > 20:
- # Wood types
- layout.prop(props, "b_width")
- else:
- # Tiles types
- if type != 4:
- # Not hexagonal
- layout.prop(props, "t_width")
- layout.prop(props, "t_length")
- else:
- layout.prop(props, "t_width_s")
-
- # Herringbone
- if type in (23, 24):
- layout.prop(props, "b_length_s")
- layout.prop(props, "hb_direction")
-
- # Parquet
- if type == 22:
- layout.prop(props, "num_boards")
-
- # Planks
- if type == 21:
- layout.prop(props, "b_length")
- layout.prop(props, "space_w")
- layout.prop(props, "space_l")
-
- layout.separator()
- layout.prop(props, "is_length_vary", icon="NLA")
- if props.is_length_vary:
- layout.prop(props, "length_vary")
- layout.prop(props, "max_boards")
-
- layout.separator()
- layout.prop(props, "is_width_vary", icon="UV_ISLANDSEL")
- if props.is_width_vary:
- layout.prop(props, "width_vary")
-
- layout.separator()
- layout.prop(props, "is_ran_thickness", icon="RNDCURVE")
- if props.is_ran_thickness:
- layout.prop(props, "ran_thickness")
- layout.prop(props, "is_floor_bottom", icon="MOVE_DOWN_VEC")
- else:
- layout.prop(props, "spacing")
-
- # Planks and tiles
- if type in (1, 21):
- layout.separator()
- layout.prop(props, "is_offset", icon="OOPS")
- if props.is_offset:
- layout.prop(props, "is_random_offset", icon="NLA")
- if not props.is_random_offset:
- layout.prop(props, "offset")
- else:
- layout.prop(props, "offset_vary")
+ box.separator()
+ box.prop(props, "vary_materials", icon="MATERIAL")
+ if props.vary_materials:
+ box.prop(props, "matid")
- # bevel
- layout.separator()
- layout.prop(props, "is_bevel", icon="MOD_BEVEL")
- if props.is_bevel:
- layout.prop(props, "bevel_res", icon="OUTLINER_DATA_CURVE")
- layout.prop(props, "bevel_amo")
- # Grout
- layout.separator()
- layout.prop(props, "is_grout", icon="OBJECT_DATA")
- if props.is_grout:
- layout.prop(props, "grout_depth")
+class ARCHIPACK_PT_floor_cutter(Panel):
+ bl_idname = "ARCHIPACK_PT_floor_cutter"
+ bl_label = "Floor Cutter"
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'UI'
+ bl_category = 'ArchiPack'
- layout.separator()
- layout.prop(props, "is_mat_vary", icon="MATERIAL")
- if props.is_mat_vary:
- layout.prop(props, "mat_vary")
+ @classmethod
+ def poll(cls, context):
+ return archipack_floor_cutter.filter(context.active_object)
+
+ def draw(self, context):
+ prop = archipack_floor_cutter.datablock(context.active_object)
+ if prop is None:
+ return
+ layout = self.layout
+ scene = context.scene
+ box = layout.box()
+ box.operator('archipack.floor_cutter_manipulate', icon='HAND')
+ box.prop(prop, 'operation', text="")
+ box = layout.box()
+ box.label(text="From curve")
+ box.prop_search(prop, "user_defined_path", scene, "objects", text="", icon='OUTLINER_OB_CURVE')
+ if prop.user_defined_path != "":
+ box.prop(prop, 'user_defined_resolution')
+ # box.prop(prop, 'x_offset')
+ # box.prop(prop, 'angle_limit')
+ """
+ box.prop_search(prop, "boundary", scene, "objects", text="", icon='OUTLINER_OB_CURVE')
+ """
+ prop.draw(layout, context)
+
+
+# ------------------------------------------------------------------
+# Define operator class to create object
+# ------------------------------------------------------------------
class ARCHIPACK_OT_floor(ArchipackCreateTool, Operator):
bl_idname = "archipack.floor"
bl_label = "Floor"
- bl_description = "Create Floor"
+ bl_description = "Floor"
bl_category = 'Archipack'
bl_options = {'REGISTER', 'UNDO'}
def create(self, context):
-
- # Create an empty mesh datablock
+ """
+ expose only basic params in operator
+ use object property for other params
+ """
m = bpy.data.meshes.new("Floor")
-
- # Create an object using the mesh datablock
o = bpy.data.objects.new("Floor", m)
-
- # Add your properties on mesh datablock
d = m.archipack_floor.add()
-
- # Link object into scene
+ # make manipulators selectable
+ d.manipulable_selectable = True
+ angle_90 = pi / 2
+ x, y, = 4, 4
+ p = d.parts.add()
+ p.a0 = - angle_90
+ p.length = y
+ p = d.parts.add()
+ p.a0 = angle_90
+ p.length = x
+ p = d.parts.add()
+ p.a0 = angle_90
+ p.length = y
+ p = d.parts.add()
+ p.a0 = angle_90
+ p.length = x
+ d.n_parts = 4
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
@@ -1125,11 +1811,174 @@ class ARCHIPACK_OT_floor(ArchipackCreateTool, Operator):
if context.mode == "OBJECT":
bpy.ops.object.select_all(action="DESELECT")
o = self.create(context)
- o.location = bpy.context.scene.cursor_location
+ o.location = context.scene.cursor_location
+ # activate manipulators at creation time
o.select = True
context.scene.objects.active = o
+ bpy.ops.archipack.floor_manipulate()
+ return {'FINISHED'}
+ else:
+ self.report({'WARNING'}, "Option only valid in Object mode")
+ return {'CANCELLED'}
+
+
+class ARCHIPACK_OT_floor_from_curve(ArchipackCreateTool, Operator):
+ bl_idname = "archipack.floor_from_curve"
+ bl_label = "Floor curve"
+ bl_description = "Create a floor from a curve"
+ bl_category = 'Archipack'
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ return context.active_object is not None and context.active_object.type == 'CURVE'
+ # -----------------------------------------------------
+ # Draw (create UI interface)
+ # -----------------------------------------------------
+ # noinspection PyUnusedLocal
+
+ 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):
+ curve = context.active_object
+ bpy.ops.archipack.floor(auto_manipulate=self.auto_manipulate, filepath=self.filepath)
+ o = context.active_object
+ d = archipack_floor.datablock(o)
+ d.user_defined_path = curve.name
+ return o
+
+ # -----------------------------------------------------
+ # Execute
+ # -----------------------------------------------------
+ def execute(self, context):
+ if context.mode == "OBJECT":
+ bpy.ops.object.select_all(action="DESELECT")
+ self.create(context)
+ return {'FINISHED'}
+ else:
+ self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
+ return {'CANCELLED'}
- # Start manipulate mode
+
+class ARCHIPACK_OT_floor_from_wall(ArchipackCreateTool, Operator):
+ bl_idname = "archipack.floor_from_wall"
+ bl_label = "->Floor"
+ bl_description = "Create a floor from a wall"
+ bl_category = 'Archipack'
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ o = context.active_object
+ return o is not None and o.data is not None and 'archipack_wall2' in o.data
+
+ def create(self, context):
+ wall = context.active_object
+ wd = wall.data.archipack_wall2[0]
+ bpy.ops.archipack.floor(auto_manipulate=False, filepath=self.filepath)
+ o = context.scene.objects.active
+ d = archipack_floor.datablock(o)
+ d.auto_update = False
+ d.closed = True
+ d.parts.clear()
+ d.n_parts = wd.n_parts + 1
+ for part in wd.parts:
+ p = d.parts.add()
+ if "S_" in part.type:
+ p.type = "S_SEG"
+ else:
+ p.type = "C_SEG"
+ p.length = part.length
+ p.radius = part.radius
+ p.da = part.da
+ p.a0 = part.a0
+ d.auto_update = True
+ # pretranslate
+ o.matrix_world = wall.matrix_world.copy()
+ return o
+
+ # -----------------------------------------------------
+ # Execute
+ # -----------------------------------------------------
+ def execute(self, context):
+ if context.mode == "OBJECT":
+ bpy.ops.object.select_all(action="DESELECT")
+ o = self.create(context)
+ o.select = True
+ context.scene.objects.active = o
+ if self.auto_manipulate:
+ bpy.ops.archipack.floor_manipulate('INVOKE_DEFAULT')
+ return {'FINISHED'}
+ else:
+ self.report({'WARNING'}, "Archipack: Option only valid in Object mode")
+ return {'CANCELLED'}
+
+
+class ARCHIPACK_OT_floor_cutter(ArchipackCreateTool, Operator):
+ bl_idname = "archipack.floor_cutter"
+ bl_label = "Floor Cutter"
+ bl_description = "Floor Cutter"
+ bl_category = 'Archipack'
+ bl_options = {'REGISTER', 'UNDO'}
+
+ parent = StringProperty("")
+
+ def create(self, context):
+ m = bpy.data.meshes.new("Floor Cutter")
+ o = bpy.data.objects.new("Floor Cutter", m)
+ d = m.archipack_floor_cutter.add()
+ parent = context.scene.objects.get(self.parent)
+ if parent is not None:
+ o.parent = parent
+ bbox = parent.bound_box
+ angle_90 = pi / 2
+ x0, y0, z = bbox[0]
+ x1, y1, z = bbox[6]
+ x = 0.2 * (x1 - x0)
+ y = 0.2 * (y1 - y0)
+ o.matrix_world = parent.matrix_world * Matrix([
+ [1, 0, 0, -3 * x],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1]
+ ])
+ p = d.parts.add()
+ p.a0 = - angle_90
+ p.length = y
+ p = d.parts.add()
+ p.a0 = angle_90
+ p.length = x
+ p = d.parts.add()
+ p.a0 = angle_90
+ p.length = y
+ d.n_parts = 3
+ # d.close = True
+ pd = archipack_floor.datablock(parent)
+ pd.boundary = o.name
+ else:
+ o.location = context.scene.cursor_location
+ # make manipulators selectable
+ d.manipulable_selectable = True
+ context.scene.objects.link(o)
+ o.select = True
+ context.scene.objects.active = o
+ # self.add_material(o)
+ self.load_preset(d)
+ update_operation(d, context)
+ return o
+
+ # -----------------------------------------------------
+ # Execute
+ # -----------------------------------------------------
+ def execute(self, context):
+ if context.mode == "OBJECT":
+ bpy.ops.object.select_all(action="DESELECT")
+ o = self.create(context)
+ o.select = True
+ context.scene.objects.active = o
self.manipulate()
return {'FINISHED'}
else:
@@ -1137,8 +1986,13 @@ class ARCHIPACK_OT_floor(ArchipackCreateTool, Operator):
return {'CANCELLED'}
+# ------------------------------------------------------------------
+# Define operator class to manipulate object
+# ------------------------------------------------------------------
+
+
class ARCHIPACK_OT_floor_preset_menu(PresetMenuOperator, Operator):
- bl_description = "Show Floor Presets"
+ bl_description = "Show Floor presets"
bl_idname = "archipack.floor_preset_menu"
bl_label = "Floor preset"
preset_subdir = "archipack_floor"
@@ -1152,7 +2006,7 @@ class ARCHIPACK_OT_floor_preset(ArchipackPreset, Operator):
@property
def blacklist(self):
- return ['manipulators', 'over_length', 'over_width']
+ return ['manipulators', 'parts', 'n_parts', 'user_defined_path', 'user_defined_resolution']
class ARCHIPACK_OT_floor_manipulate(Operator):
@@ -1171,7 +2025,31 @@ class ARCHIPACK_OT_floor_manipulate(Operator):
return {'FINISHED'}
+class ARCHIPACK_OT_floor_cutter_manipulate(Operator):
+ bl_idname = "archipack.floor_cutter_manipulate"
+ bl_label = "Manipulate"
+ bl_description = "Manipulate"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(self, context):
+ return archipack_floor_cutter.filter(context.active_object)
+
+ def invoke(self, context, event):
+ d = archipack_floor_cutter.datablock(context.active_object)
+ d.manipulable_invoke(context)
+ return {'FINISHED'}
+
+
def register():
+ bpy.utils.register_class(archipack_floor_cutter_segment)
+ bpy.utils.register_class(archipack_floor_cutter)
+ Mesh.archipack_floor_cutter = CollectionProperty(type=archipack_floor_cutter)
+ bpy.utils.register_class(ARCHIPACK_OT_floor_cutter)
+ bpy.utils.register_class(ARCHIPACK_PT_floor_cutter)
+ bpy.utils.register_class(ARCHIPACK_OT_floor_cutter_manipulate)
+
+ bpy.utils.register_class(archipack_floor_part)
bpy.utils.register_class(archipack_floor)
Mesh.archipack_floor = CollectionProperty(type=archipack_floor)
bpy.utils.register_class(ARCHIPACK_PT_floor)
@@ -1179,9 +2057,19 @@ def register():
bpy.utils.register_class(ARCHIPACK_OT_floor_preset_menu)
bpy.utils.register_class(ARCHIPACK_OT_floor_preset)
bpy.utils.register_class(ARCHIPACK_OT_floor_manipulate)
+ bpy.utils.register_class(ARCHIPACK_OT_floor_from_curve)
+ bpy.utils.register_class(ARCHIPACK_OT_floor_from_wall)
def unregister():
+ bpy.utils.unregister_class(archipack_floor_cutter_segment)
+ bpy.utils.unregister_class(archipack_floor_cutter)
+ del Mesh.archipack_floor_cutter
+ bpy.utils.unregister_class(ARCHIPACK_OT_floor_cutter)
+ bpy.utils.unregister_class(ARCHIPACK_PT_floor_cutter)
+ bpy.utils.unregister_class(ARCHIPACK_OT_floor_cutter_manipulate)
+
+ bpy.utils.unregister_class(archipack_floor_part)
bpy.utils.unregister_class(archipack_floor)
del Mesh.archipack_floor
bpy.utils.unregister_class(ARCHIPACK_PT_floor)
@@ -1189,3 +2077,5 @@ def unregister():
bpy.utils.unregister_class(ARCHIPACK_OT_floor_preset_menu)
bpy.utils.unregister_class(ARCHIPACK_OT_floor_preset)
bpy.utils.unregister_class(ARCHIPACK_OT_floor_manipulate)
+ bpy.utils.unregister_class(ARCHIPACK_OT_floor_from_curve)
+ bpy.utils.unregister_class(ARCHIPACK_OT_floor_from_wall)