Skip to content
Snippets Groups Projects
Commit 23fc6a34 authored by Gerwin Damsteegt's avatar Gerwin Damsteegt
Browse files

first commit, lets see if it works

updating regular solids add mesh script to version 2
parent 5c7faa59
No related branches found
No related tags found
No related merge requests found
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # 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. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
...@@ -20,10 +20,10 @@ ...@@ -20,10 +20,10 @@
bl_info = { bl_info = {
"name": "Regular Solids", "name": "Regular Solids",
"author": "DreamPainter", "author": "DreamPainter",
"version": (1, 0, 1), "version": (2, 0),
"blender": (2, 5, 7), "blender": (2, 5, 7),
"api": 35853, "api": 36336,
"location": "View3D > Add > Mesh > Regular Solids", "location": "View3D > Add > Mesh > Solids",
"description": "Add a Regular Solid mesh.", "description": "Add a Regular Solid mesh.",
"warning": "", "warning": "",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
...@@ -32,102 +32,13 @@ bl_info = { ...@@ -32,102 +32,13 @@ bl_info = {
"func=detail&aid=22405", "func=detail&aid=22405",
"category": "Add Mesh"} "category": "Add Mesh"}
import bpy import bpy
from bpy.props import FloatProperty,EnumProperty,BoolProperty from bpy.props import FloatProperty,EnumProperty,BoolProperty
from math import sqrt from math import sqrt
from mathutils import Vector,Matrix from mathutils import Vector,Matrix
#from rawMeshUtils import *
from functools import reduce 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 # 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 # for each polygon created in this script. be aware though, that this function
# assumes each polygon is convex. # assumes each polygon is convex.
...@@ -143,28 +54,26 @@ def createPolys(poly): ...@@ -143,28 +54,26 @@ def createPolys(poly):
poly = [poly] # if only one, make it a list of one face poly = [poly] # if only one, make it a list of one face
faces = [] faces = []
for i in poly: for i in poly:
l = len(i) L = len(i)
# let all faces of 3 or 4 verts be # let all faces of 3 or 4 verts be
if l < 5: if L < 5:
faces.append(i) faces.append(i)
# split all polygons in half and bridge the two halves # split all polygons in half and bridge the two halves
else: else:
half = int(l/2) f = [[i[x],i[x+1],i[L-2-x],i[L-1-x]] for x in range(L//2-1)]
f = createFaces(i[:half],[i[-1-j] for j in range(half)])
faces.extend(f) faces.extend(f)
# if the polygon has an odd number of verts, add the last tri if L&1 == 1:
if l%2 == 1: faces.append([i[L//2-1+x] for x in [0,1,2]])
faces.append([i[half-1],i[half],i[half+1]])
return faces return faces
# function to make the reduce function work as a workaround to sum a list of vectors # 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) return reduce(lambda a,b: a+b, list)
# creates the 5 platonic solids as a base for the rest # 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 # plato: should be one of {"4","6","8","12","20"}. decides what solid the
# outcome will be. # outcome will be.
# returns a list of vertices and faces and the appropriate name # returns a list of vertices and faces
def source(plato): def source(plato):
verts = [] verts = []
faces = [] faces = []
...@@ -225,16 +134,11 @@ def source(plato): ...@@ -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], [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]] [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 # convert the tuples to Vectors
verts = [Vector(i) for i in v] verts = [Vector(i) for i in v]
return verts,faces return verts,faces
# processes the raw data from source # processes the raw data from source
def createSolid(plato,vtrunc,etrunc,dual,snub): def createSolid(plato,vtrunc,etrunc,dual,snub):
verts = [] verts = []
...@@ -250,304 +154,194 @@ def createSolid(plato,vtrunc,etrunc,dual,snub): ...@@ -250,304 +154,194 @@ def createSolid(plato,vtrunc,etrunc,dual,snub):
# constants saving space and readability # constants saving space and readability
vtrunc *= 0.5 vtrunc *= 0.5
etrunc *= 0.5 etrunc *= 0.5
supposed_size = 0 supposedSize = 0
noSnub = (snub == "0") or (etrunc == 0.5) or (etrunc == 0) noSnub = (snub == "None") or (etrunc == 0.5) or (etrunc == 0)
lSnub = (snub == "L") and (0 < etrunc < 0.5) lSnub = (snub == "Left") and (0 < etrunc < 0.5)
rSnub = (snub == "R") and (0 < etrunc < 0.5) rSnub = (snub == "Right") and (0 < etrunc < 0.5)
# no truncation # no truncation
if vtrunc == 0: if vtrunc == 0:
if dual: # dual is as simple as another, but mirrored platonic solid if dual: # dual is as simple as another, but mirrored platonic solid
vInput,fInput = source(dualSource[plato]) vInput, fInput = source(dualSource[plato])
supposed_size = Asum(vInput[i] for i in fInput[0]).length / len(fInput[0]) supposedSize = vSum(vInput[i] for i in fInput[0]).length/len(fInput[0])
vInput = [-i*supposed_size for i in vInput] # mirror it vInput = [-i*supposedSize for i in vInput] # mirror it
return vInput,fInput return vInput, fInput
return source(plato) return source(plato)
# simple truncation of the source elif 0 < vtrunc <= 0.5: # simple truncation of the source
elif 0.5 >= vtrunc > 0: vInput, fInput = source(plato)
vInput,fInput = source(plato) else:
# truncation is now equal to simple truncation of the dual of the source # truncation is now equal to simple truncation of the dual of the source
elif vtrunc > 0.5: vInput, fInput = source(dualSource[plato])
vInput,fInput = source(dualSource[plato]) supposedSize = vSum(vInput[i] for i in fInput[0]).length / len(fInput[0])
supposed_size = Asum(vInput[i] for i in fInput[0]).length / len(fInput[0]) vtrunc = 1-vtrunc # account for the source being a dual
# account for the source being a dual if vtrunc == 0: # no truncation needed
vtrunc = 1-vtrunc
if vtrunc == 0: # no truncation
if dual: if dual:
vInput,fInput = source(plato) vInput, fInput = source(plato)
vInput = [i*supposed_size for i in vInput] vInput = [i*supposedSize for i in vInput]
return vInput,fInput#,sourceName return vInput, fInput
#JayDez - I don't know what sourceName is, but commenting that vInput = [-i*supposedSize for i in vInput]
#part out fixes vert truncation problems. return vInput, fInput
vInput = [-i*supposed_size for i in vInput]
return vInput,fInput # generate connection database
vDict = [{} for i in vInput]
# generate a database for creating the faces. this exists out of a list for # for every face, store what vertex comes after and before the current vertex
# 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
for x in range(len(fInput)): for x in range(len(fInput)):
i = fInput[x] i = fInput[x]
# in every faces, check which vertices connect the each vert and sort for j in range(len(i)):
# in ccw order vDict[i[j-1]][i[j]] = [i[j-2],x]
for j in range(-1,len(i)-1): if len(vDict[i[j-1]]) == 1: vDict[i[j-1]][-1] = i[j]
# only generate an edge dict, if edge truncation is needed
if etrunc: # the actual connection database: exists out of:
# list edges as [min,max], to evade confusion # [vtrunc pos, etrunc pos, connected vert IDs, connected face IDs]
first = min([i[j-1],i[j]]) vData = [[[],[],[],[]] for i in vInput]
last = max([i[j-1],i[j]]) fvOutput = [] # faces created from truncated vertices
# if an edge is not allready in, add it and give the index feOutput = [] # faces created from truncated edges
try: vOutput = [] # newly created vertices
y = edges.index([first,last]) for x in range(len(vInput)):
except: i = vDict[x] # lookup the current vertex
edges.append([first,last]) current = i[-1]
y = len(edges)-1 while True: # follow the chain to get a ccw order of connected verts and faces
# add a dict item vData[x][2].append(i[current][0])
v[i[j]][6][str(y)] = [0,0] vData[x][3].append(i[current][1])
# the vertex before and after the current vertex, check whether they # create truncated vertices
# are allready in the database vData[x][0].append((1-vtrunc)*vInput[x] + vtrunc*vInput[vData[x][2][-1]])
after = i[j+1] not in v[i[j]][1] current = i[current][0]
before = i[j-1] not in v[i[j]][1] if current == i[-1]: break # if we're back at the first: stop the loop
# sort them and add faces and, when necessary, edges in the database fvOutput.append([]) # new face from truncated vert
if after: fOffset = x*(len(i)-1) # where to start off counting faceVerts
if before: # only create one vert where one is needed (v1 todo: done)
v[i[j]][1].append(i[j+1]) if etrunc == 0.5:
v[i[j]][1].append(i[j-1]) for j in range(len(i)-1):
v[i[j]][3].append(x) vOutput.append((vData[x][0][j]+vData[x][0][j-1])*etrunc) # create vert
if etrunc: v[i[j]][5].append(y) fvOutput[x].append(fOffset+j) # add to face
else: fvOutput[x] = fvOutput[x][1:]+[fvOutput[x][0]] # rotate face for ease later on
z = v[i[j]][1].index(i[j-1]) # create faces from truncated edges.
v[i[j]][1].insert(z,i[j+1]) for j in range(len(i)-1):
v[i[j]][3].insert(z,x) if x > vData[x][2][j]: #only create when other vertex has been added
if etrunc: v[i[j]][5].insert(z,y) index = vData[vData[x][2][j]][2].index(x)
else: feOutput.append([fvOutput[x][j],fvOutput[x][j-1],
z = v[i[j]][1].index(i[j+1]) fvOutput[vData[x][2][j]][index],
v[i[j]][3].insert(z,x) fvOutput[vData[x][2][j]][index-1]])
if etrunc: v[i[j]][5].insert(z,y) # edge truncation between none and full
if before: elif etrunc > 0:
v[i[j]][1].insert(z+1,i[j-1]) for j in range(len(i)-1):
# add the current face to the current vertex in the dict # create snubs from selecting verts from rectified meshes
v[i[j]][4][str(x)] = [0,0] if rSnub:
vOutput.append(etrunc*vData[x][0][j]+(1-etrunc)*vData[x][0][j-1])
# generate vert-only truncated vertices by linear interpolation fvOutput[x].append(fOffset+j)
for i in v: elif lSnub:
for j in range(len(i[1])): vOutput.append((1-etrunc)*vData[x][0][j]+etrunc*vData[x][0][j-1])
verts.append(vInput[i[0]]*(1-vtrunc)+vInput[i[1][j]]*vtrunc) fvOutput[x].append(fOffset+j)
l = len(verts)-1 else: #noSnub, select both verts from rectified mesh
# face resulting from truncating this vertex vOutput.append(etrunc*vData[x][0][j]+(1-etrunc)*vData[x][0][j-1])
i[2].append(l) vOutput.append((1-etrunc)*vData[x][0][j]+etrunc*vData[x][0][j-1])
# this vertex is used by both faces using this edge fvOutput[x].append(2*fOffset+2*j)
i[4][str(i[3][j])][1] = l fvOutput[x].append(2*fOffset+2*j+1)
i[4][str(i[3][j-1])][0] = l # rotate face for ease later on
if noSnub: fvOutput[x] = fvOutput[x][2:]+fvOutput[x][:2]
# only truncate edges when needed else: fvOutput[x] = fvOutput[x][1:]+[fvOutput[x][0]]
vert_faces = [] # create single face for each edge
if etrunc: if noSnub:
# generate a new list of vertices, by linear interpolating each vert-face for j in range(len(i)-1):
nVerts = [] if x > vData[x][2][j]:
for i in v: index = vData[vData[x][2][j]][2].index(x)
f = [] feOutput.append([fvOutput[x][j*2],fvOutput[x][2*j-1],
# weird range so we dont run out of array bounds fvOutput[vData[x][2][j]][2*index],
for j in range(-1,len(i[2])-1): fvOutput[vData[x][2][j]][2*index-1]])
# making use of the fact that the snub operation takes only # create 2 tri's for each edge for the snubs
# one of the two vertices per edge. so rSnub only takes the elif rSnub:
# first, lSnub only takes the second, and noSnub takes both for j in range(len(i)-1):
if rSnub or noSnub: if x > vData[x][2][j]:
# interpolate index = vData[vData[x][2][j]][2].index(x)
nVerts.append((1-etrunc)*verts[i[2][j]] + etrunc*verts[i[2][j-1]]) feOutput.append([fvOutput[x][j],fvOutput[x][j-1],
# add last vertex to the vert-face, face-face and edge-face fvOutput[vData[x][2][j]][index]])
l = len(nVerts)-1 feOutput.append([fvOutput[x][j],fvOutput[vData[x][2][j]][index],
f.append(l) fvOutput[vData[x][2][j]][index-1]])
i[4][str(i[3][j-1])][0] = l elif lSnub:
i[6][str(i[5][j-1])][1] = l for j in range(len(i)-1):
if lSnub or noSnub: if x > vData[x][2][j]:
# interpolate index = vData[vData[x][2][j]][2].index(x)
nVerts.append((1-etrunc)*verts[i[2][j]] + etrunc*verts[i[2][j+1]]) feOutput.append([fvOutput[x][j],fvOutput[x][j-1],
# add last vertex to the vert-face, face-face and edge-face fvOutput[vData[x][2][j]][index-1]])
l = len(nVerts)-1 feOutput.append([fvOutput[x][j-1],fvOutput[vData[x][2][j]][index],
f.append(l) fvOutput[vData[x][2][j]][index-1]])
i[4][str(i[3][j])][1] = l # special rules fro birectified mesh (v1 todo: done)
i[6][str(i[5][j-1])][0] = l elif vtrunc == 0.5:
# add vert-face for j in range(len(i)-1):
vert_faces.append(f) if x < vData[x][2][j]: # use current vert, since other one has not passed yet
vOutput.append(vData[x][0][j])
# snub operator creates 2 tri's instead of a planar quad, needing the fvOutput[x].append(len(vOutput)-1)
# 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])]))
else: else:
if index+1 >= len(one[5]): index = -1 # search for other edge to avoid duplicity
f.append(max(two[6][str(two[5][index+1])])) connectee = vData[x][2][j]
edge_faces.append(f) fvOutput[x].append(fvOutput[connectee][vData[connectee][2].index(x)])
else: else: # vert truncation only
# generate edge-faces from the dictionairy, simple quads for noSnub vOutput.extend(vData[x][0]) # use generated verts from way above
edge_faces = [] for j in range(len(i)-1): # create face from them
for i in range(len(edges)): fvOutput[x].append(fOffset+j)
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]
# calculate supposed vertex length to ensure continuity # calculate supposed vertex length to ensure continuity
if supposed_size: if supposedSize and not dual: # this to make the vtrunc > 1 work
supposed_size *= len(vert_faces[0])/Asum(verts[i] for i in vert_faces[0]).length supposedSize *= len(fvOutput[0])/vSum(vOutput[i] for i in fvOutput[0]).length
verts = [-i*supposed_size for i in verts] vOutput = [-i*supposedSize for i in vOutput]
# generate face-faces by looking up the old verts and replacing them with # create new faces by replacing old vert IDs by newly generated verts
# the vertices in the dictionairy ffOutput = [[] for i in fInput]
face_faces = []
for x in range(len(fInput)): for x in range(len(fInput)):
f = [] # only one generated vert per vertex, so choose accordingly
for j in fInput[x]: if etrunc == 0.5 or (etrunc == 0 and vtrunc == 0.5) or lSnub or rSnub:
# again using the fact, that only one of the two verts is used ffOutput[x] = [fvOutput[i][vData[i][3].index(x)-1] for i in fInput[x]]
# for snub operation # two generated verts per vertex
if rSnub and etrunc: elif etrunc > 0:
f.append(v[j][4][str(x)][0]) for i in fInput[x]:
elif lSnub and etrunc: ffOutput[x].append(fvOutput[i][2*vData[i][3].index(x)-1])
f.append(v[j][4][str(x)][1]) ffOutput[x].append(fvOutput[i][2*vData[i][3].index(x)-2])
else: else: # cutting off corners also makes 2 verts
# for cool graphics, comment the first line and uncomment the second line for i in fInput[x]:
# then work the vTrunc property, leave the other properties at 0 ffOutput[x].append(fvOutput[i][vData[i][3].index(x)])
# (can also change 0 to 1 in second line to change from ccw to cw) ffOutput[x].append(fvOutput[i][vData[i][3].index(x)-1])
f.extend(v[j][4][str(x)]) # first
#f.append(v[j][4][str(x)][0]) # second
face_faces.append(f)
if dual: if not dual:
# create verts by taking the average of all vertices that make up each return vOutput,fvOutput + feOutput + ffOutput
# face. do it in this order to ease the following face creation else:
nVerts = [] # do the same procedure as above, only now on the generated mesh
for i in vert_faces: # generate connection database
nVerts.append(Asum(verts[j] for j in i)/len(i)) vDict = [{} for i in vOutput]
if etrunc: dvOutput = [0 for i in fvOutput + feOutput + ffOutput]
eStart = len(nVerts) dfOutput = []
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 etrunc: # for every vertex for x in range(len(dvOutput)): # for every face
for i in v: # add the face, consisting of the vert,edge,next i = (fvOutput + feOutput + ffOutput)[x] # choose face to work with
# edge and face between those edges # find vertex from face
for j in range(len(i[1])): normal = (vOutput[i[0]]-vOutput[i[1]]).cross(vOutput[i[2]]-vOutput[i[1]]).normalized()
f = [i[0],eStart+i[5][j-1],fStart+i[3][j],eStart+i[5][j]] dvOutput[x] = normal/(normal.dot(vOutput[i[0]]))
if direction == 1: # first diagonal for j in range(len(i)): # create vert chain
faces.extend([[f[0],f[1],f[3]],[f[1],f[2],f[3]]]) vDict[i[j-1]][i[j]] = [i[j-2],x]
elif direction == -1: # first diagonal if len(vDict[i[j-1]]) == 1: vDict[i[j-1]][-1] = i[j]
faces.extend([[f[0],f[1],f[2]],[f[0],f[2],f[3]]])
else: # calculate supposed size for continuity
faces.append(f) # no diagonal supposedSize = vSum([vInput[i] for i in fInput[0]]).length/len(fInput[0])
else: supposedSize /= dvOutput[-1].length
for i in v: # for every vertex dvOutput = [i*supposedSize for i in dvOutput]
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
# 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): class Solids(bpy.types.Operator):
"""Add one of the (regular) solids (mesh)""" """Add one of the (regular) solids (mesh)"""
bl_idname = "mesh.primitive_solid_add" bl_idname = "mesh.primitive_solid_add"
bl_label = "(Regular) solids" 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'} bl_options = {'REGISTER', 'UNDO'}
source = EnumProperty(items = (("4","Tetrahedron",""), source = EnumProperty(items = (("4","Tetrahedron",""),
...@@ -566,7 +360,7 @@ class Solids(bpy.types.Operator): ...@@ -566,7 +360,7 @@ class Solids(bpy.types.Operator):
default = 1.0) default = 1.0)
vTrunc = FloatProperty(name = "Vertex Truncation", vTrunc = FloatProperty(name = "Vertex Truncation",
description = "Ammount of vertex truncation", description = "Ammount of vertex truncation",
min = 0.001, min = 0.0,
soft_min = 0.0, soft_min = 0.0,
max = 2.0, max = 2.0,
soft_max = 2.0, soft_max = 2.0,
...@@ -582,9 +376,9 @@ class Solids(bpy.types.Operator): ...@@ -582,9 +376,9 @@ class Solids(bpy.types.Operator):
default = 0.0, default = 0.0,
precision = 3, precision = 3,
step = 0.2) step = 0.2)
snub = EnumProperty(items = (("0","No Snub",""), snub = EnumProperty(items = (("None","No Snub",""),
("L","Left Snub",""), ("Left","Left Snub",""),
("R","Right Snub","")), ("Right","Right Snub","")),
name = "Snub", name = "Snub",
description = "Create the snub version") description = "Create the snub version")
dual = BoolProperty(name="Dual", dual = BoolProperty(name="Dual",
...@@ -610,7 +404,7 @@ class Solids(bpy.types.Operator): ...@@ -610,7 +404,7 @@ class Solids(bpy.types.Operator):
("dt4","Triakis Tetrahedron",""), ("dt4","Triakis Tetrahedron",""),
("dr4","Rhombic Dodecahedron",""), ("dr4","Rhombic Dodecahedron",""),
("dt6","Triakis Octahedron",""), ("dt6","Triakis Octahedron",""),
("dt8","Triakis Hexahedron",""), ("dt8","Tetrakis Hexahedron",""),
("db6","Deltoidal Icositetrahedron",""), ("db6","Deltoidal Icositetrahedron",""),
("dc6","Disdyakis Dodecahedron",""), ("dc6","Disdyakis Dodecahedron",""),
("ds6","Pentagonal Icositetrahedron",""), ("ds6","Pentagonal Icositetrahedron",""),
...@@ -619,95 +413,103 @@ class Solids(bpy.types.Operator): ...@@ -619,95 +413,103 @@ class Solids(bpy.types.Operator):
("dt20","Pentakis Dodecahedron",""), ("dt20","Pentakis Dodecahedron",""),
("db12","Deltoidal Hexecontahedron",""), ("db12","Deltoidal Hexecontahedron",""),
("dc12","Disdyakis Triacontahedron",""), ("dc12","Disdyakis Triacontahedron",""),
("ds12","Pentagonal Hexecontahedron",""), ("ds12","Pentagonal Hexecontahedron","")),
("c","Cube",""),
("sb","Soccer ball","")),
name = "Presets", name = "Presets",
description = "Parameters for some hard names") description = "Parameters for some hard names")
# actual preset values # actual preset values
p = {"t4":["4",2/3,0,0,"0"], p = {"t4":["4",2/3,0,0,"None"],
"r4":["4",1,1,0,"0"], "r4":["4",1,1,0,"None"],
"t6":["6",2/3,0,0,"0"], "t6":["6",2/3,0,0,"None"],
"t8":["8",2/3,0,0,"0"], "t8":["8",2/3,0,0,"None"],
"b6":["6",1.0938,1,0,"0"], "b6":["6",1.0938,1,0,"None"],
"c6":["6",1.0572,0.585786,0,"0"], "c6":["6",1.0572,0.585786,0,"None"],
"s6":["6",1.0875,0.704,0,"L"], "s6":["6",1.0875,0.704,0,"Left"],
"r12":["12",1,0,0,"0"], "r12":["12",1,0,0,"None"],
"t12":["12",2/3,0,0,"0"], "t12":["12",2/3,0,0,"None"],
"t20":["20",2/3,0,0,"0"], "t20":["20",2/3,0,0,"None"],
"b12":["12",1.1338,1,0,"0"], "b12":["12",1.1338,1,0,"None"],
"c12":["20",0.921,0.553,0,"0"], "c12":["20",0.921,0.553,0,"None"],
"s12":["12",1.1235,0.68,0,"L"], "s12":["12",1.1235,0.68,0,"Left"],
"dt4":["4",2/3,0,1,"0"], "dt4":["4",2/3,0,1,"None"],
"dr4":["4",1,2/3,1,"0"], "dr4":["4",1,1,1,"None"],
"dt6":["6",4/3,0,1,"0"], "dt6":["6",2/3,0,1,"None"],
"dt8":["8",1,0,1,"0"], "dt8":["8",2/3,0,1,"None"],
"db6":["6",1.0938,0.756,1,"0"], "db6":["6",1.0938,1,1,"None"],
"dc6":["6",1,1,1,"0"], "dc6":["6",1.0572,0.585786,1,"None"],
"ds6":["6",1.0875,0.704,1,"L"], "ds6":["6",1.0875,0.704,1,"Left"],
"dr12":["12",1.54,0,1,"0"], "dr12":["12",1,0,1,"None"],
"dt12":["12",5/3,0,1,"0"], "dt12":["12",2/3,0,1,"None"],
"dt20":["20",2/3,0,1,"0"], "dt20":["20",2/3,0,1,"None"],
"db12":["12",1,0.912,1,"0"], "db12":["12",1.1338,1,1,"None"],
"dc12":["20",0.921,1,1,"0"], "dc12":["20",0.921,0.553,1,"None"],
"ds12":["12",1.1235,0.68,1,"L"], "ds12":["12",1.1235,0.68,1,"Left"]}
"c":["6",0,0,0,"0"],
"sb":["20",2/3,0,0,"0"]} #previous preset, for User-friendly reasons
previousSetting = ""
def execute(self,context): 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 # that mesh ops are undoable and entire script acts as one operator
bpy.context.user_preferences.edit.use_global_undo = False bpy.context.user_preferences.edit.use_global_undo = False
# piece of code to make presets remain until parameters are changed
#if preset, set preset
if self.preset != "0": if self.preset != "0":
using = self.p[self.preset] #if preset, set preset
self.source = using[0] if self.previousSetting != self.preset:
self.vTrunc = using[1] using = self.p[self.preset]
self.eTrunc = using[2] self.source = using[0]
self.dual = using[3] self.vTrunc = using[1]
self.snub = using[4] self.eTrunc = using[2]
self.preset = "0" 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 # generate mesh
verts,faces = createSolid(self.source, verts,faces = createSolid(self.source,
self.vTrunc, self.vTrunc,
self.eTrunc, self.eTrunc,
self.dual, self.dual,
self.snub) self.snub)
# turn n-gons in quads and tri's # turn n-gons in quads and tri's
faces = createPolys(faces) faces = createPolys(faces)
# resize to normal size, or if keepSize, make sure all verts are of length 'size' # resize to normal size, or if keepSize, make sure all verts are of length 'size'
if self.keepSize: 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 else: rad = self.size
verts = [i*rad for i in verts] verts = [i*rad for i in verts]
# generate object # generate object
obj = create_mesh_object(context,verts,[],faces,"Solid") # Create new mesh
mesh = bpy.data.meshes.new("Solid")
# vertices will be on top of each other in some cases,
# so remove doubles then # Make a mesh from a list of verts/edges/faces.
if ((self.vTrunc == 1) and (self.eTrunc == 0)) or (self.eTrunc == 1): mesh.from_pydata(verts, [], faces)
current_mode = context.active_object.mode
if current_mode == 'OBJECT': # Update mesh geometry after adding stuff.
bpy.ops.object.mode_set(mode='EDIT') mesh.update()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles() object_data_add(context, mesh, operator=None)
bpy.ops.object.mode_set(mode=current_mode) # object generation done
# 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)
# turn undo back on # turn undo back on
bpy.context.user_preferences.edit.use_global_undo = True bpy.context.user_preferences.edit.use_global_undo = True
...@@ -726,7 +528,6 @@ class Solids_add_menu(bpy.types.Menu): ...@@ -726,7 +528,6 @@ class Solids_add_menu(bpy.types.Menu):
layout.menu(PlatonicMenu.bl_idname, text = "Platonic") layout.menu(PlatonicMenu.bl_idname, text = "Platonic")
layout.menu(ArchiMenu.bl_idname, text = "Archimeadean") layout.menu(ArchiMenu.bl_idname, text = "Archimeadean")
layout.menu(CatalanMenu.bl_idname, text = "Catalan") layout.menu(CatalanMenu.bl_idname, text = "Catalan")
layout.menu(OtherMenu.bl_idname, text = "Others")
class PlatonicMenu(bpy.types.Menu): class PlatonicMenu(bpy.types.Menu):
"""Define Platonic menu""" """Define Platonic menu"""
...@@ -782,22 +583,10 @@ class CatalanMenu(bpy.types.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 = "Rhombic Triacontahedron").preset = "dr12"
layout.operator(Solids.bl_idname, text = "Triakis Icosahedron").preset = "dt12" 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 = "Pentakis Dodecahedron").preset = "dt20"
layout.operator(Solids.bl_idname, text = "Deltoidal Hexecontahedron").preset = "dt20" layout.operator(Solids.bl_idname, text = "Deltoidal Hexecontahedron").preset = "db12"
layout.operator(Solids.bl_idname, text = "Disdyakis Triacontahedron").preset = "db12" layout.operator(Solids.bl_idname, text = "Disdyakis Triacontahedron").preset = "dc12"
layout.operator(Solids.bl_idname, text = "Pentagonal Hexecontahedron").preset = "ds12" 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): def menu_func(self, context):
self.layout.menu(Solids_add_menu.bl_idname, icon="PLUGIN") self.layout.menu(Solids_add_menu.bl_idname, icon="PLUGIN")
...@@ -815,4 +604,4 @@ def unregister(): ...@@ -815,4 +604,4 @@ def unregister():
if __name__ == "__main__": if __name__ == "__main__":
register() register()
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment