fix [#36995] FBX Importer does not import fbx model
[blender-addons.git] / add_mesh_solid.py
blobcb874fed590199fda7f722250c487fd18c040e5d
1 # ***** BEGIN GPL LICENSE BLOCK *****
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # ***** END GPL LICENCE BLOCK *****
20 bl_info = {
21 "name": "Regular Solids",
22 "author": "DreamPainter",
23 "version": (2, 0),
24 "blender": (2, 59, 0),
25 "location": "View3D > Add > Mesh > Solids",
26 "description": "Add a regular solid",
27 "warning": "",
28 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
29 "Scripts/Add_Mesh/Add_Solid",
30 "tracker_url": "https://projects.blender.org/tracker/index.php?"\
31 "func=detail&aid=22405",
32 "category": "Add Mesh"}
34 import bpy
35 from bpy.props import FloatProperty,EnumProperty,BoolProperty
36 from math import sqrt
37 from mathutils import Vector
38 from functools import reduce
39 from bpy_extras.object_utils import object_data_add
41 # this function creates a chain of quads and, when necessary, a remaining tri
42 # for each polygon created in this script. be aware though, that this function
43 # assumes each polygon is convex.
44 # poly: list of faces, or a single face, like those
45 # needed for mesh.from_pydata.
46 # returns the tessellated faces.
47 def createPolys(poly):
48 # check for faces
49 if len(poly) == 0:
50 return []
51 # one or more faces
52 if type(poly[0]) == type(1):
53 poly = [poly] # if only one, make it a list of one face
54 faces = []
55 for i in poly:
56 L = len(i)
57 # let all faces of 3 or 4 verts be
58 if L < 5:
59 faces.append(i)
60 # split all polygons in half and bridge the two halves
61 else:
62 f = [[i[x],i[x+1],i[L-2-x],i[L-1-x]] for x in range(L//2-1)]
63 faces.extend(f)
64 if L&1 == 1:
65 faces.append([i[L//2-1+x] for x in [0,1,2]])
66 return faces
68 # function to make the reduce function work as a workaround to sum a list of vectors
69 def vSum(list):
70 return reduce(lambda a,b: a+b, list)
72 # creates the 5 platonic solids as a base for the rest
73 # plato: should be one of {"4","6","8","12","20"}. decides what solid the
74 # outcome will be.
75 # returns a list of vertices and faces
76 def source(plato):
77 verts = []
78 faces = []
80 # Tetrahedron
81 if plato == "4":
82 # Calculate the necessary constants
83 s = sqrt(2)/3.0
84 t = -1/3
85 u = sqrt(6)/3
87 # create the vertices and faces
88 v = [(0,0,1),(2*s,0,t),(-s,u,t),(-s,-u,t)]
89 faces = [[0,1,2],[0,2,3],[0,3,1],[1,3,2]]
91 # Hexahedron (cube)
92 elif plato == "6":
93 # Calculate the necessary constants
94 s = 1/sqrt(3)
96 # create the vertices and faces
97 v = [(-s,-s,-s),(s,-s,-s),(s,s,-s),(-s,s,-s),(-s,-s,s),(s,-s,s),(s,s,s),(-s,s,s)]
98 faces = [[0,3,2,1],[0,1,5,4],[0,4,7,3],[6,5,1,2],[6,2,3,7],[6,7,4,5]]
100 # Octahedron
101 elif plato == "8":
102 # create the vertices and faces
103 v = [(1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)]
104 faces = [[4,0,2],[4,2,1],[4,1,3],[4,3,0],[5,2,0],[5,1,2],[5,3,1],[5,0,3]]
106 # Dodecahedron
107 elif plato == "12":
108 # Calculate the necessary constants
109 s = 1/sqrt(3)
110 t = sqrt((3-sqrt(5))/6)
111 u = sqrt((3+sqrt(5))/6)
113 # create the vertices and faces
114 v = [(s,s,s),(s,s,-s),(s,-s,s),(s,-s,-s),(-s,s,s),(-s,s,-s),(-s,-s,s),(-s,-s,-s),
115 (t,u,0),(-t,u,0),(t,-u,0),(-t,-u,0),(u,0,t),(u,0,-t),(-u,0,t),(-u,0,-t),(0,t,u),
116 (0,-t,u),(0,t,-u),(0,-t,-u)]
117 faces = [[0,8,9,4,16],[0,12,13,1,8],[0,16,17,2,12],[8,1,18,5,9],[12,2,10,3,13],
118 [16,4,14,6,17],[9,5,15,14,4],[6,11,10,2,17],[3,19,18,1,13],[7,15,5,18,19],
119 [7,11,6,14,15],[7,19,3,10,11]]
121 # Icosahedron
122 elif plato == "20":
123 # Calculate the necessary constants
124 s = (1+sqrt(5))/2
125 t = sqrt(1+s*s)
126 s = s/t
127 t = 1/t
129 # create the vertices and faces
130 v = [(s,t,0),(-s,t,0),(s,-t,0),(-s,-t,0),(t,0,s),(t,0,-s),(-t,0,s),(-t,0,-s),
131 (0,s,t),(0,-s,t),(0,s,-t),(0,-s,-t)]
132 faces = [[0,8,4],[0,5,10],[2,4,9],[2,11,5],[1,6,8],[1,10,7],[3,9,6],[3,7,11],
133 [0,10,8],[1,8,10],[2,9,11],[3,11,9],[4,2,0],[5,0,2],[6,1,3],[7,3,1],
134 [8,6,4],[9,4,6],[10,5,7],[11,7,5]]
136 # convert the tuples to Vectors
137 verts = [Vector(i) for i in v]
139 return verts,faces
141 # processes the raw data from source
142 def createSolid(plato,vtrunc,etrunc,dual,snub):
143 # the duals from each platonic solid
144 dualSource = {"4":"4",
145 "6":"8",
146 "8":"6",
147 "12":"20",
148 "20":"12"}
150 # constants saving space and readability
151 vtrunc *= 0.5
152 etrunc *= 0.5
153 supposedSize = 0
154 noSnub = (snub == "None") or (etrunc == 0.5) or (etrunc == 0)
155 lSnub = (snub == "Left") and (0 < etrunc < 0.5)
156 rSnub = (snub == "Right") and (0 < etrunc < 0.5)
158 # no truncation
159 if vtrunc == 0:
160 if dual: # dual is as simple as another, but mirrored platonic solid
161 vInput, fInput = source(dualSource[plato])
162 supposedSize = vSum(vInput[i] for i in fInput[0]).length/len(fInput[0])
163 vInput = [-i*supposedSize for i in vInput] # mirror it
164 return vInput, fInput
165 return source(plato)
166 elif 0 < vtrunc <= 0.5: # simple truncation of the source
167 vInput, fInput = source(plato)
168 else:
169 # truncation is now equal to simple truncation of the dual of the source
170 vInput, fInput = source(dualSource[plato])
171 supposedSize = vSum(vInput[i] for i in fInput[0]).length / len(fInput[0])
172 vtrunc = 1-vtrunc # account for the source being a dual
173 if vtrunc == 0: # no truncation needed
174 if dual:
175 vInput, fInput = source(plato)
176 vInput = [i*supposedSize for i in vInput]
177 return vInput, fInput
178 vInput = [-i*supposedSize for i in vInput]
179 return vInput, fInput
181 # generate connection database
182 vDict = [{} for i in vInput]
183 # for every face, store what vertex comes after and before the current vertex
184 for x in range(len(fInput)):
185 i = fInput[x]
186 for j in range(len(i)):
187 vDict[i[j-1]][i[j]] = [i[j-2],x]
188 if len(vDict[i[j-1]]) == 1: vDict[i[j-1]][-1] = i[j]
190 # the actual connection database: exists out of:
191 # [vtrunc pos, etrunc pos, connected vert IDs, connected face IDs]
192 vData = [[[],[],[],[]] for i in vInput]
193 fvOutput = [] # faces created from truncated vertices
194 feOutput = [] # faces created from truncated edges
195 vOutput = [] # newly created vertices
196 for x in range(len(vInput)):
197 i = vDict[x] # lookup the current vertex
198 current = i[-1]
199 while True: # follow the chain to get a ccw order of connected verts and faces
200 vData[x][2].append(i[current][0])
201 vData[x][3].append(i[current][1])
202 # create truncated vertices
203 vData[x][0].append((1-vtrunc)*vInput[x] + vtrunc*vInput[vData[x][2][-1]])
204 current = i[current][0]
205 if current == i[-1]: break # if we're back at the first: stop the loop
206 fvOutput.append([]) # new face from truncated vert
207 fOffset = x*(len(i)-1) # where to start off counting faceVerts
208 # only create one vert where one is needed (v1 todo: done)
209 if etrunc == 0.5:
210 for j in range(len(i)-1):
211 vOutput.append((vData[x][0][j]+vData[x][0][j-1])*etrunc) # create vert
212 fvOutput[x].append(fOffset+j) # add to face
213 fvOutput[x] = fvOutput[x][1:]+[fvOutput[x][0]] # rotate face for ease later on
214 # create faces from truncated edges.
215 for j in range(len(i)-1):
216 if x > vData[x][2][j]: #only create when other vertex has been added
217 index = vData[vData[x][2][j]][2].index(x)
218 feOutput.append([fvOutput[x][j],fvOutput[x][j-1],
219 fvOutput[vData[x][2][j]][index],
220 fvOutput[vData[x][2][j]][index-1]])
221 # edge truncation between none and full
222 elif etrunc > 0:
223 for j in range(len(i)-1):
224 # create snubs from selecting verts from rectified meshes
225 if rSnub:
226 vOutput.append(etrunc*vData[x][0][j]+(1-etrunc)*vData[x][0][j-1])
227 fvOutput[x].append(fOffset+j)
228 elif lSnub:
229 vOutput.append((1-etrunc)*vData[x][0][j]+etrunc*vData[x][0][j-1])
230 fvOutput[x].append(fOffset+j)
231 else: #noSnub, select both verts from rectified mesh
232 vOutput.append(etrunc*vData[x][0][j]+(1-etrunc)*vData[x][0][j-1])
233 vOutput.append((1-etrunc)*vData[x][0][j]+etrunc*vData[x][0][j-1])
234 fvOutput[x].append(2*fOffset+2*j)
235 fvOutput[x].append(2*fOffset+2*j+1)
236 # rotate face for ease later on
237 if noSnub: fvOutput[x] = fvOutput[x][2:]+fvOutput[x][:2]
238 else: fvOutput[x] = fvOutput[x][1:]+[fvOutput[x][0]]
239 # create single face for each edge
240 if noSnub:
241 for j in range(len(i)-1):
242 if x > vData[x][2][j]:
243 index = vData[vData[x][2][j]][2].index(x)
244 feOutput.append([fvOutput[x][j*2],fvOutput[x][2*j-1],
245 fvOutput[vData[x][2][j]][2*index],
246 fvOutput[vData[x][2][j]][2*index-1]])
247 # create 2 tri's for each edge for the snubs
248 elif rSnub:
249 for j in range(len(i)-1):
250 if x > vData[x][2][j]:
251 index = vData[vData[x][2][j]][2].index(x)
252 feOutput.append([fvOutput[x][j],fvOutput[x][j-1],
253 fvOutput[vData[x][2][j]][index]])
254 feOutput.append([fvOutput[x][j],fvOutput[vData[x][2][j]][index],
255 fvOutput[vData[x][2][j]][index-1]])
256 elif lSnub:
257 for j in range(len(i)-1):
258 if x > vData[x][2][j]:
259 index = vData[vData[x][2][j]][2].index(x)
260 feOutput.append([fvOutput[x][j],fvOutput[x][j-1],
261 fvOutput[vData[x][2][j]][index-1]])
262 feOutput.append([fvOutput[x][j-1],fvOutput[vData[x][2][j]][index],
263 fvOutput[vData[x][2][j]][index-1]])
264 # special rules fro birectified mesh (v1 todo: done)
265 elif vtrunc == 0.5:
266 for j in range(len(i)-1):
267 if x < vData[x][2][j]: # use current vert, since other one has not passed yet
268 vOutput.append(vData[x][0][j])
269 fvOutput[x].append(len(vOutput)-1)
270 else:
271 # search for other edge to avoid duplicity
272 connectee = vData[x][2][j]
273 fvOutput[x].append(fvOutput[connectee][vData[connectee][2].index(x)])
274 else: # vert truncation only
275 vOutput.extend(vData[x][0]) # use generated verts from way above
276 for j in range(len(i)-1): # create face from them
277 fvOutput[x].append(fOffset+j)
279 # calculate supposed vertex length to ensure continuity
280 if supposedSize and not dual: # this to make the vtrunc > 1 work
281 supposedSize *= len(fvOutput[0])/vSum(vOutput[i] for i in fvOutput[0]).length
282 vOutput = [-i*supposedSize for i in vOutput]
284 # create new faces by replacing old vert IDs by newly generated verts
285 ffOutput = [[] for i in fInput]
286 for x in range(len(fInput)):
287 # only one generated vert per vertex, so choose accordingly
288 if etrunc == 0.5 or (etrunc == 0 and vtrunc == 0.5) or lSnub or rSnub:
289 ffOutput[x] = [fvOutput[i][vData[i][3].index(x)-1] for i in fInput[x]]
290 # two generated verts per vertex
291 elif etrunc > 0:
292 for i in fInput[x]:
293 ffOutput[x].append(fvOutput[i][2*vData[i][3].index(x)-1])
294 ffOutput[x].append(fvOutput[i][2*vData[i][3].index(x)-2])
295 else: # cutting off corners also makes 2 verts
296 for i in fInput[x]:
297 ffOutput[x].append(fvOutput[i][vData[i][3].index(x)])
298 ffOutput[x].append(fvOutput[i][vData[i][3].index(x)-1])
300 if not dual:
301 return vOutput,fvOutput + feOutput + ffOutput
302 else:
303 # do the same procedure as above, only now on the generated mesh
304 # generate connection database
305 vDict = [{} for i in vOutput]
306 dvOutput = [0 for i in fvOutput + feOutput + ffOutput]
307 dfOutput = []
309 for x in range(len(dvOutput)): # for every face
310 i = (fvOutput + feOutput + ffOutput)[x] # choose face to work with
311 # find vertex from face
312 normal = (vOutput[i[0]]-vOutput[i[1]]).cross(vOutput[i[2]]-vOutput[i[1]]).normalized()
313 dvOutput[x] = normal/(normal.dot(vOutput[i[0]]))
314 for j in range(len(i)): # create vert chain
315 vDict[i[j-1]][i[j]] = [i[j-2],x]
316 if len(vDict[i[j-1]]) == 1: vDict[i[j-1]][-1] = i[j]
318 # calculate supposed size for continuity
319 supposedSize = vSum([vInput[i] for i in fInput[0]]).length/len(fInput[0])
320 supposedSize /= dvOutput[-1].length
321 dvOutput = [i*supposedSize for i in dvOutput]
323 # use chains to create faces
324 for x in range(len(vOutput)):
325 i = vDict[x]
326 current = i[-1]
327 face = []
328 while True:
329 face.append(i[current][1])
330 current = i[current][0]
331 if current == i[-1]: break
332 dfOutput.append(face)
334 return dvOutput,dfOutput
336 class Solids(bpy.types.Operator):
337 """Add one of the (regular) solids (mesh)"""
338 bl_idname = "mesh.primitive_solid_add"
339 bl_label = "(Regular) solids"
340 bl_description = "Add one of the Platonic, Archimedean or Catalan solids"
341 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
343 source = EnumProperty(items = (("4","Tetrahedron",""),
344 ("6","Hexahedron",""),
345 ("8","Octahedron",""),
346 ("12","Dodecahedron",""),
347 ("20","Icosahedron","")),
348 name = "Source",
349 description = "Starting point of your solid")
350 size = FloatProperty(name = "Size",
351 description = "Radius of the sphere through the vertices",
352 min = 0.01,
353 soft_min = 0.01,
354 max = 100,
355 soft_max = 100,
356 default = 1.0)
357 vTrunc = FloatProperty(name = "Vertex Truncation",
358 description = "Ammount of vertex truncation",
359 min = 0.0,
360 soft_min = 0.0,
361 max = 2.0,
362 soft_max = 2.0,
363 default = 0.0,
364 precision = 3,
365 step = 0.5)
366 eTrunc = FloatProperty(name = "Edge Truncation",
367 description = "Ammount of edge truncation",
368 min = 0.0,
369 soft_min = 0.0,
370 max = 1.0,
371 soft_max = 1.0,
372 default = 0.0,
373 precision = 3,
374 step = 0.2)
375 snub = EnumProperty(items = (("None","No Snub",""),
376 ("Left","Left Snub",""),
377 ("Right","Right Snub","")),
378 name = "Snub",
379 description = "Create the snub version")
380 dual = BoolProperty(name="Dual",
381 description="Create the dual of the current solid",
382 default=False)
383 keepSize = BoolProperty(name="Keep Size",
384 description="Keep the whole solid at a constant size",
385 default=False)
386 preset = EnumProperty(items = (("0","Custom",""),
387 ("t4","Truncated Tetrahedron",""),
388 ("r4","Cuboctahedron",""),
389 ("t6","Truncated Cube",""),
390 ("t8","Truncated Octahedron",""),
391 ("b6","Rhombicuboctahedron",""),
392 ("c6","Truncated Cuboctahedron",""),
393 ("s6","Snub Cube",""),
394 ("r12","Icosidodecahedron",""),
395 ("t12","Truncated Dodecahedron",""),
396 ("t20","Truncated Icosahedron",""),
397 ("b12","Rhombicosidodecahedron",""),
398 ("c12","Truncated Icosidodecahedron",""),
399 ("s12","Snub Dodecahedron",""),
400 ("dt4","Triakis Tetrahedron",""),
401 ("dr4","Rhombic Dodecahedron",""),
402 ("dt6","Triakis Octahedron",""),
403 ("dt8","Tetrakis Hexahedron",""),
404 ("db6","Deltoidal Icositetrahedron",""),
405 ("dc6","Disdyakis Dodecahedron",""),
406 ("ds6","Pentagonal Icositetrahedron",""),
407 ("dr12","Rhombic Triacontahedron",""),
408 ("dt12","Triakis Icosahedron",""),
409 ("dt20","Pentakis Dodecahedron",""),
410 ("db12","Deltoidal Hexecontahedron",""),
411 ("dc12","Disdyakis Triacontahedron",""),
412 ("ds12","Pentagonal Hexecontahedron","")),
413 name = "Presets",
414 description = "Parameters for some hard names")
416 # actual preset values
417 p = {"t4":["4",2/3,0,0,"None"],
418 "r4":["4",1,1,0,"None"],
419 "t6":["6",2/3,0,0,"None"],
420 "t8":["8",2/3,0,0,"None"],
421 "b6":["6",1.0938,1,0,"None"],
422 "c6":["6",1.0572,0.585786,0,"None"],
423 "s6":["6",1.0875,0.704,0,"Left"],
424 "r12":["12",1,0,0,"None"],
425 "t12":["12",2/3,0,0,"None"],
426 "t20":["20",2/3,0,0,"None"],
427 "b12":["12",1.1338,1,0,"None"],
428 "c12":["20",0.921,0.553,0,"None"],
429 "s12":["12",1.1235,0.68,0,"Left"],
430 "dt4":["4",2/3,0,1,"None"],
431 "dr4":["4",1,1,1,"None"],
432 "dt6":["6",2/3,0,1,"None"],
433 "dt8":["8",2/3,0,1,"None"],
434 "db6":["6",1.0938,1,1,"None"],
435 "dc6":["6",1.0572,0.585786,1,"None"],
436 "ds6":["6",1.0875,0.704,1,"Left"],
437 "dr12":["12",1,0,1,"None"],
438 "dt12":["12",2/3,0,1,"None"],
439 "dt20":["20",2/3,0,1,"None"],
440 "db12":["12",1.1338,1,1,"None"],
441 "dc12":["20",0.921,0.553,1,"None"],
442 "ds12":["12",1.1235,0.68,1,"Left"]}
444 #previous preset, for User-friendly reasons
445 previousSetting = ""
447 def execute(self,context):
448 # turn off undo for better performance (3-5x faster), also makes sure
449 # that mesh ops are undoable and entire script acts as one operator
450 bpy.context.user_preferences.edit.use_global_undo = False
452 # piece of code to make presets remain until parameters are changed
453 if self.preset != "0":
454 #if preset, set preset
455 if self.previousSetting != self.preset:
456 using = self.p[self.preset]
457 self.source = using[0]
458 self.vTrunc = using[1]
459 self.eTrunc = using[2]
460 self.dual = using[3]
461 self.snub = using[4]
462 else:
463 using = self.p[self.preset]
464 result0 = self.source == using[0]
465 result1 = abs(self.vTrunc - using[1]) < 0.004
466 result2 = abs(self.eTrunc - using[2]) < 0.0015
467 result4 = using[4] == self.snub or ((using[4] == "Left") and
468 self.snub in ["Left","Right"])
469 if (result0 and result1 and result2 and result4):
470 if self.p[self.previousSetting][3] != self.dual:
471 if self.preset[0] == "d":
472 self.preset = self.preset[1:]
473 else:
474 self.preset = "d" + self.preset
475 else:
476 self.preset = "0"
478 self.previousSetting = self.preset
480 # generate mesh
481 verts,faces = createSolid(self.source,
482 self.vTrunc,
483 self.eTrunc,
484 self.dual,
485 self.snub)
487 # turn n-gons in quads and tri's
488 faces = createPolys(faces)
490 # resize to normal size, or if keepSize, make sure all verts are of length 'size'
491 if self.keepSize:
492 rad = self.size/verts[-1 if self.dual else 0].length
493 else: rad = self.size
494 verts = [i*rad for i in verts]
496 # generate object
497 # Create new mesh
498 mesh = bpy.data.meshes.new("Solid")
500 # Make a mesh from a list of verts/edges/faces.
501 mesh.from_pydata(verts, [], faces)
503 # Update mesh geometry after adding stuff.
504 mesh.update()
506 object_data_add(context, mesh, operator=None)
507 # object generation done
509 # turn undo back on
510 bpy.context.user_preferences.edit.use_global_undo = True
512 return {'FINISHED'}
514 class Solids_add_menu(bpy.types.Menu):
515 """Define the menu with presets"""
516 bl_idname = "Solids_add_menu"
517 bl_label = "Solids"
519 def draw(self,context):
520 layout = self.layout
521 layout.operator_context = 'INVOKE_REGION_WIN'
522 layout.operator(Solids.bl_idname, text = "Solid")
523 layout.menu(PlatonicMenu.bl_idname, text = "Platonic")
524 layout.menu(ArchiMenu.bl_idname, text = "Archimeadean")
525 layout.menu(CatalanMenu.bl_idname, text = "Catalan")
527 class PlatonicMenu(bpy.types.Menu):
528 """Define Platonic menu"""
529 bl_idname = "Platonic_calls"
530 bl_label = "Platonic"
532 def draw(self,context):
533 layout = self.layout
534 layout.operator_context = 'INVOKE_REGION_WIN'
535 layout.operator(Solids.bl_idname, text = "Tetrahedron").source = "4"
536 layout.operator(Solids.bl_idname, text = "Hexahedron").source = "6"
537 layout.operator(Solids.bl_idname, text = "Octahedron").source = "8"
538 layout.operator(Solids.bl_idname, text = "Dodecahedron").source = "12"
539 layout.operator(Solids.bl_idname, text = "Icosahedron").source = "20"
541 class ArchiMenu(bpy.types.Menu):
542 """Defines Achimedean preset menu"""
543 bl_idname = "Achimedean_calls"
544 bl_label = "Archimedean"
546 def draw(self,context):
547 layout = self.layout
548 layout.operator_context = 'INVOKE_REGION_WIN'
549 layout.operator(Solids.bl_idname, text = "Truncated Tetrahedron").preset = "t4"
550 layout.operator(Solids.bl_idname, text = "Cuboctahedron").preset = "r4"
551 layout.operator(Solids.bl_idname, text = "Truncated Cube").preset = "t6"
552 layout.operator(Solids.bl_idname, text = "Truncated Octahedron").preset = "t8"
553 layout.operator(Solids.bl_idname, text = "Rhombicuboctahedron").preset = "b6"
554 layout.operator(Solids.bl_idname, text = "Truncated Cuboctahedron").preset = "c6"
555 layout.operator(Solids.bl_idname, text = "Snub Cube").preset = "s6"
556 layout.operator(Solids.bl_idname, text = "Icosidodecahedron").preset = "r12"
557 layout.operator(Solids.bl_idname, text = "Truncated Dodecahedron").preset = "t12"
558 layout.operator(Solids.bl_idname, text = "Truncated Icosahedron").preset = "t20"
559 layout.operator(Solids.bl_idname, text = "Rhombicosidodecahedron").preset = "b12"
560 layout.operator(Solids.bl_idname, text = "Truncated Icosidodecahedron").preset = "c12"
561 layout.operator(Solids.bl_idname, text = "Snub Dodecahedron").preset = "s12"
563 class CatalanMenu(bpy.types.Menu):
564 """Defines Catalan preset menu"""
565 bl_idname = "Catalan_calls"
566 bl_label = "Catalan"
568 def draw(self, context):
569 layout = self.layout
570 layout.operator_context = 'INVOKE_REGION_WIN'
571 layout.operator(Solids.bl_idname, text = "Triakis Tetrahedron").preset = "dt4"
572 layout.operator(Solids.bl_idname, text = "Rhombic Dodecahedron").preset = "dr4"
573 layout.operator(Solids.bl_idname, text = "Triakis Octahedron").preset = "dt6"
574 layout.operator(Solids.bl_idname, text = "Triakis Hexahedron").preset = "dt8"
575 layout.operator(Solids.bl_idname, text = "Deltoidal Icositetrahedron").preset = "db6"
576 layout.operator(Solids.bl_idname, text = "Disdyakis Dodecahedron").preset = "dc6"
577 layout.operator(Solids.bl_idname, text = "Pentagonal Icositetrahedron").preset = "ds6"
578 layout.operator(Solids.bl_idname, text = "Rhombic Triacontahedron").preset = "dr12"
579 layout.operator(Solids.bl_idname, text = "Triakis Icosahedron").preset = "dt12"
580 layout.operator(Solids.bl_idname, text = "Pentakis Dodecahedron").preset = "dt20"
581 layout.operator(Solids.bl_idname, text = "Deltoidal Hexecontahedron").preset = "db12"
582 layout.operator(Solids.bl_idname, text = "Disdyakis Triacontahedron").preset = "dc12"
583 layout.operator(Solids.bl_idname, text = "Pentagonal Hexecontahedron").preset = "ds12"
585 def menu_func(self, context):
586 self.layout.menu(Solids_add_menu.bl_idname, icon="PLUGIN")
589 def register():
590 bpy.utils.register_module(__name__)
592 bpy.types.INFO_MT_mesh_add.append(menu_func)
595 def unregister():
596 bpy.utils.unregister_module(__name__)
598 bpy.types.INFO_MT_mesh_add.remove(menu_func)
601 if __name__ == "__main__":
602 register()