Cleanup: trailing space
[blender-addons.git] / add_mesh_extra_objects / add_mesh_honeycomb.py
blob5c72f952e3749550eb9878a25075287d65828309
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Author: Kayo Phoenix
5 import bpy
6 from bpy_extras import object_utils
7 from math import (
8 pi, sin,
9 cos,
11 from bpy.props import (
12 IntProperty,
13 BoolProperty,
14 BoolVectorProperty,
15 FloatProperty,
16 FloatVectorProperty,
17 StringProperty,
21 class honeycomb_geometry():
22 def __init__(self, rows, cols, D, E):
23 self.rows = rows
24 self.cols = cols
25 self.D = D
26 self.E = E
28 self.hE = 0.5 * self.E
29 self.R = 0.5 * self.D
31 self.a = sin(pi / 3)
33 self.d = self.a * self.D
34 self.hd = 0.5 * self.d
35 self.e = self.hE / self.a
36 self.he = 0.5 * self.e
37 self.r = self.R - self.e
38 self.hr = 0.5 * self.r
40 self.H = self.R * (1.5 * self.rows + 0.5) + self.e
41 if self.rows > 1:
42 self.W = self.d * (self.cols + 0.5) + self.E
43 else:
44 self.W = self.d * self.cols + self.E
46 self.hH = 0.5 * self.H
47 self.hW = 0.5 * self.W
49 self.sy = -self.hH + self.he + self.R
50 self.sx = -self.hW + self.hE + self.hd
52 self.gx = self.hd
54 self.dy = 1.5 * self.R
55 self.dx = self.d
57 def vert(self, row, col):
58 # full cell
59 if row >= 0 and row < self.rows and col >= 0 and col < self.cols:
60 return [0, 1, 2, 3, 4, 5]
61 # right down corner
62 if row == -1 and col == self.cols - 1:
63 return [1, 2]
64 if row == 0 and self.rows > 1 and col == self.cols:
65 return [1, 2, 3]
66 # left down corner
67 if row == -1 and col == -1:
68 return [0, 1]
69 if self.rows % 2:
70 # left up corner
71 if row == self.rows and col == -1:
72 return [4, 5]
73 # right up corner
74 if row == self.rows and col == self.cols - 1:
75 return [3, 4]
76 if row == self.rows - 1 and self.rows > 1 and col == self.cols:
77 return [2, 3, 4]
78 else:
79 # left up corner
80 if row == self.rows and col == 0:
81 return [4, 5]
82 if row == self.rows - 1 and self.rows > 1 and col == -1:
83 return [0, 4, 5]
84 # right up corner
85 if row == self.rows and col == self.cols:
86 return [3, 4]
87 # horizontal lines
88 if col >= 0 and col < self.cols:
89 if row == -1:
90 return [0, 1, 2]
91 if row == self.rows:
92 return [3, 4, 5]
93 # vertical lines
94 if row >= 0 and row < self.rows:
95 if col == -1:
96 if row % 2:
97 return [0, 1, 4, 5]
98 else:
99 return [0, 5]
100 if col == self.cols:
101 if row % 2 or self.rows == 1:
102 return [2, 3]
103 else:
104 return [1, 2, 3, 4]
105 return []
107 def cell(self, row, col, idx):
108 cp = [self.sx + self.dx * col, self.sy + self.dy * row, 0] # central point
109 if row % 2:
110 cp[0] += self.gx
111 co = [] # vertices coords
112 vi = self.vert(row, col)
113 ap = {}
115 for i in vi:
116 a = pi / 6 + i * pi / 3 # angle
117 ap[i] = idx + len(co)
118 co.append((cp[0] + cos(a) * self.r, cp[1] + sin(a) * self.r, cp[2]))
119 return co, ap
121 def generate(self):
122 ar = 1
123 ac = 1
125 cells = []
126 verts = []
127 faces = []
129 for row in range(-ar, self.rows + ar):
130 level = []
131 for col in range(-ac, self.cols + ac):
132 co, ap = self.cell(row, col, len(verts))
133 verts += co
134 level.append(ap)
135 cells.append(level)
137 # bottom row
138 row = 0
139 for col in range(1, len(cells[row]) - 1):
140 s = cells[row][col]
141 l = cells[row][col - 1]
142 u = cells[row + 1][col]
144 faces.append((s[1], u[5], u[4], s[2]))
145 faces.append((s[2], u[4], l[0]))
147 # top row
148 row = len(cells) - 1
149 cs = 0
150 if row % 2:
151 cs += 1
152 for col in range(1 + cs, len(cells[row]) - 1):
153 s = cells[row][col]
154 l = cells[row][col - 1]
155 d = cells[row - 1][col - cs]
156 faces.append((s[3], l[5], d[1]))
157 faces.append([s[3], d[1], d[0], s[4]])
159 # middle rows
160 for row in range(1, len(cells) - 1):
161 cs = 0
162 if row % 2:
163 cs += 1
164 for col in range(1, len(cells[row]) - 1):
165 s = cells[row][col]
166 l = cells[row][col - 1]
167 u = cells[row + 1][col - cs]
168 d = cells[row - 1][col - cs]
170 faces.append((s[1], u[5], u[4], s[2]))
171 faces.append((s[2], u[4], l[0]))
172 faces.append([s[2], l[0], l[5], s[3]])
173 faces.append((s[3], l[5], d[1]))
174 faces.append([s[3], d[1], d[0], s[4]])
176 # right column
177 row = 0
178 col = len(cells[row]) - 1
179 for row in range(1, len(cells) - 1):
180 cs = 0
181 if row % 2:
182 cs += 1
184 s = cells[row][col]
185 l = cells[row][col - 1]
186 u = cells[row + 1][col - cs]
187 d = cells[row - 1][col - cs]
189 if row % 2 and row < len(cells) - 2:
190 faces.append((s[1], u[5], u[4], s[2]))
191 faces.append((s[2], u[4], l[0]))
192 faces.append([s[2], l[0], l[5], s[3]])
193 faces.append((s[3], l[5], d[1]))
194 if row % 2 and row > 1:
195 faces.append([s[3], d[1], d[0], s[4]])
197 # final fix
198 if not self.rows % 2:
199 row = len(cells) - 1
200 s = cells[row][col]
201 l = cells[row][col - 1]
202 d = cells[row - 1][col - 1]
203 faces.append((s[3], l[5], d[1]))
204 faces.append([s[3], d[1], d[0], s[4]])
206 return verts, faces
209 def edge_max(diam):
210 return diam * sin(pi / 3)
213 class add_mesh_honeycomb(bpy.types.Operator, object_utils.AddObjectHelper):
214 bl_idname = "mesh.honeycomb_add"
215 bl_label = "Add HoneyComb"
216 bl_description = "Simple honeycomb mesh generator"
217 bl_options = {'REGISTER', 'UNDO'}
219 def fix_edge(self, context):
220 m = edge_max(self.diam)
221 if self.edge > m:
222 self.edge = m
224 HoneyComb : BoolProperty(name = "HoneyComb",
225 default = True,
226 description = "HoneyComb")
227 change : BoolProperty(name = "Change",
228 default = False,
229 description = "change HoneyComb")
231 rows: IntProperty(
232 name="Num of rows",
233 default=2,
234 min=1, max=100,
235 description='Number of the rows'
237 cols: IntProperty(
238 name='Num of cols',
239 default=2,
240 min=1, max=100,
241 description='Number of the columns'
243 diam: FloatProperty(
244 name='Cell Diameter',
245 default=1.0,
246 min=0.0, update=fix_edge,
247 description='Diameter of the cell'
249 edge: FloatProperty(
250 name='Edge Width',
251 default=0.1,
252 min=0.0, update=fix_edge,
253 description='Width of the edge'
256 def draw(self, context):
257 layout = self.layout
259 layout.prop(self, 'rows', expand=True)
260 layout.prop(self, 'cols', expand=True)
261 layout.prop(self, 'diam', expand=True)
262 layout.prop(self, 'edge', expand=True)
264 if self.change == False:
265 col = layout.column(align=True)
266 col.prop(self, 'align', expand=True)
267 col = layout.column(align=True)
268 col.prop(self, 'location', expand=True)
269 col = layout.column(align=True)
270 col.prop(self, 'rotation', expand=True)
272 @classmethod
273 def poll(cls, context):
274 return context.scene is not None
276 def execute(self, context):
277 # turn off 'Enter Edit Mode'
278 use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
279 bpy.context.preferences.edit.use_enter_edit_mode = False
281 if bpy.context.mode == "OBJECT":
282 if context.selected_objects != [] and context.active_object and \
283 (context.active_object.data is not None) and ('HoneyComb' in context.active_object.data.keys()) and \
284 (self.change == True):
285 obj = context.active_object
286 oldmesh = obj.data
287 oldmeshname = obj.data.name
288 comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
289 verts, faces = comb.generate()
290 mesh = bpy.data.meshes.new('HoneyComb')
291 mesh.from_pydata(verts, [], faces)
292 obj.data = mesh
293 for material in oldmesh.materials:
294 obj.data.materials.append(material)
295 bpy.data.meshes.remove(oldmesh)
296 obj.data.name = oldmeshname
297 else:
298 comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
299 verts, faces = comb.generate()
300 mesh = bpy.data.meshes.new('HoneyComb')
301 mesh.from_pydata(verts, [], faces)
302 obj = object_utils.object_data_add(context, mesh, operator=self)
304 obj.data["HoneyComb"] = True
305 obj.data["change"] = False
306 for prm in HoneyCombParameters():
307 obj.data[prm] = getattr(self, prm)
309 if bpy.context.mode == "EDIT_MESH":
310 active_object = context.active_object
311 name_active_object = active_object.name
312 bpy.ops.object.mode_set(mode='OBJECT')
313 comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
314 verts, faces = comb.generate()
315 mesh = bpy.data.meshes.new('HoneyComb')
316 mesh.from_pydata(verts, [], faces)
317 obj = object_utils.object_data_add(context, mesh, operator=self)
318 obj.select_set(True)
319 active_object.select_set(True)
320 bpy.context.view_layer.objects.active = active_object
321 bpy.ops.object.join()
322 context.active_object.name = name_active_object
323 bpy.ops.object.mode_set(mode='EDIT')
325 if use_enter_edit_mode:
326 bpy.ops.object.mode_set(mode = 'EDIT')
328 # restore pre operator state
329 bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
331 return {'FINISHED'}
333 def HoneyCombParameters():
334 HoneyCombParameters = [
335 "rows",
336 "cols",
337 "diam",
338 "edge",
340 return HoneyCombParameters