From 23fc6a347f64ca7220c028ee99289b94d023a3bb Mon Sep 17 00:00:00 2001 From: Gerwin Damsteegt <gjdamsteegt@gmail.com> Date: Tue, 26 Apr 2011 21:14:37 +0000 Subject: [PATCH] first commit, lets see if it works updating regular solids add mesh script to version 2 --- add_mesh_solid.py | 747 +++++++++++++++++----------------------------- 1 file changed, 268 insertions(+), 479 deletions(-) diff --git a/add_mesh_solid.py b/add_mesh_solid.py index 2875934cd..a1204ecc2 100644 --- a/add_mesh_solid.py +++ b/add_mesh_solid.py @@ -8,7 +8,7 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License @@ -20,10 +20,10 @@ bl_info = { "name": "Regular Solids", "author": "DreamPainter", - "version": (1, 0, 1), + "version": (2, 0), "blender": (2, 5, 7), - "api": 35853, - "location": "View3D > Add > Mesh > Regular Solids", + "api": 36336, + "location": "View3D > Add > Mesh > Solids", "description": "Add a Regular Solid mesh.", "warning": "", "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\ @@ -32,102 +32,13 @@ bl_info = { "func=detail&aid=22405", "category": "Add Mesh"} - import bpy from bpy.props import FloatProperty,EnumProperty,BoolProperty from math import sqrt from mathutils import Vector,Matrix -#from rawMeshUtils import * from functools import reduce +from add_object_utils import object_data_add -# Create a new mesh (object) from verts/edges/faces. -# verts/edges/faces ... List of vertices/edges/faces for the -# new mesh (as used in from_pydata). -# name ... Name of the new mesh (& object). -def create_mesh_object(context, verts, edges, faces, name): - scene = context.scene - obj_act = scene.objects.active - - # Create new mesh - mesh = bpy.data.meshes.new(name) - - # Make a mesh from a list of verts/edges/faces. - mesh.from_pydata(verts, edges, faces) - - # Update mesh geometry after adding stuff. - mesh.update() - - import add_object_utils - return add_object_utils.object_data_add(context, mesh, operator=None) - - -# A very simple "bridge" tool. -# Connects two equally long vertex rows with faces. -# Returns a list of the new faces (list of lists) -# -# vertIdx1 ... First vertex list (list of vertex indices). -# vertIdx2 ... Second vertex list (list of vertex indices). -# closed ... Creates a loop (first & last are closed). -# flipped ... Invert the normal of the face(s). -# -# Note: You can set vertIdx1 to a single vertex index to create -# a fan/star of faces. -# Note: If both vertex idx list are the same length they have -# to have at least 2 vertices. -def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False): - faces = [] - - if not vertIdx1 or not vertIdx2: - return None - - if len(vertIdx1) < 2 and len(vertIdx2) < 2: - return None - - fan = False - if (len(vertIdx1) != len(vertIdx2)): - if (len(vertIdx1) == 1 and len(vertIdx2) > 1): - fan = True - else: - return None - - total = len(vertIdx2) - - if closed: - # Bridge the start with the end. - if flipped: - face = [ - vertIdx1[0], - vertIdx2[0], - vertIdx2[total - 1]] - if not fan: - face.append(vertIdx1[total - 1]) - faces.append(face) - - else: - face = [vertIdx2[0], vertIdx1[0]] - if not fan: - face.append(vertIdx1[total - 1]) - face.append(vertIdx2[total - 1]) - faces.append(face) - - # Bridge the rest of the faces. - for num in range(total - 1): - if flipped: - if fan: - face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]] - else: - face = [vertIdx2[num], vertIdx1[num], - vertIdx1[num + 1], vertIdx2[num + 1]] - faces.append(face) - else: - if fan: - face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]] - else: - face = [vertIdx1[num], vertIdx2[num], - vertIdx2[num + 1], vertIdx1[num + 1]] - faces.append(face) - - return faces # this function creates a chain of quads and, when necessary, a remaining tri # for each polygon created in this script. be aware though, that this function # assumes each polygon is convex. @@ -143,28 +54,26 @@ def createPolys(poly): poly = [poly] # if only one, make it a list of one face faces = [] for i in poly: - l = len(i) + L = len(i) # let all faces of 3 or 4 verts be - if l < 5: + if L < 5: faces.append(i) # split all polygons in half and bridge the two halves else: - half = int(l/2) - f = createFaces(i[:half],[i[-1-j] for j in range(half)]) + f = [[i[x],i[x+1],i[L-2-x],i[L-1-x]] for x in range(L//2-1)] faces.extend(f) - # if the polygon has an odd number of verts, add the last tri - if l%2 == 1: - faces.append([i[half-1],i[half],i[half+1]]) + if L&1 == 1: + faces.append([i[L//2-1+x] for x in [0,1,2]]) return faces - + # function to make the reduce function work as a workaround to sum a list of vectors -def Asum(list): +def vSum(list): return reduce(lambda a,b: a+b, list) - + # creates the 5 platonic solids as a base for the rest # plato: should be one of {"4","6","8","12","20"}. decides what solid the # outcome will be. -# returns a list of vertices and faces and the appropriate name +# returns a list of vertices and faces def source(plato): verts = [] faces = [] @@ -225,16 +134,11 @@ def source(plato): [0,10,8],[1,8,10],[2,9,11],[3,11,9],[4,2,0],[5,0,2],[6,1,3],[7,3,1], [8,6,4],[9,4,6],[10,5,7],[11,7,5]] - # handles faulty values of plato - else: - print("Choose keyword 'plato' from {'4','6','8','12','20'}") - return None - # convert the tuples to Vectors verts = [Vector(i) for i in v] return verts,faces - + # processes the raw data from source def createSolid(plato,vtrunc,etrunc,dual,snub): verts = [] @@ -250,304 +154,194 @@ def createSolid(plato,vtrunc,etrunc,dual,snub): # constants saving space and readability vtrunc *= 0.5 etrunc *= 0.5 - supposed_size = 0 - noSnub = (snub == "0") or (etrunc == 0.5) or (etrunc == 0) - lSnub = (snub == "L") and (0 < etrunc < 0.5) - rSnub = (snub == "R") and (0 < etrunc < 0.5) + supposedSize = 0 + noSnub = (snub == "None") or (etrunc == 0.5) or (etrunc == 0) + lSnub = (snub == "Left") and (0 < etrunc < 0.5) + rSnub = (snub == "Right") and (0 < etrunc < 0.5) # no truncation if vtrunc == 0: if dual: # dual is as simple as another, but mirrored platonic solid - vInput,fInput = source(dualSource[plato]) - supposed_size = Asum(vInput[i] for i in fInput[0]).length / len(fInput[0]) - vInput = [-i*supposed_size for i in vInput] # mirror it - return vInput,fInput + vInput, fInput = source(dualSource[plato]) + supposedSize = vSum(vInput[i] for i in fInput[0]).length/len(fInput[0]) + vInput = [-i*supposedSize for i in vInput] # mirror it + return vInput, fInput return source(plato) - # simple truncation of the source - elif 0.5 >= vtrunc > 0: - vInput,fInput = source(plato) - # truncation is now equal to simple truncation of the dual of the source - elif vtrunc > 0.5: - vInput,fInput = source(dualSource[plato]) - supposed_size = Asum(vInput[i] for i in fInput[0]).length / len(fInput[0]) - # account for the source being a dual - vtrunc = 1-vtrunc - if vtrunc == 0: # no truncation + elif 0 < vtrunc <= 0.5: # simple truncation of the source + vInput, fInput = source(plato) + else: + # truncation is now equal to simple truncation of the dual of the source + vInput, fInput = source(dualSource[plato]) + supposedSize = vSum(vInput[i] for i in fInput[0]).length / len(fInput[0]) + vtrunc = 1-vtrunc # account for the source being a dual + if vtrunc == 0: # no truncation needed if dual: - vInput,fInput = source(plato) - vInput = [i*supposed_size for i in vInput] - return vInput,fInput#,sourceName - #JayDez - I don't know what sourceName is, but commenting that - #part out fixes vert truncation problems. - vInput = [-i*supposed_size for i in vInput] - return vInput,fInput - - # generate a database for creating the faces. this exists out of a list for - # every vertex in the source - # 0 : vertex id - # 1 : vertices connected to this vertex, listed ccw(Counter Clock Wise) - # 2 : vertices generated to form the faces of this vertex - # 3 : faces connected to this vertex, listed ccw - # 4 : dictionairy containing the verts used by the connected faces - # 5 : list of edges that use this vertex, listed ccw - # 6 : dictionairy containing the verts used by the connected edges - v = [[i,[],[],[],{},[],{}] for i in range(len(vInput))] - - # this piece of code, generates the database and the lists in ccw order + vInput, fInput = source(plato) + vInput = [i*supposedSize for i in vInput] + return vInput, fInput + vInput = [-i*supposedSize for i in vInput] + return vInput, fInput + + # generate connection database + vDict = [{} for i in vInput] + # for every face, store what vertex comes after and before the current vertex for x in range(len(fInput)): i = fInput[x] - # in every faces, check which vertices connect the each vert and sort - # in ccw order - for j in range(-1,len(i)-1): - # only generate an edge dict, if edge truncation is needed - if etrunc: - # list edges as [min,max], to evade confusion - first = min([i[j-1],i[j]]) - last = max([i[j-1],i[j]]) - # if an edge is not allready in, add it and give the index - try: - y = edges.index([first,last]) - except: - edges.append([first,last]) - y = len(edges)-1 - # add a dict item - v[i[j]][6][str(y)] = [0,0] - # the vertex before and after the current vertex, check whether they - # are allready in the database - after = i[j+1] not in v[i[j]][1] - before = i[j-1] not in v[i[j]][1] - # sort them and add faces and, when necessary, edges in the database - if after: - if before: - v[i[j]][1].append(i[j+1]) - v[i[j]][1].append(i[j-1]) - v[i[j]][3].append(x) - if etrunc: v[i[j]][5].append(y) - else: - z = v[i[j]][1].index(i[j-1]) - v[i[j]][1].insert(z,i[j+1]) - v[i[j]][3].insert(z,x) - if etrunc: v[i[j]][5].insert(z,y) - else: - z = v[i[j]][1].index(i[j+1]) - v[i[j]][3].insert(z,x) - if etrunc: v[i[j]][5].insert(z,y) - if before: - v[i[j]][1].insert(z+1,i[j-1]) - # add the current face to the current vertex in the dict - v[i[j]][4][str(x)] = [0,0] - - # generate vert-only truncated vertices by linear interpolation - for i in v: - for j in range(len(i[1])): - verts.append(vInput[i[0]]*(1-vtrunc)+vInput[i[1][j]]*vtrunc) - l = len(verts)-1 - # face resulting from truncating this vertex - i[2].append(l) - # this vertex is used by both faces using this edge - i[4][str(i[3][j])][1] = l - i[4][str(i[3][j-1])][0] = l - - # only truncate edges when needed - vert_faces = [] - if etrunc: - # generate a new list of vertices, by linear interpolating each vert-face - nVerts = [] - for i in v: - f = [] - # weird range so we dont run out of array bounds - for j in range(-1,len(i[2])-1): - # making use of the fact that the snub operation takes only - # one of the two vertices per edge. so rSnub only takes the - # first, lSnub only takes the second, and noSnub takes both - if rSnub or noSnub: - # interpolate - nVerts.append((1-etrunc)*verts[i[2][j]] + etrunc*verts[i[2][j-1]]) - # add last vertex to the vert-face, face-face and edge-face - l = len(nVerts)-1 - f.append(l) - i[4][str(i[3][j-1])][0] = l - i[6][str(i[5][j-1])][1] = l - if lSnub or noSnub: - # interpolate - nVerts.append((1-etrunc)*verts[i[2][j]] + etrunc*verts[i[2][j+1]]) - # add last vertex to the vert-face, face-face and edge-face - l = len(nVerts)-1 - f.append(l) - i[4][str(i[3][j])][1] = l - i[6][str(i[5][j-1])][0] = l - # add vert-face - vert_faces.append(f) - - # snub operator creates 2 tri's instead of a planar quad, needing the - # next piece of code. making use of the dictionairy to create them. - if lSnub or rSnub: - edge_faces = [] - for x in range(len(edges)): - one = v[edges[x][0]] # the first vertex of this edge - two = v[edges[x][1]] # the second - # using max() since the dict consists of one filled spot and one - # empty('cause only one vert is created) - f = [max(two[6][str(x)]),max(one[6][str(x)])] - index = one[5].index(x) - # create this tri from the middle line and the the previous edge - # on this vertex - if lSnub: - f.append(max(one[6][str(one[5][index-1])])) - else: # or in this case, the next - if index+1 >= len(one[5]): index = -1 - f.append(max(one[6][str(one[5][index+1])])) - edge_faces.append(f) - - # do the same for the other end of the edge - f = [max(one[6][str(x)]),max(two[6][str(x)])] - index = two[5].index(x) - if lSnub: - f.append(max(two[6][str(two[5][index-1])])) + for j in range(len(i)): + vDict[i[j-1]][i[j]] = [i[j-2],x] + if len(vDict[i[j-1]]) == 1: vDict[i[j-1]][-1] = i[j] + + # the actual connection database: exists out of: + # [vtrunc pos, etrunc pos, connected vert IDs, connected face IDs] + vData = [[[],[],[],[]] for i in vInput] + fvOutput = [] # faces created from truncated vertices + feOutput = [] # faces created from truncated edges + vOutput = [] # newly created vertices + for x in range(len(vInput)): + i = vDict[x] # lookup the current vertex + current = i[-1] + while True: # follow the chain to get a ccw order of connected verts and faces + vData[x][2].append(i[current][0]) + vData[x][3].append(i[current][1]) + # create truncated vertices + vData[x][0].append((1-vtrunc)*vInput[x] + vtrunc*vInput[vData[x][2][-1]]) + current = i[current][0] + if current == i[-1]: break # if we're back at the first: stop the loop + fvOutput.append([]) # new face from truncated vert + fOffset = x*(len(i)-1) # where to start off counting faceVerts + # only create one vert where one is needed (v1 todo: done) + if etrunc == 0.5: + for j in range(len(i)-1): + vOutput.append((vData[x][0][j]+vData[x][0][j-1])*etrunc) # create vert + fvOutput[x].append(fOffset+j) # add to face + fvOutput[x] = fvOutput[x][1:]+[fvOutput[x][0]] # rotate face for ease later on + # create faces from truncated edges. + for j in range(len(i)-1): + if x > vData[x][2][j]: #only create when other vertex has been added + index = vData[vData[x][2][j]][2].index(x) + feOutput.append([fvOutput[x][j],fvOutput[x][j-1], + fvOutput[vData[x][2][j]][index], + fvOutput[vData[x][2][j]][index-1]]) + # edge truncation between none and full + elif etrunc > 0: + for j in range(len(i)-1): + # create snubs from selecting verts from rectified meshes + if rSnub: + vOutput.append(etrunc*vData[x][0][j]+(1-etrunc)*vData[x][0][j-1]) + fvOutput[x].append(fOffset+j) + elif lSnub: + vOutput.append((1-etrunc)*vData[x][0][j]+etrunc*vData[x][0][j-1]) + fvOutput[x].append(fOffset+j) + else: #noSnub, select both verts from rectified mesh + vOutput.append(etrunc*vData[x][0][j]+(1-etrunc)*vData[x][0][j-1]) + vOutput.append((1-etrunc)*vData[x][0][j]+etrunc*vData[x][0][j-1]) + fvOutput[x].append(2*fOffset+2*j) + fvOutput[x].append(2*fOffset+2*j+1) + # rotate face for ease later on + if noSnub: fvOutput[x] = fvOutput[x][2:]+fvOutput[x][:2] + else: fvOutput[x] = fvOutput[x][1:]+[fvOutput[x][0]] + # create single face for each edge + if noSnub: + for j in range(len(i)-1): + if x > vData[x][2][j]: + index = vData[vData[x][2][j]][2].index(x) + feOutput.append([fvOutput[x][j*2],fvOutput[x][2*j-1], + fvOutput[vData[x][2][j]][2*index], + fvOutput[vData[x][2][j]][2*index-1]]) + # create 2 tri's for each edge for the snubs + elif rSnub: + for j in range(len(i)-1): + if x > vData[x][2][j]: + index = vData[vData[x][2][j]][2].index(x) + feOutput.append([fvOutput[x][j],fvOutput[x][j-1], + fvOutput[vData[x][2][j]][index]]) + feOutput.append([fvOutput[x][j],fvOutput[vData[x][2][j]][index], + fvOutput[vData[x][2][j]][index-1]]) + elif lSnub: + for j in range(len(i)-1): + if x > vData[x][2][j]: + index = vData[vData[x][2][j]][2].index(x) + feOutput.append([fvOutput[x][j],fvOutput[x][j-1], + fvOutput[vData[x][2][j]][index-1]]) + feOutput.append([fvOutput[x][j-1],fvOutput[vData[x][2][j]][index], + fvOutput[vData[x][2][j]][index-1]]) + # special rules fro birectified mesh (v1 todo: done) + elif vtrunc == 0.5: + for j in range(len(i)-1): + if x < vData[x][2][j]: # use current vert, since other one has not passed yet + vOutput.append(vData[x][0][j]) + fvOutput[x].append(len(vOutput)-1) else: - if index+1 >= len(one[5]): index = -1 - f.append(max(two[6][str(two[5][index+1])])) - edge_faces.append(f) - else: - # generate edge-faces from the dictionairy, simple quads for noSnub - edge_faces = [] - for i in range(len(edges)): - f = [] - for j in edges[i]: - f.extend(v[j][6][str(i)]) - edge_faces.append(f) - verts = nVerts - else: - # generate vert-faces for non-edge-truncation - vert_faces = [i[2] for i in v] + # search for other edge to avoid duplicity + connectee = vData[x][2][j] + fvOutput[x].append(fvOutput[connectee][vData[connectee][2].index(x)]) + else: # vert truncation only + vOutput.extend(vData[x][0]) # use generated verts from way above + for j in range(len(i)-1): # create face from them + fvOutput[x].append(fOffset+j) # calculate supposed vertex length to ensure continuity - if supposed_size: - supposed_size *= len(vert_faces[0])/Asum(verts[i] for i in vert_faces[0]).length - verts = [-i*supposed_size for i in verts] - - # generate face-faces by looking up the old verts and replacing them with - # the vertices in the dictionairy - face_faces = [] + if supposedSize and not dual: # this to make the vtrunc > 1 work + supposedSize *= len(fvOutput[0])/vSum(vOutput[i] for i in fvOutput[0]).length + vOutput = [-i*supposedSize for i in vOutput] + + # create new faces by replacing old vert IDs by newly generated verts + ffOutput = [[] for i in fInput] for x in range(len(fInput)): - f = [] - for j in fInput[x]: - # again using the fact, that only one of the two verts is used - # for snub operation - if rSnub and etrunc: - f.append(v[j][4][str(x)][0]) - elif lSnub and etrunc: - f.append(v[j][4][str(x)][1]) - else: - # for cool graphics, comment the first line and uncomment the second line - # then work the vTrunc property, leave the other properties at 0 - # (can also change 0 to 1 in second line to change from ccw to cw) - f.extend(v[j][4][str(x)]) # first - #f.append(v[j][4][str(x)][0]) # second - face_faces.append(f) + # only one generated vert per vertex, so choose accordingly + if etrunc == 0.5 or (etrunc == 0 and vtrunc == 0.5) or lSnub or rSnub: + ffOutput[x] = [fvOutput[i][vData[i][3].index(x)-1] for i in fInput[x]] + # two generated verts per vertex + elif etrunc > 0: + for i in fInput[x]: + ffOutput[x].append(fvOutput[i][2*vData[i][3].index(x)-1]) + ffOutput[x].append(fvOutput[i][2*vData[i][3].index(x)-2]) + else: # cutting off corners also makes 2 verts + for i in fInput[x]: + ffOutput[x].append(fvOutput[i][vData[i][3].index(x)]) + ffOutput[x].append(fvOutput[i][vData[i][3].index(x)-1]) - if dual: - # create verts by taking the average of all vertices that make up each - # face. do it in this order to ease the following face creation - nVerts = [] - for i in vert_faces: - nVerts.append(Asum(verts[j] for j in i)/len(i)) - if etrunc: - eStart = len(nVerts) - for i in edge_faces: - nVerts.append(Asum(verts[j] for j in i)/len(i)) - fStart = len(nVerts) - for i in face_faces: - nVerts.append(Asum(verts[j] for j in i)/len(i)) - # the special face generation for snub duals, it sucks, even i dont get it - if lSnub or rSnub: - for x in range(len(fInput)): - i = fInput[x] - for j in range(-1,len(i)-1): - - if i[j] > i[j+1]: - eNext = edges.index([i[j+1],i[j]]) - [a,b] = [1,0] - else: - eNext = edges.index([i[j],i[j+1]]) - [a,b] = [0,1] - if i[j] > i[j-1]: - ePrev = edges.index([i[j-1],i[j]]) - [c,d] = [0,1] - else: - ePrev = edges.index([i[j],i[j-1]]) - [c,d] = [1,0] - if lSnub: - f = [eStart+2*eNext+b,eStart+2*eNext+a,i[j]] - f.append(eStart+2*ePrev+d) - f.append(fStart + x) - else: - f = [eStart+2*ePrev+c,eStart+2*ePrev+d,i[j]] - f.append(eStart+2*eNext+a) - f.append(fStart + x) - if supposed_size: faces.append(f) - else: faces.append(f[2:]+f[:2]) - else: - # for noSnub situations, the face generation is somewhat easier. - # first calculate what order faces must be added to ensure convex solids - # this by calculating the angle between the middle of the four vertices - # and the first face. if the face is above the middle, use that diagonal - # otherwise use the other diagonal - if etrunc: - f = [v[0][0],eStart+v[0][5][-1],fStart+v[0][3][0],eStart+v[0][5][0]] - else: - f = [v[0][0],fStart+v[0][3][0],v[0][1][0],fStart+v[0][3][-1]] - p = [nVerts[i] for i in f] - mid = 0.25*Asum(p) - norm = (p[1]-p[0]).cross(p[2]-p[0]) - dot = norm.dot(mid-p[0])/(norm.length*(mid-p[0]).length) - tollerance = 0.001 # ~ cos(0.06 degrees) - if ((dot > tollerance) and (not supposed_size)) or ((dot < -tollerance) and (supposed_size)): - direction = 1 # first diagonal - elif ((dot < -tollerance) and (not supposed_size)) or ((dot > tollerance) and (supposed_size)): - direction = -1 # second diagonal - else: - direction = 0 # no diagonal, face is planar (somewhat) + if not dual: + return vOutput,fvOutput + feOutput + ffOutput + else: + # do the same procedure as above, only now on the generated mesh + # generate connection database + vDict = [{} for i in vOutput] + dvOutput = [0 for i in fvOutput + feOutput + ffOutput] + dfOutput = [] - if etrunc: # for every vertex - for i in v: # add the face, consisting of the vert,edge,next - # edge and face between those edges - for j in range(len(i[1])): - f = [i[0],eStart+i[5][j-1],fStart+i[3][j],eStart+i[5][j]] - if direction == 1: # first diagonal - faces.extend([[f[0],f[1],f[3]],[f[1],f[2],f[3]]]) - elif direction == -1: # first diagonal - faces.extend([[f[0],f[1],f[2]],[f[0],f[2],f[3]]]) - else: - faces.append(f) # no diagonal - else: - for i in v: # for every vertex - for j in range(len(i[1])): - if i[0] < i[1][j]: # face consists of vert, vert on other - # end of edge and both faces using that - # edge, so exclude verts allready used - f = [i[0],fStart+i[3][j], i[1][j],fStart+i[3][j-1]] - if direction == -1: # secong diagonal - faces.extend([[f[0],f[1],f[3]],[f[1],f[2],f[3]]]) - elif direction == 1: # first diagonal - faces.extend([[f[0],f[1],f[2]],[f[0],f[2],f[3]]]) - else: - faces.append(f) # no diagonal - verts = nVerts # use new vertices - else: - # concatenate all faces, since they dont have to be used sepperately anymore - faces = face_faces - if etrunc: faces += edge_faces - faces += vert_faces - - return verts,faces - + for x in range(len(dvOutput)): # for every face + i = (fvOutput + feOutput + ffOutput)[x] # choose face to work with + # find vertex from face + normal = (vOutput[i[0]]-vOutput[i[1]]).cross(vOutput[i[2]]-vOutput[i[1]]).normalized() + dvOutput[x] = normal/(normal.dot(vOutput[i[0]])) + for j in range(len(i)): # create vert chain + vDict[i[j-1]][i[j]] = [i[j-2],x] + if len(vDict[i[j-1]]) == 1: vDict[i[j-1]][-1] = i[j] + + # calculate supposed size for continuity + supposedSize = vSum([vInput[i] for i in fInput[0]]).length/len(fInput[0]) + supposedSize /= dvOutput[-1].length + dvOutput = [i*supposedSize for i in dvOutput] + # use chains to create faces + for x in range(len(vOutput)): + i = vDict[x] + current = i[-1] + face = [] + while True: + face.append(i[current][1]) + current = i[current][0] + if current == i[-1]: break + dfOutput.append(face) + + return dvOutput,dfOutput + class Solids(bpy.types.Operator): """Add one of the (regular) solids (mesh)""" bl_idname = "mesh.primitive_solid_add" bl_label = "(Regular) solids" - bl_description = "Add one of the platoic or archimedean solids" + bl_description = "Add one of the Platonic, Archimedean or Catalan solids" bl_options = {'REGISTER', 'UNDO'} source = EnumProperty(items = (("4","Tetrahedron",""), @@ -566,7 +360,7 @@ class Solids(bpy.types.Operator): default = 1.0) vTrunc = FloatProperty(name = "Vertex Truncation", description = "Ammount of vertex truncation", - min = 0.001, + min = 0.0, soft_min = 0.0, max = 2.0, soft_max = 2.0, @@ -582,9 +376,9 @@ class Solids(bpy.types.Operator): default = 0.0, precision = 3, step = 0.2) - snub = EnumProperty(items = (("0","No Snub",""), - ("L","Left Snub",""), - ("R","Right Snub","")), + snub = EnumProperty(items = (("None","No Snub",""), + ("Left","Left Snub",""), + ("Right","Right Snub","")), name = "Snub", description = "Create the snub version") dual = BoolProperty(name="Dual", @@ -610,7 +404,7 @@ class Solids(bpy.types.Operator): ("dt4","Triakis Tetrahedron",""), ("dr4","Rhombic Dodecahedron",""), ("dt6","Triakis Octahedron",""), - ("dt8","Triakis Hexahedron",""), + ("dt8","Tetrakis Hexahedron",""), ("db6","Deltoidal Icositetrahedron",""), ("dc6","Disdyakis Dodecahedron",""), ("ds6","Pentagonal Icositetrahedron",""), @@ -619,95 +413,103 @@ class Solids(bpy.types.Operator): ("dt20","Pentakis Dodecahedron",""), ("db12","Deltoidal Hexecontahedron",""), ("dc12","Disdyakis Triacontahedron",""), - ("ds12","Pentagonal Hexecontahedron",""), - ("c","Cube",""), - ("sb","Soccer ball","")), + ("ds12","Pentagonal Hexecontahedron","")), name = "Presets", description = "Parameters for some hard names") # actual preset values - p = {"t4":["4",2/3,0,0,"0"], - "r4":["4",1,1,0,"0"], - "t6":["6",2/3,0,0,"0"], - "t8":["8",2/3,0,0,"0"], - "b6":["6",1.0938,1,0,"0"], - "c6":["6",1.0572,0.585786,0,"0"], - "s6":["6",1.0875,0.704,0,"L"], - "r12":["12",1,0,0,"0"], - "t12":["12",2/3,0,0,"0"], - "t20":["20",2/3,0,0,"0"], - "b12":["12",1.1338,1,0,"0"], - "c12":["20",0.921,0.553,0,"0"], - "s12":["12",1.1235,0.68,0,"L"], - "dt4":["4",2/3,0,1,"0"], - "dr4":["4",1,2/3,1,"0"], - "dt6":["6",4/3,0,1,"0"], - "dt8":["8",1,0,1,"0"], - "db6":["6",1.0938,0.756,1,"0"], - "dc6":["6",1,1,1,"0"], - "ds6":["6",1.0875,0.704,1,"L"], - "dr12":["12",1.54,0,1,"0"], - "dt12":["12",5/3,0,1,"0"], - "dt20":["20",2/3,0,1,"0"], - "db12":["12",1,0.912,1,"0"], - "dc12":["20",0.921,1,1,"0"], - "ds12":["12",1.1235,0.68,1,"L"], - "c":["6",0,0,0,"0"], - "sb":["20",2/3,0,0,"0"]} + p = {"t4":["4",2/3,0,0,"None"], + "r4":["4",1,1,0,"None"], + "t6":["6",2/3,0,0,"None"], + "t8":["8",2/3,0,0,"None"], + "b6":["6",1.0938,1,0,"None"], + "c6":["6",1.0572,0.585786,0,"None"], + "s6":["6",1.0875,0.704,0,"Left"], + "r12":["12",1,0,0,"None"], + "t12":["12",2/3,0,0,"None"], + "t20":["20",2/3,0,0,"None"], + "b12":["12",1.1338,1,0,"None"], + "c12":["20",0.921,0.553,0,"None"], + "s12":["12",1.1235,0.68,0,"Left"], + "dt4":["4",2/3,0,1,"None"], + "dr4":["4",1,1,1,"None"], + "dt6":["6",2/3,0,1,"None"], + "dt8":["8",2/3,0,1,"None"], + "db6":["6",1.0938,1,1,"None"], + "dc6":["6",1.0572,0.585786,1,"None"], + "ds6":["6",1.0875,0.704,1,"Left"], + "dr12":["12",1,0,1,"None"], + "dt12":["12",2/3,0,1,"None"], + "dt20":["20",2/3,0,1,"None"], + "db12":["12",1.1338,1,1,"None"], + "dc12":["20",0.921,0.553,1,"None"], + "ds12":["12",1.1235,0.68,1,"Left"]} + + #previous preset, for User-friendly reasons + previousSetting = "" def execute(self,context): - # turn off undo for better performance (3 - 5x faster), also makes sure + # turn off undo for better performance (3-5x faster), also makes sure # that mesh ops are undoable and entire script acts as one operator bpy.context.user_preferences.edit.use_global_undo = False - - #if preset, set preset + # piece of code to make presets remain until parameters are changed if self.preset != "0": - using = self.p[self.preset] - self.source = using[0] - self.vTrunc = using[1] - self.eTrunc = using[2] - self.dual = using[3] - self.snub = using[4] - self.preset = "0" + #if preset, set preset + if self.previousSetting != self.preset: + using = self.p[self.preset] + self.source = using[0] + self.vTrunc = using[1] + self.eTrunc = using[2] + self.dual = using[3] + self.snub = using[4] + else: + using = self.p[self.preset] + result0 = self.source == using[0] + result1 = abs(self.vTrunc - using[1]) < 0.004 + result2 = abs(self.eTrunc - using[2]) < 0.0015 + result4 = using[4] == self.snub or ((using[4] == "Left") and + self.snub in ["Left","Right"]) + if (result0 and result1 and result2 and result4): + if self.p[self.previousSetting][3] != self.dual: + if self.preset[0] == "d": + self.preset = self.preset[1:] + else: + self.preset = "d" + self.preset + else: + self.preset = "0" + self.previousSetting = self.preset + # generate mesh - verts,faces = createSolid(self.source, - self.vTrunc, - self.eTrunc, - self.dual, - self.snub) + verts,faces = createSolid(self.source, + self.vTrunc, + self.eTrunc, + self.dual, + self.snub) # turn n-gons in quads and tri's faces = createPolys(faces) # resize to normal size, or if keepSize, make sure all verts are of length 'size' if self.keepSize: - rad = self.size/verts[0].length + if dual: rad = self.size/verts[-1].length + else: rad = self.size/verts[0].length else: rad = self.size verts = [i*rad for i in verts] # generate object - obj = create_mesh_object(context,verts,[],faces,"Solid") - - # vertices will be on top of each other in some cases, - # so remove doubles then - if ((self.vTrunc == 1) and (self.eTrunc == 0)) or (self.eTrunc == 1): - current_mode = context.active_object.mode - if current_mode == 'OBJECT': - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.remove_doubles() - bpy.ops.object.mode_set(mode=current_mode) - - # snub duals suck, so make all normals point outwards - #if self.dual and (self.snub != "0"): - current_mode = context.active_object.mode - if current_mode == 'OBJECT': - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.normals_make_consistent() - bpy.ops.object.mode_set(mode=current_mode) + # Create new mesh + mesh = bpy.data.meshes.new("Solid") + + # Make a mesh from a list of verts/edges/faces. + mesh.from_pydata(verts, [], faces) + + # Update mesh geometry after adding stuff. + mesh.update() + + object_data_add(context, mesh, operator=None) + # object generation done # turn undo back on bpy.context.user_preferences.edit.use_global_undo = True @@ -726,7 +528,6 @@ class Solids_add_menu(bpy.types.Menu): layout.menu(PlatonicMenu.bl_idname, text = "Platonic") layout.menu(ArchiMenu.bl_idname, text = "Archimeadean") layout.menu(CatalanMenu.bl_idname, text = "Catalan") - layout.menu(OtherMenu.bl_idname, text = "Others") class PlatonicMenu(bpy.types.Menu): """Define Platonic menu""" @@ -782,22 +583,10 @@ class CatalanMenu(bpy.types.Menu): layout.operator(Solids.bl_idname, text = "Rhombic Triacontahedron").preset = "dr12" layout.operator(Solids.bl_idname, text = "Triakis Icosahedron").preset = "dt12" layout.operator(Solids.bl_idname, text = "Pentakis Dodecahedron").preset = "dt20" - layout.operator(Solids.bl_idname, text = "Deltoidal Hexecontahedron").preset = "dt20" - layout.operator(Solids.bl_idname, text = "Disdyakis Triacontahedron").preset = "db12" + layout.operator(Solids.bl_idname, text = "Deltoidal Hexecontahedron").preset = "db12" + layout.operator(Solids.bl_idname, text = "Disdyakis Triacontahedron").preset = "dc12" layout.operator(Solids.bl_idname, text = "Pentagonal Hexecontahedron").preset = "ds12" - -class OtherMenu(bpy.types.Menu): - """Defines Others preset menu""" - bl_idname = "Others_calls" - bl_label = "Others" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator(Solids.bl_idname, text = "Cube").preset = "c" - layout.operator(Solids.bl_idname, text = "Soccer ball").preset = "sb" - - + def menu_func(self, context): self.layout.menu(Solids_add_menu.bl_idname, icon="PLUGIN") @@ -815,4 +604,4 @@ def unregister(): if __name__ == "__main__": - register() + register() \ No newline at end of file -- GitLab