18
18
# ***** END GPL LICENCE BLOCK *****
21
21
"name": "Regular Solids",
22
22
"author": "DreamPainter",
26
"location": "View3D > Add > Mesh > Regular Solids",
27
"description": "Add a Regular Solid mesh.",
25
"location": "View3D > Add > Mesh > Solids",
26
"description": "Add a regular solid",
29
28
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
30
29
"Scripts/Add_Mesh/Add_Solid",
31
30
"tracker_url": "https://projects.blender.org/tracker/index.php?"\
32
"func=detail&aid=22405&group_id=153&atid=469",
31
"func=detail&aid=22405",
33
32
"category": "Add Mesh"}
37
35
from bpy.props import FloatProperty,EnumProperty,BoolProperty
38
36
from math import sqrt
39
from mathutils import Vector,Matrix
40
#from rawMeshUtils import *
37
from mathutils import Vector
41
38
from functools import reduce
43
# Apply view rotation to objects if "Align To" for
44
# new objects was set to "VIEW" in the User Preference.
45
def apply_object_align(context, ob):
46
obj_align = bpy.context.user_preferences.edit.object_align
48
if (context.space_data.type == 'VIEW_3D'
49
and obj_align == 'VIEW'):
50
view3d = context.space_data
51
region = view3d.region_3d
52
viewMatrix = region.view_matrix
53
rot = viewMatrix.rotation_part()
54
ob.rotation_euler = rot.invert().to_euler()
57
# Create a new mesh (object) from verts/edges/faces.
58
# verts/edges/faces ... List of vertices/edges/faces for the
59
# new mesh (as used in from_pydata).
60
# name ... Name of the new mesh (& object).
61
# edit ... Replace existing mesh data.
62
# Note: Using "edit" will destroy/delete existing mesh data.
63
def create_mesh_object(context, verts, edges, faces, name, edit):
65
obj_act = scene.objects.active
67
# Can't edit anything, unless we have an active obj.
68
if edit and not obj_act:
72
mesh = bpy.data.meshes.new(name)
74
# Make a mesh from a list of verts/edges/faces.
75
mesh.from_pydata(verts, edges, faces)
77
# Update mesh geometry after adding stuff.
80
# Deselect all objects.
81
bpy.ops.object.select_all(action='DESELECT')
84
# Replace geometry of existing object
86
# Use the active obj and select it.
90
if obj_act.mode == 'OBJECT':
91
# Get existing mesh datablock.
92
old_mesh = ob_new.data
94
# Set object data to nothing
97
# Clear users of existing mesh datablock.
100
# Remove old mesh datablock if no users are left.
101
if (old_mesh.users == 0):
102
bpy.data.meshes.remove(old_mesh)
104
# Assign new mesh datablock.
109
ob_new = bpy.data.objects.new(name, mesh)
111
# Link new object to the given scene and select it.
112
scene.objects.link(ob_new)
115
# Place the object at the 3D cursor location.
116
ob_new.location = scene.cursor_location
118
apply_object_align(context, ob_new)
120
if obj_act and obj_act.mode == 'EDIT':
122
# We are in EditMode, switch to ObjectMode.
123
bpy.ops.object.mode_set(mode='OBJECT')
125
# Select the active object as well.
126
obj_act.select = True
128
# Apply location of new object.
131
# Join new object into the active.
132
bpy.ops.object.join()
134
# Switching back to EditMode.
135
bpy.ops.object.mode_set(mode='EDIT')
140
# We are in ObjectMode.
141
# Make the new object the active one.
142
scene.objects.active = ob_new
147
# A very simple "bridge" tool.
148
# Connects two equally long vertex rows with faces.
149
# Returns a list of the new faces (list of lists)
151
# vertIdx1 ... First vertex list (list of vertex indices).
152
# vertIdx2 ... Second vertex list (list of vertex indices).
153
# closed ... Creates a loop (first & last are closed).
154
# flipped ... Invert the normal of the face(s).
156
# Note: You can set vertIdx1 to a single vertex index to create
157
# a fan/star of faces.
158
# Note: If both vertex idx list are the same length they have
159
# to have at least 2 vertices.
160
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
163
if not vertIdx1 or not vertIdx2:
166
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
170
if (len(vertIdx1) != len(vertIdx2)):
171
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
176
total = len(vertIdx2)
179
# Bridge the start with the end.
186
face.append(vertIdx1[total - 1])
190
face = [vertIdx2[0], vertIdx1[0]]
192
face.append(vertIdx1[total - 1])
193
face.append(vertIdx2[total - 1])
196
# Bridge the rest of the faces.
197
for num in range(total - 1):
200
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
202
face = [vertIdx2[num], vertIdx1[num],
203
vertIdx1[num + 1], vertIdx2[num + 1]]
207
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
209
face = [vertIdx1[num], vertIdx2[num],
210
vertIdx2[num + 1], vertIdx1[num + 1]]
39
from bpy_extras.object_utils import object_data_add
214
41
# this function creates a chain of quads and, when necessary, a remaining tri
215
42
# for each polygon created in this script. be aware though, that this function
216
43
# assumes each polygon is convex.
217
44
# poly: list of faces, or a single face, like those
218
45
# needed for mesh.from_pydata.
219
# returns the tesselated faces.
46
# returns the tessellated faces.
220
47
def createPolys(poly):
222
49
if len(poly) == 0:
333
150
# constants saving space and readability
337
noSnub = (snub == "0") or (etrunc == 0.5) or (etrunc == 0)
338
lSnub = (snub == "L") and (0 < etrunc < 0.5)
339
rSnub = (snub == "R") and (0 < etrunc < 0.5)
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)
343
160
if dual: # dual is as simple as another, but mirrored platonic solid
344
vInput,fInput = source(dualSource[plato])
345
supposed_size = Asum(vInput[i] for i in fInput[0]).length / len(fInput[0])
346
vInput = [-i*supposed_size for i in vInput] # mirror it
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
348
165
return source(plato)
349
# simple truncation of the source
350
elif 0.5 >= vtrunc > 0:
351
vInput,fInput = source(plato)
352
# truncation is now equal to simple truncation of the dual of the source
354
vInput,fInput = source(dualSource[plato])
355
supposed_size = Asum(vInput[i] for i in fInput[0]).length / len(fInput[0])
356
# account for the source being a dual
358
if vtrunc == 0: # no truncation
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
360
vInput,fInput = source(plato)
361
vInput = [i*supposed_size for i in vInput]
362
return vInput,fInput,sourceName
363
vInput = [-i*supposed_size for i in vInput]
366
# generate a database for creating the faces. this exists out of a list for
367
# every vertex in the source
369
# 1 : vertices connected to this vertex, listed ccw(Counter Clock Wise)
370
# 2 : vertices generated to form the faces of this vertex
371
# 3 : faces connected to this vertex, listed ccw
372
# 4 : dictionairy containing the verts used by the connected faces
373
# 5 : list of edges that use this vertex, listed ccw
374
# 6 : dictionairy containing the verts used by the connected edges
375
v = [[i,[],[],[],{},[],{}] for i in range(len(vInput))]
377
# this piece of code, generates the database and the lists in ccw order
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
378
184
for x in range(len(fInput)):
380
# in every faces, check which vertices connect the each vert and sort
382
for j in range(-1,len(i)-1):
383
# only generate an edge dict, if edge truncation is needed
385
# list edges as [min,max], to evade confusion
386
first = min([i[j-1],i[j]])
387
last = max([i[j-1],i[j]])
388
# if an edge is not allready in, add it and give the index
390
y = edges.index([first,last])
392
edges.append([first,last])
395
v[i[j]][6][str(y)] = [0,0]
396
# the vertex before and after the current vertex, check whether they
397
# are allready in the database
398
after = i[j+1] not in v[i[j]][1]
399
before = i[j-1] not in v[i[j]][1]
400
# sort them and add faces and, when necessary, edges in the database
403
v[i[j]][1].append(i[j+1])
404
v[i[j]][1].append(i[j-1])
406
if etrunc: v[i[j]][5].append(y)
408
z = v[i[j]][1].index(i[j-1])
409
v[i[j]][1].insert(z,i[j+1])
410
v[i[j]][3].insert(z,x)
411
if etrunc: v[i[j]][5].insert(z,y)
413
z = v[i[j]][1].index(i[j+1])
414
v[i[j]][3].insert(z,x)
415
if etrunc: v[i[j]][5].insert(z,y)
417
v[i[j]][1].insert(z+1,i[j-1])
418
# add the current face to the current vertex in the dict
419
v[i[j]][4][str(x)] = [0,0]
421
# generate vert-only truncated vertices by linear interpolation
423
for j in range(len(i[1])):
424
verts.append(vInput[i[0]]*(1-vtrunc)+vInput[i[1][j]]*vtrunc)
426
# face resulting from truncating this vertex
428
# this vertex is used by both faces using this edge
429
i[4][str(i[3][j])][1] = l
430
i[4][str(i[3][j-1])][0] = l
432
# only truncate edges when needed
435
# generate a new list of vertices, by linear interpolating each vert-face
439
# weird range so we dont run out of array bounds
440
for j in range(-1,len(i[2])-1):
441
# making use of the fact that the snub operation takes only
442
# one of the two vertices per edge. so rSnub only takes the
443
# first, lSnub only takes the second, and noSnub takes both
446
nVerts.append((1-etrunc)*verts[i[2][j]] + etrunc*verts[i[2][j-1]])
447
# add last vertex to the vert-face, face-face and edge-face
450
i[4][str(i[3][j-1])][0] = l
451
i[6][str(i[5][j-1])][1] = l
454
nVerts.append((1-etrunc)*verts[i[2][j]] + etrunc*verts[i[2][j+1]])
455
# add last vertex to the vert-face, face-face and edge-face
458
i[4][str(i[3][j])][1] = l
459
i[6][str(i[5][j-1])][0] = l
463
# snub operator creates 2 tri's instead of a planar quad, needing the
464
# next piece of code. making use of the dictionairy to create them.
467
for x in range(len(edges)):
468
one = v[edges[x][0]] # the first vertex of this edge
469
two = v[edges[x][1]] # the second
470
# using max() since the dict consists of one filled spot and one
471
# empty('cause only one vert is created)
472
f = [max(two[6][str(x)]),max(one[6][str(x)])]
473
index = one[5].index(x)
474
# create this tri from the middle line and the the previous edge
477
f.append(max(one[6][str(one[5][index-1])]))
478
else: # or in this case, the next
479
if index+1 >= len(one[5]): index = -1
480
f.append(max(one[6][str(one[5][index+1])]))
483
# do the same for the other end of the edge
484
f = [max(one[6][str(x)]),max(two[6][str(x)])]
485
index = two[5].index(x)
487
f.append(max(two[6][str(two[5][index-1])]))
489
if index+1 >= len(one[5]): index = -1
490
f.append(max(two[6][str(two[5][index+1])]))
493
# generate edge-faces from the dictionairy, simple quads for noSnub
495
for i in range(len(edges)):
498
f.extend(v[j][6][str(i)])
502
# generate vert-faces for non-edge-truncation
503
vert_faces = [i[2] for i in v]
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)
505
279
# calculate supposed vertex length to ensure continuity
507
supposed_size *= len(vert_faces[0])/Asum(verts[i] for i in vert_faces[0]).length
508
verts = [-i*supposed_size for i in verts]
510
# generate face-faces by looking up the old verts and replacing them with
511
# the vertices in the dictionairy
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]
513
286
for x in range(len(fInput)):
516
# again using the fact, that only one of the two verts is used
519
f.append(v[j][4][str(x)][0])
520
elif lSnub and etrunc:
521
f.append(v[j][4][str(x)][1])
523
# for cool graphics, comment the first line and uncomment the second line
524
# then work the vTrunc property, leave the other properties at 0
525
# (can also change 0 to 1 in second line to change from ccw to cw)
526
f.extend(v[j][4][str(x)]) # first
527
#f.append(v[j][4][str(x)][0]) # second
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])
531
# create verts by taking the average of all vertices that make up each
532
# face. do it in this order to ease the following face creation
535
nVerts.append(Asum(verts[j] for j in i)/len(i))
539
nVerts.append(Asum(verts[j] for j in i)/len(i))
542
nVerts.append(Asum(verts[j] for j in i)/len(i))
543
# the special face generation for snub duals, it sucks, even i dont get it
545
for x in range(len(fInput)):
547
for j in range(-1,len(i)-1):
550
eNext = edges.index([i[j+1],i[j]])
553
eNext = edges.index([i[j],i[j+1]])
556
ePrev = edges.index([i[j-1],i[j]])
559
ePrev = edges.index([i[j],i[j-1]])
562
f = [eStart+2*eNext+b,eStart+2*eNext+a,i[j]]
563
f.append(eStart+2*ePrev+d)
566
f = [eStart+2*ePrev+c,eStart+2*ePrev+d,i[j]]
567
f.append(eStart+2*eNext+a)
569
if supposed_size: faces.append(f)
570
else: faces.append(f[2:]+f[:2])
572
# for noSnub situations, the face generation is somewhat easier.
573
# first calculate what order faces must be added to ensure convex solids
574
# this by calculating the angle between the middle of the four vertices
575
# and the first face. if the face is above the middle, use that diagonal
576
# otherwise use the other diagonal
578
f = [v[0][0],eStart+v[0][5][-1],fStart+v[0][3][0],eStart+v[0][5][0]]
580
f = [v[0][0],fStart+v[0][3][0],v[0][1][0],fStart+v[0][3][-1]]
581
p = [nVerts[i] for i in f]
583
norm = (p[1]-p[0]).cross(p[2]-p[0])
584
dot = norm.dot(mid-p[0])/(norm.length*(mid-p[0]).length)
585
tollerance = 0.001 # ~ cos(0.06 degrees)
586
if ((dot > tollerance) and (not supposed_size)) or ((dot < -tollerance) and (supposed_size)):
587
direction = 1 # first diagonal
588
elif ((dot < -tollerance) and (not supposed_size)) or ((dot > tollerance) and (supposed_size)):
589
direction = -1 # second diagonal
591
direction = 0 # no diagonal, face is planar (somewhat)
593
if etrunc: # for every vertex
594
for i in v: # add the face, consisting of the vert,edge,next
595
# edge and face between those edges
596
for j in range(len(i[1])):
597
f = [i[0],eStart+i[5][j-1],fStart+i[3][j],eStart+i[5][j]]
598
if direction == 1: # first diagonal
599
faces.extend([[f[0],f[1],f[3]],[f[1],f[2],f[3]]])
600
elif direction == -1: # first diagonal
601
faces.extend([[f[0],f[1],f[2]],[f[0],f[2],f[3]]])
603
faces.append(f) # no diagonal
605
for i in v: # for every vertex
606
for j in range(len(i[1])):
607
if i[0] < i[1][j]: # face consists of vert, vert on other
608
# end of edge and both faces using that
609
# edge, so exclude verts allready used
610
f = [i[0],fStart+i[3][j], i[1][j],fStart+i[3][j-1]]
611
if direction == -1: # secong diagonal
612
faces.extend([[f[0],f[1],f[3]],[f[1],f[2],f[3]]])
613
elif direction == 1: # first diagonal
614
faces.extend([[f[0],f[1],f[2]],[f[0],f[2],f[3]]])
616
faces.append(f) # no diagonal
617
verts = nVerts # use new vertices
619
# concatenate all faces, since they dont have to be used sepperately anymore
621
if etrunc: faces += edge_faces
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
627
336
class Solids(bpy.types.Operator):
628
337
"""Add one of the (regular) solids (mesh)"""
629
338
bl_idname = "mesh.primitive_solid_add"
630
339
bl_label = "(Regular) solids"
631
bl_description = "Add one of the platoic or archimedean solids"
632
bl_options = {'REGISTER', 'UNDO'}
340
bl_description = "Add one of the Platonic, Archimedean or Catalan solids"
341
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
634
343
source = EnumProperty(items = (("4","Tetrahedron",""),
635
344
("6","Hexahedron",""),
700
409
("dt20","Pentakis Dodecahedron",""),
701
410
("db12","Deltoidal Hexecontahedron",""),
702
411
("dc12","Disdyakis Triacontahedron",""),
703
("ds12","Pentagonal Hexecontahedron",""),
705
("sb","Soccer ball","")),
412
("ds12","Pentagonal Hexecontahedron","")),
706
413
name = "Presets",
707
414
description = "Parameters for some hard names")
709
416
# actual preset values
710
p = {"t4":["4",2/3,0,0,"0"],
711
"r4":["4",1,1,0,"0"],
712
"t6":["6",2/3,0,0,"0"],
713
"t8":["8",2/3,0,0,"0"],
714
"b6":["6",1.0938,1,0,"0"],
715
"c6":["6",1.0572,0.585786,0,"0"],
716
"s6":["6",1.0875,0.704,0,"L"],
717
"r12":["12",1,0,0,"0"],
718
"t12":["12",2/3,0,0,"0"],
719
"t20":["20",2/3,0,0,"0"],
720
"b12":["12",1.1338,1,0,"0"],
721
"c12":["20",0.921,0.553,0,"0"],
722
"s12":["12",1.1235,0.68,0,"L"],
723
"dt4":["4",2/3,0,1,"0"],
724
"dr4":["4",1,2/3,1,"0"],
725
"dt6":["6",4/3,0,1,"0"],
726
"dt8":["8",1,0,1,"0"],
727
"db6":["6",1.0938,0.756,1,"0"],
728
"dc6":["6",1,1,1,"0"],
729
"ds6":["6",1.0875,0.704,1,"L"],
730
"dr12":["12",1.54,0,1,"0"],
731
"dt12":["12",5/3,0,1,"0"],
732
"dt20":["20",2/3,0,1,"0"],
733
"db12":["12",1,0.912,1,"0"],
734
"dc12":["20",0.921,1,1,"0"],
735
"ds12":["12",1.1235,0.68,1,"L"],
737
"sb":["20",2/3,0,0,"0"]}
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"]}
739
edit = BoolProperty(name="",
444
#previous preset, for User-friendly reasons
744
447
def execute(self,context):
745
# turn off undo for better performance (3 - 5x faster), also makes sure
448
# turn off undo for better performance (3-5x faster), also makes sure
746
449
# that mesh ops are undoable and entire script acts as one operator
747
450
bpy.context.user_preferences.edit.use_global_undo = False
750
#if preset, set preset
452
# piece of code to make presets remain until parameters are changed
751
453
if self.preset != "0":
752
using = self.p[self.preset]
753
self.source = using[0]
754
self.vTrunc = using[1]
755
self.eTrunc = using[2]
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
761
verts,faces = createSolid(self.source,
481
verts,faces = createSolid(self.source,
767
487
# turn n-gons in quads and tri's
768
488
faces = createPolys(faces)
770
490
# resize to normal size, or if keepSize, make sure all verts are of length 'size'
771
491
if self.keepSize:
772
rad = self.size/verts[0].length
492
rad = self.size/verts[-1 if self.dual else 0].length
773
493
else: rad = self.size
774
494
verts = [i*rad for i in verts]
776
496
# generate object
777
obj = create_mesh_object(context,verts,[],faces,"Solid",self.edit)
779
# vertices will be on top of each other in some cases,
780
# so remove doubles then
781
if ((self.vTrunc == 1) and (self.eTrunc == 0)) or (self.eTrunc == 1):
782
current_mode = obj.mode
783
if current_mode == 'OBJECT':
784
bpy.ops.object.mode_set(mode='EDIT')
785
bpy.ops.mesh.select_all(action='SELECT')
786
bpy.ops.mesh.remove_doubles()
787
bpy.ops.object.mode_set(mode=current_mode)
789
# snub duals suck, so make all normals point outwards
790
if self.dual and (self.snub != "0"):
791
current_mode = obj.mode
792
if current_mode == 'OBJECT':
793
bpy.ops.object.mode_set(mode='EDIT')
794
bpy.ops.mesh.select_all(action='SELECT')
795
bpy.ops.mesh.normals_make_consistent()
796
bpy.ops.object.mode_set(mode=current_mode)
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
798
509
# turn undo back on
799
510
bpy.context.user_preferences.edit.use_global_undo = True