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 *****
21 "name": "Regular Solids",
22 "author": "DreamPainter",
24 "blender": (2, 59, 0),
25 "location": "View3D > Add > Mesh > Solids",
26 "description": "Add a regular solid",
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"}
35 from bpy
.props
import FloatProperty
,EnumProperty
,BoolProperty
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
):
52 if type(poly
[0]) == type(1):
53 poly
= [poly
] # if only one, make it a list of one face
57 # let all faces of 3 or 4 verts be
60 # split all polygons in half and bridge the two halves
62 f
= [[i
[x
],i
[x
+1],i
[L
-2-x
],i
[L
-1-x
]] for x
in range(L
//2-1)]
65 faces
.append([i
[L
//2-1+x
] for x
in [0,1,2]])
68 # function to make the reduce function work as a workaround to sum a list of vectors
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
75 # returns a list of vertices and faces
82 # Calculate the necessary constants
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]]
93 # Calculate the necessary constants
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]]
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]]
108 # Calculate the necessary constants
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]]
123 # Calculate the necessary constants
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
]
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",
150 # constants saving space and readability
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)
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
166 elif 0 < vtrunc
<= 0.5: # simple truncation of the source
167 vInput
, fInput
= source(plato
)
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
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
)):
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
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)
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
223 for j
in range(len(i
)-1):
224 # create snubs from selecting verts from rectified meshes
226 vOutput
.append(etrunc
*vData
[x
][0][j
]+(1-etrunc
)*vData
[x
][0][j
-1])
227 fvOutput
[x
].append(fOffset
+j
)
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
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
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]])
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)
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)
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
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
297 ffOutput
[x
].append(fvOutput
[i
][vData
[i
][3].index(x
)])
298 ffOutput
[x
].append(fvOutput
[i
][vData
[i
][3].index(x
)-1])
301 return vOutput
,fvOutput
+ feOutput
+ ffOutput
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
]
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
)):
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","")),
349 description
= "Starting point of your solid")
350 size
= FloatProperty(name
= "Size",
351 description
= "Radius of the sphere through the vertices",
357 vTrunc
= FloatProperty(name
= "Vertex Truncation",
358 description
= "Ammount of vertex truncation",
366 eTrunc
= FloatProperty(name
= "Edge Truncation",
367 description
= "Ammount of edge truncation",
375 snub
= EnumProperty(items
= (("None","No Snub",""),
376 ("Left","Left Snub",""),
377 ("Right","Right Snub","")),
379 description
= "Create the snub version")
380 dual
= BoolProperty(name
="Dual",
381 description
="Create the dual of the current solid",
383 keepSize
= BoolProperty(name
="Keep Size",
384 description
="Keep the whole solid at a constant size",
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","")),
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
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]
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:]
474 self
.preset
= "d" + self
.preset
478 self
.previousSetting
= self
.preset
481 verts
,faces
= createSolid(self
.source
,
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'
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
]
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.
506 object_data_add(context
, mesh
, operator
=None)
507 # object generation done
510 bpy
.context
.user_preferences
.edit
.use_global_undo
= True
514 class Solids_add_menu(bpy
.types
.Menu
):
515 """Define the menu with presets"""
516 bl_idname
= "Solids_add_menu"
519 def draw(self
,context
):
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
):
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
):
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"
568 def draw(self
, context
):
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")
590 bpy
.utils
.register_module(__name__
)
592 bpy
.types
.INFO_MT_mesh_add
.append(menu_func
)
596 bpy
.utils
.unregister_module(__name__
)
598 bpy
.types
.INFO_MT_mesh_add
.remove(menu_func
)
601 if __name__
== "__main__":