algorithmic modeling for Rhino
Hi all,
I am a python user(beginner) trying to understand meshes and meshing algorithms like Marching cubes, Ball Pivot and Marching tetrahedra. I saw basic example provided with rhinoscriptsyntax, It was hard to imagine how hard it was to make mesh faces assigned from points. One way i could think of not Reinventing the wheel was to call mesh from points commands in python . I needs some direction to head towards creating meshes in rhino
I was suggested Libraries but i not smart enough to figure out how to or where to use them exactly.
Tags:
Since marching cubes scripts online had too many big ugly arrays of numbers I grabbed an elegant marching tetrahedrons script instead, here:
Start Small: Marching Tetrahedrons in Python
However, it is set up to manipulate strings into an STL file that is quite different from how Grasshopper defines meshes, in that an STL seems to define each face by XYZ points, Grasshopper wants a single list of all vertex points and then has an allied lists of topological connectivity according to vertex number, so for now I just hacked it to spit out points minus so many duplicates it generates for STL:
Right now it has an internal 3D trigonometric function I added input sliders to control, that creates surfaces that look a lot like molecular orbitals.
So how do I make a mesh? I failed to make a single mesh face from each STL face since AddMesh seems to want a list, so I tried making a single list and matching it with a simple ((1,2,3),(4,5,6),(7,8,9)...) array of connectivity but it hasn't worked yet since the STL list of vertices has duplicates that won't work for Grasshopper and removing the duplicates scrambles the connectivity relation.
After some work on this and seeing the output, I figure I could just randomly populate the mathematical function with points instead, unless it really gives a better mesh result than other routines. I'm not sure what to do with it yet, even if I get the mesh figured out.
import rhinoscriptsyntax
import Rhino
POINTS_CONTAINER =[]
POINTS = []
class Vector: # struct XYZ
def __init__(self,x,y,z):
self.x=x
self.y=y
self.z=z
def __str__(self):
return str(self.x)+" "+str(self.y)+" "+str(self.z)
class Gridcell: # struct GRIDCELL
def __init__(self,p,n,val):
self.p = p # p=[8]
self.n = n # n=[8]
self.val = val # val=[8]
class Triangle: # struct TRIANGLE
def __init__(self,p1,p2,p3):
self.p = [p1, p2, p3] # vertices
# HACK TO GRAB VERTICES FOR PYTHON OUTPUT
POINTS_CONTAINER.append( (p1.x,p1.y,p1.z) )
POINTS_CONTAINER.append( (p2.x,p2.y,p2.z) )
POINTS_CONTAINER.append( (p3.x,p3.y,p3.z) )
# return a 3d list of values
def readdata(f=lambda x,y,z:x*x+y*y+z*z,size=5.0,steps=11):
m=int(steps/2)
ki = []
for i in range(steps):
kj = []
for j in range(steps):
kd=[]
for k in range(steps):
kd.append(f(size*(i-m)/m,size*(j-m)/m,size*(k-m)/m))
kj.append(kd)
ki.append(kj)
return ki
from math import sin,cos,exp,atan2
def lobes(x,y,z):
try:
theta = atan2(x,y) # sin t = o
except:
theta = 0
try:
phi = atan2(z,y)
except:
phi = 0
r = x*x+y*y+z*z
ct=cos(PARAMETER_A * theta)
cp=cos(PARAMETER_B * phi)
return ct*ct*cp*cp*exp(-r/10)
def main():
data = readdata(lobes,10,40)
isolevel = 0.1
#print(data)
triangles=[]
for i in range(len(data)-1):
for j in range(len(data[i])-1):
for k in range(len(data[i][j])-1):
p=[None]*8
val=[None]*8
#print(i,j,k)
p[0]=Vector(i,j,k)
val[0] = data[i][j][k]
p[1]=Vector(i+1,j,k)
val[1] = data[i+1][j][k]
p[2]=Vector(i+1,j+1,k)
val[2] = data[i+1][j+1][k]
p[3]=Vector(i,j+1,k)
val[3] = data[i][j+1][k]
p[4]=Vector(i,j,k+1)
val[4] = data[i][j][k+1]
p[5]=Vector(i+1,j,k+1)
val[5] = data[i+1][j][k+1]
p[6]=Vector(i+1,j+1,k+1)
val[6] = data[i+1][j+1][k+1]
p[7]=Vector(i,j+1,k+1)
val[7] = data[i][j+1][k+1]
grid=Gridcell(p,[],val)
triangles.extend(PolygoniseTri(grid,isolevel,0,2,3,7))
triangles.extend(PolygoniseTri(grid,isolevel,0,2,6,7))
triangles.extend(PolygoniseTri(grid,isolevel,0,4,6,7))
triangles.extend(PolygoniseTri(grid,isolevel,0,6,1,2))
triangles.extend(PolygoniseTri(grid,isolevel,0,6,1,4))
triangles.extend(PolygoniseTri(grid,isolevel,5,6,1,4))
def t000F(g, iso, v0, v1, v2, v3):
return []
def t0E01(g, iso, v0, v1, v2, v3):
return [Triangle(
VertexInterp(iso,g.p[v0],g.p[v1],g.val[v0],g.val[v1]),
VertexInterp(iso,g.p[v0],g.p[v2],g.val[v0],g.val[v2]),
VertexInterp(iso,g.p[v0],g.p[v3],g.val[v0],g.val[v3]))
]
def t0D02(g, iso, v0, v1, v2, v3):
return [Triangle(
VertexInterp(iso,g.p[v1],g.p[v0],g.val[v1],g.val[v0]),
VertexInterp(iso,g.p[v1],g.p[v3],g.val[v1],g.val[v3]),
VertexInterp(iso,g.p[v1],g.p[v2],g.val[v1],g.val[v2]))
]
def t0C03(g, iso, v0, v1, v2, v3):
tri=Triangle(
VertexInterp(iso,g.p[v0],g.p[v3],g.val[v0],g.val[v3]),
VertexInterp(iso,g.p[v0],g.p[v2],g.val[v0],g.val[v2]),
VertexInterp(iso,g.p[v1],g.p[v3],g.val[v1],g.val[v3]))
return [tri,Triangle(
tri.p[2],
VertexInterp(iso,g.p[v1],g.p[v2],g.val[v1],g.val[v2]),
tri.p[1])
]
def t0B04(g, iso, v0, v1, v2, v3):
return [Triangle(
VertexInterp(iso,g.p[v2],g.p[v0],g.val[v2],g.val[v0]),
VertexInterp(iso,g.p[v2],g.p[v1],g.val[v2],g.val[v1]),
VertexInterp(iso,g.p[v2],g.p[v3],g.val[v2],g.val[v3]))
]
def t0A05(g, iso, v0, v1, v2, v3):
tri = Triangle(
VertexInterp(iso,g.p[v0],g.p[v1],g.val[v0],g.val[v1]),
VertexInterp(iso,g.p[v2],g.p[v3],g.val[v2],g.val[v3]),
VertexInterp(iso,g.p[v0],g.p[v3],g.val[v0],g.val[v3]))
return [tri,Triangle(
tri.p[0],
VertexInterp(iso,g.p[v1],g.p[v2],g.val[v1],g.val[v2]),
tri.p[1])
]
def t0906(g, iso, v0, v1, v2, v3):
tri=Triangle(
VertexInterp(iso,g.p[v0],g.p[v1],g.val[v0],g.val[v1]),
VertexInterp(iso,g.p[v1],g.p[v3],g.val[v1],g.val[v3]),
VertexInterp(iso,g.p[v2],g.p[v3],g.val[v2],g.val[v3]))
return [tri,
Triangle(
tri.p[0],
VertexInterp(iso,g.p[v0],g.p[v2],g.val[v0],g.val[v2]),
tri.p[2])
]
def t0708(g, iso, v0, v1, v2, v3):
return [Triangle(
VertexInterp(iso,g.p[v3],g.p[v0],g.val[v3],g.val[v0]),
VertexInterp(iso,g.p[v3],g.p[v2],g.val[v3],g.val[v2]),
VertexInterp(iso,g.p[v3],g.p[v1],g.val[v3],g.val[v1]))
]
trianglefs = {7:t0708,8:t0708,9:t0906,6:t0906,10:t0A05,5:t0A05,11:t0B04,4:t0B04,12:t0C03,3:t0C03,13:t0D02,2:t0D02,14:t0E01,1:t0E01,0:t000F,15:t000F}
def PolygoniseTri(g, iso, v0, v1, v2, v3):
triangles = []
# Determine which of the 16 cases we have given which vertices
# are above or below the isosurface
triindex = 0;
if g.val[v0] < iso: triindex |= 1
if g.val[v1] < iso: triindex |= 2
if g.val[v2] < iso: triindex |= 4
if g.val[v3] < iso: triindex |= 8
return trianglefs[triindex](g, iso, v0, v1, v2, v3)
def VertexInterp(isolevel,p1,p2,valp1,valp2):
if abs(isolevel-valp1) < 0.00001 :
return(p1);
if abs(isolevel-valp2) < 0.00001 :
return(p2);
if abs(valp1-valp2) < 0.00001 :
return(p1);
mu = (isolevel - valp1) / (valp2 - valp1)
return Vector(p1.x + mu * (p2.x - p1.x), p1.y + mu * (p2.y - p1.y), p1.z + mu * (p2.z - p1.z))
if __name__ == "__main__":
main()
# GRASSHOPPER PYTHON OUTPUT
POINTS = rhinoscriptsyntax.AddPoints(POINTS_CONTAINER)
POINTS = rhinoscriptsyntax.CullDuplicatePoints(POINTS)
Yeah, so Rhino's downloadable MeshFromPoints command is not very good at surfacing these point clouds that should have the same positions as marching tetrahedrons output, even after filling mesh holes which left plenty of holes:
If it worked well I could issue direct Rhino command line commands from Python as needed if I couldn't find the associated Rhinocommon command to use natively as Rhinoscript.
I can make lines instead since the script deals with single faces at a time defined by three points but removing the duplicates is tough since lines have two directions so Python can't easily spot them and the Grasshopper RemoveDuplicateLines component is terribly slow (2-3 minutes), but I found a second RemoveDuplicateLines in Grasshopper that is fast, so then Weaverbird can make a real mesh out of the lines:
Ahh sweet ... Thank u Nik.
What actual command or component?
Hi Nik-
If you're interested, there are some resources here for looking at directly meshing the results from the marching tetrahedra algorithm: http://www.grasshopper3d.com/profiles/blogs/metaball-meshing
Thank u David . Always there for help.
NURBS to the rescue. Since the script creates one face at a time, and I had trouble creating single mesh faces since AddMesh wants a list of more than one face, I just made single NURBS faces, and the duplicate points or lines problem went away, for a speedy result that can then externally be converted to single mesh faces and joined (welding failed) into a real mesh that could then be smoothed as needed:
The few stringy artifacts are part of the marching cubes after all, not a meshing issue.
You can build this entirely from individual mesh faces. For each face from the marching tetrahedra:
1. Create a new mesh
2. add the vertices
3. add a face with vertices 0,1,2
then you append this mesh to your full mesh. After you've appended all of the faces, you can call Vertices.CombineIdentical(true, true) to make it one, seamless mesh.
I had trouble getting rhinoscriptsyntax.AddMesh to accept only a single face like that in Python, which asks for four numbers that you repeat the last one of for a triangle:
Adds a mesh object to the document.
rhinoscriptsyntax.AddMesh ( vertices, face_vertices, vertex_normals=None, texture_coordinates=None, vertex_colors=None )
rhinoscript.mesh.AddMesh ( vertices, face_vertices, vertex_normals=None, texture_coordinates=None, vertex_colors=None )
vertices |
Required. List. A list of 3-D points defining the vertices of the mesh. |
face_vertices |
Required. List. A list containing lists of four numbers that define the vertex indices for each face of the mesh. If the third and forth vertex indices of a face are identical, a triangular face will be created. Otherwise a quad face will be created. |
Welcome to
Grasshopper
Added by Parametric House 0 Comments 0 Likes
Added by Parametric House 0 Comments 0 Likes
© 2024 Created by Scott Davidson. Powered by