mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-20 13:22:58 +00:00
337 lines
10 KiB
Python
337 lines
10 KiB
Python
# GPL # "author": "Kayo Phoenix"
|
|
|
|
import bpy
|
|
from bpy_extras import object_utils
|
|
from math import (
|
|
pi, sin,
|
|
cos,
|
|
)
|
|
from bpy.props import (
|
|
IntProperty,
|
|
BoolProperty,
|
|
BoolVectorProperty,
|
|
FloatProperty,
|
|
FloatVectorProperty,
|
|
StringProperty,
|
|
)
|
|
|
|
|
|
class honeycomb_geometry():
|
|
def __init__(self, rows, cols, D, E):
|
|
self.rows = rows
|
|
self.cols = cols
|
|
self.D = D
|
|
self.E = E
|
|
|
|
self.hE = 0.5 * self.E
|
|
self.R = 0.5 * self.D
|
|
|
|
self.a = sin(pi / 3)
|
|
|
|
self.d = self.a * self.D
|
|
self.hd = 0.5 * self.d
|
|
self.e = self.hE / self.a
|
|
self.he = 0.5 * self.e
|
|
self.r = self.R - self.e
|
|
self.hr = 0.5 * self.r
|
|
|
|
self.H = self.R * (1.5 * self.rows + 0.5) + self.e
|
|
if self.rows > 1:
|
|
self.W = self.d * (self.cols + 0.5) + self.E
|
|
else:
|
|
self.W = self.d * self.cols + self.E
|
|
|
|
self.hH = 0.5 * self.H
|
|
self.hW = 0.5 * self.W
|
|
|
|
self.sy = -self.hH + self.he + self.R
|
|
self.sx = -self.hW + self.hE + self.hd
|
|
|
|
self.gx = self.hd
|
|
|
|
self.dy = 1.5 * self.R
|
|
self.dx = self.d
|
|
|
|
def vert(self, row, col):
|
|
# full cell
|
|
if row >= 0 and row < self.rows and col >= 0 and col < self.cols:
|
|
return [0, 1, 2, 3, 4, 5]
|
|
# right down corner
|
|
if row == -1 and col == self.cols - 1:
|
|
return [1, 2]
|
|
if row == 0 and self.rows > 1 and col == self.cols:
|
|
return [1, 2, 3]
|
|
# left down corner
|
|
if row == -1 and col == -1:
|
|
return [0, 1]
|
|
if self.rows % 2:
|
|
# left up corner
|
|
if row == self.rows and col == -1:
|
|
return [4, 5]
|
|
# right up corner
|
|
if row == self.rows and col == self.cols - 1:
|
|
return [3, 4]
|
|
if row == self.rows - 1 and self.rows > 1 and col == self.cols:
|
|
return [2, 3, 4]
|
|
else:
|
|
# left up corner
|
|
if row == self.rows and col == 0:
|
|
return [4, 5]
|
|
if row == self.rows - 1 and self.rows > 1 and col == -1:
|
|
return [0, 4, 5]
|
|
# right up corner
|
|
if row == self.rows and col == self.cols:
|
|
return [3, 4]
|
|
# horizontal lines
|
|
if col >= 0 and col < self.cols:
|
|
if row == -1:
|
|
return [0, 1, 2]
|
|
if row == self.rows:
|
|
return [3, 4, 5]
|
|
# vertical lines
|
|
if row >= 0 and row < self.rows:
|
|
if col == -1:
|
|
if row % 2:
|
|
return [0, 1, 4, 5]
|
|
else:
|
|
return [0, 5]
|
|
if col == self.cols:
|
|
if row % 2 or self.rows == 1:
|
|
return [2, 3]
|
|
else:
|
|
return [1, 2, 3, 4]
|
|
return []
|
|
|
|
def cell(self, row, col, idx):
|
|
cp = [self.sx + self.dx * col, self.sy + self.dy * row, 0] # central point
|
|
if row % 2:
|
|
cp[0] += self.gx
|
|
co = [] # vertices coords
|
|
vi = self.vert(row, col)
|
|
ap = {}
|
|
|
|
for i in vi:
|
|
a = pi / 6 + i * pi / 3 # angle
|
|
ap[i] = idx + len(co)
|
|
co.append((cp[0] + cos(a) * self.r, cp[1] + sin(a) * self.r, cp[2]))
|
|
return co, ap
|
|
|
|
def generate(self):
|
|
ar = 1
|
|
ac = 1
|
|
|
|
cells = []
|
|
verts = []
|
|
faces = []
|
|
|
|
for row in range(-ar, self.rows + ar):
|
|
level = []
|
|
for col in range(-ac, self.cols + ac):
|
|
co, ap = self.cell(row, col, len(verts))
|
|
verts += co
|
|
level.append(ap)
|
|
cells.append(level)
|
|
|
|
# bottom row
|
|
row = 0
|
|
for col in range(1, len(cells[row]) - 1):
|
|
s = cells[row][col]
|
|
l = cells[row][col - 1]
|
|
u = cells[row + 1][col]
|
|
|
|
faces.append((s[1], u[5], u[4], s[2]))
|
|
faces.append((s[2], u[4], l[0]))
|
|
|
|
# top row
|
|
row = len(cells) - 1
|
|
cs = 0
|
|
if row % 2:
|
|
cs += 1
|
|
for col in range(1 + cs, len(cells[row]) - 1):
|
|
s = cells[row][col]
|
|
l = cells[row][col - 1]
|
|
d = cells[row - 1][col - cs]
|
|
faces.append((s[3], l[5], d[1]))
|
|
faces.append([s[3], d[1], d[0], s[4]])
|
|
|
|
# middle rows
|
|
for row in range(1, len(cells) - 1):
|
|
cs = 0
|
|
if row % 2:
|
|
cs += 1
|
|
for col in range(1, len(cells[row]) - 1):
|
|
s = cells[row][col]
|
|
l = cells[row][col - 1]
|
|
u = cells[row + 1][col - cs]
|
|
d = cells[row - 1][col - cs]
|
|
|
|
faces.append((s[1], u[5], u[4], s[2]))
|
|
faces.append((s[2], u[4], l[0]))
|
|
faces.append([s[2], l[0], l[5], s[3]])
|
|
faces.append((s[3], l[5], d[1]))
|
|
faces.append([s[3], d[1], d[0], s[4]])
|
|
|
|
# right column
|
|
row = 0
|
|
col = len(cells[row]) - 1
|
|
for row in range(1, len(cells) - 1):
|
|
cs = 0
|
|
if row % 2:
|
|
cs += 1
|
|
|
|
s = cells[row][col]
|
|
l = cells[row][col - 1]
|
|
u = cells[row + 1][col - cs]
|
|
d = cells[row - 1][col - cs]
|
|
|
|
if row % 2 and row < len(cells) - 2:
|
|
faces.append((s[1], u[5], u[4], s[2]))
|
|
faces.append((s[2], u[4], l[0]))
|
|
faces.append([s[2], l[0], l[5], s[3]])
|
|
faces.append((s[3], l[5], d[1]))
|
|
if row % 2 and row > 1:
|
|
faces.append([s[3], d[1], d[0], s[4]])
|
|
|
|
# final fix
|
|
if not self.rows % 2:
|
|
row = len(cells) - 1
|
|
s = cells[row][col]
|
|
l = cells[row][col - 1]
|
|
d = cells[row - 1][col - 1]
|
|
faces.append((s[3], l[5], d[1]))
|
|
faces.append([s[3], d[1], d[0], s[4]])
|
|
|
|
return verts, faces
|
|
|
|
|
|
def edge_max(diam):
|
|
return diam * sin(pi / 3)
|
|
|
|
|
|
class add_mesh_honeycomb(bpy.types.Operator, object_utils.AddObjectHelper):
|
|
bl_idname = "mesh.honeycomb_add"
|
|
bl_label = "Add HoneyComb"
|
|
bl_description = "Simple honeycomb mesh generator"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def fix_edge(self, context):
|
|
m = edge_max(self.diam)
|
|
if self.edge > m:
|
|
self.edge = m
|
|
|
|
HoneyComb : BoolProperty(name = "HoneyComb",
|
|
default = True,
|
|
description = "HoneyComb")
|
|
change : BoolProperty(name = "Change",
|
|
default = False,
|
|
description = "change HoneyComb")
|
|
|
|
rows: IntProperty(
|
|
name="Num of rows",
|
|
default=2,
|
|
min=1, max=100,
|
|
description='Number of the rows'
|
|
)
|
|
cols: IntProperty(
|
|
name='Num of cols',
|
|
default=2,
|
|
min=1, max=100,
|
|
description='Number of the columns'
|
|
)
|
|
diam: FloatProperty(
|
|
name='Cell Diameter',
|
|
default=1.0,
|
|
min=0.0, update=fix_edge,
|
|
description='Diameter of the cell'
|
|
)
|
|
edge: FloatProperty(
|
|
name='Edge Width',
|
|
default=0.1,
|
|
min=0.0, update=fix_edge,
|
|
description='Width of the edge'
|
|
)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
layout.prop(self, 'rows', expand=True)
|
|
layout.prop(self, 'cols', expand=True)
|
|
layout.prop(self, 'diam', expand=True)
|
|
layout.prop(self, 'edge', expand=True)
|
|
|
|
if self.change == False:
|
|
col = layout.column(align=True)
|
|
col.prop(self, 'align', expand=True)
|
|
col = layout.column(align=True)
|
|
col.prop(self, 'location', expand=True)
|
|
col = layout.column(align=True)
|
|
col.prop(self, 'rotation', expand=True)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.scene is not None
|
|
|
|
def execute(self, context):
|
|
# turn off 'Enter Edit Mode'
|
|
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
|
|
bpy.context.preferences.edit.use_enter_edit_mode = False
|
|
|
|
if bpy.context.mode == "OBJECT":
|
|
if context.selected_objects != [] and context.active_object and \
|
|
('HoneyComb' in context.active_object.data.keys()) and (self.change == True):
|
|
obj = context.active_object
|
|
oldmesh = obj.data
|
|
oldmeshname = obj.data.name
|
|
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
|
|
verts, faces = comb.generate()
|
|
mesh = bpy.data.meshes.new('HoneyComb')
|
|
mesh.from_pydata(verts, [], faces)
|
|
obj.data = mesh
|
|
for material in oldmesh.materials:
|
|
obj.data.materials.append(material)
|
|
bpy.data.meshes.remove(oldmesh)
|
|
obj.data.name = oldmeshname
|
|
else:
|
|
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
|
|
verts, faces = comb.generate()
|
|
mesh = bpy.data.meshes.new('HoneyComb')
|
|
mesh.from_pydata(verts, [], faces)
|
|
obj = object_utils.object_data_add(context, mesh, operator=self)
|
|
|
|
obj.data["HoneyComb"] = True
|
|
obj.data["change"] = False
|
|
for prm in HoneyCombParameters():
|
|
obj.data[prm] = getattr(self, prm)
|
|
|
|
if bpy.context.mode == "EDIT_MESH":
|
|
active_object = context.active_object
|
|
name_active_object = active_object.name
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
|
|
verts, faces = comb.generate()
|
|
mesh = bpy.data.meshes.new('HoneyComb')
|
|
mesh.from_pydata(verts, [], faces)
|
|
obj = object_utils.object_data_add(context, mesh, operator=self)
|
|
obj.select_set(True)
|
|
active_object.select_set(True)
|
|
bpy.ops.object.join()
|
|
context.active_object.name = name_active_object
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
if use_enter_edit_mode:
|
|
bpy.ops.object.mode_set(mode = 'EDIT')
|
|
|
|
# restore pre operator state
|
|
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
|
|
|
|
return {'FINISHED'}
|
|
|
|
def HoneyCombParameters():
|
|
HoneyCombParameters = [
|
|
"rows",
|
|
"cols",
|
|
"diam",
|
|
"edge",
|
|
]
|
|
return HoneyCombParameters
|