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