Spatially varying fields, which associate a value (such as a number or a vector) to every point in space, have a wide range of applications.
I wanted a simple way of defining such a field in a Grasshopper script component as an equation, or a function of distance to some geometry, then passing this field to another script component, in a way that the downstream component can easily query the value of the field at any point. For speed reasons I did not want to do this by passing millions of sample values.
Various tools for fields do already exist in Grasshopper, both natively and as plugins, but of the ones I looked at, I couldn't find any that provided the flexibility and speed I wanted, so I put together a very simple library. For now this includes scalar, vector and frame fields.
With the scalar fields, I also made an implicit surface tool. These are also sometimes called isosurfaces or level sets, and they can be thought of as the equivalent of the contours of a heightfield, but in one dimension higher. A scalar field has a value everywhere in space, and the surface is where it switches from negative to positive, or crosses some given threshold value.
Probably the most popular approach to isosurfacing is an algorithm called marching cubes, which checks values of the field at the 8 corners of each cell of a cubic grid, and assigns each cell one of 256 pre-defined possible polygon topology configurations depending on how the field changes values between the corners, with some interpolation for the positions. There’s also a variant of this using tetrahedra, which I’ve played around with in the past (0,1,2).
Here I've come up with a different approach, consisting of two stages.
The first part of this is a voxelized threshold of the field. The value of the field is queried at the points of a 3d grid, and the ‘cell’ centred at each point is assigned to either in or out, then we take the faces of the cells which divide inside from outside, giving us a sort of chunky stepped approximation of the surface.
However, instead of using cubic voxels, I use truncated octahedra. This 14 sided polyhedron tessellates to fill space, and can be seen as the Voronoi tessellation of the points of a body-centred-cubic lattice (the corners of a cubic grid, plus an extra point at the centre of each cube).
image by Jim Lehman
The advantage of the truncated octahedral voxels over cubes is that the resulting threshold surface will not have any non-manifold edges. For example, 2 diagonally adjacent cubic voxels could share an edge, and that edge would then have 4 adjacent faces, which would cause problems later on. Marching cubes resolves this with its big lookup table of edge cases, but I felt like trying something a bit different.
As a Voronoi tessellation, the truncated octahedra always meet 3 around an edge, so this problem of the non-manifold edges can never occur (yes, ok – cubic voxels are also technically a Voronoi tessellation, but a nasty degenerate one where one of the 3 faces per edge disappears to nothing).
This can be illustrated with a 2d equivalent - A hexagonal tiling (which is the Voronoi diagram of points on a triangular grid) will never have the non-manifold edges that can occur with squares.
The second stage of my algorithm takes the stepped approximation of the surface from the first stage, and iteratively pulls its vertices onto the actual isosurface. Here we make use of the gradient of the field, and do a Newton-Raphson iteration, which converges quickly. Because all the vertices are pulled independently, this can be done in parallel.
The advantage of this two-stage approach is that the first stage can be carried out with a coarse grid, as it only needs to capture the essential topological features of the surface, and because it is only a binary check with no interpolation it can be fast.
Then, only after we have ruled out the vast majority of the points, we pull the remaining ones onto the surface to capture the finer details of the shape. We can also even include smoothing or subdivision steps between the 2 stages to improve the quality of the resulting mesh with little extra cost.
So here it is if you want to play with it:
(and the source is all up on GitHub here)
To try this out, put the Aether.dll in your Grasshopper libraries folder and make sure it is unblocked. Then when you open the Grasshopper definition you will be prompted to update the referenced assembly locations, and you should point it to where you just placed the dll. You'll also need Weaverbird installed if you want to do the smoothing or subdivision between steps.
This definition doesn’t do any clean-up around the edges of the grid – so you’ll probably want to either make the grid big enough to leave a gap around your geometry, or trim the result with some clipping planes.
I’ve included implementations of inverse distance weighted fields for curves and points (a.k.a. metaballs), and the mathematically defined implicit surface which approximates the gyroid.
You can find equations for many other nice surface under the Level surfaces section here:
http://www.msri.org/publications/sgp/jim/menud.html
If you want to have a go at defining your own fields, you’ll also need to include the gradient function. For this I find Wolfram Alpha comes in very handy – you can just plug in Grad(some function of xyz) and get the result. For example: grad(sin x * cos y + sin y * cos z + sin z * cos x)
Bear in mind that this is all work-in-progress - I just put it together yesterday, so there are very likely still some bugs. Any feedback welcome, or just post if you find a nice example.
Hope it is of some use/fun!
Daniel
Thanks to Dave Stasiuk, Will Pearson and Anders Deleuran for helpful conversation around this.
dvdrbls
Nice! Thank you Daniel
Dec 16, 2014
Daniel González Abalde
Thanks Daniel, looks very funny.
But it gives me error...
1. Error (CS1705): The assembly 'Aether, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' used 'RhinoCommon, Version = 5.1.30000.13, Culture = neutral, PublicKeyToken = 552281e97c755530' having a higher version the assembly 'RhinoCommon, Version = 5.1.30000.12, Culture = neutral, PublicKeyToken = 552281e97c755530' referred to.
I have to update Rhinoceros or...?
Dec 16, 2014
Ángel Linares
Nice!!! Thanks Daniel!!!!
Dec 16, 2014
Daniel Piker
Dec 16, 2014
Daniel González Abalde
Version 5 SR8 64-bit.
Is there a rhinocommon.dll 5.1.30000.13 to download or how to do?
Dec 16, 2014
Daniel Piker
Hi Daniel,
Can you try downloading the latest service release(in Rhino under Help>Check for updates)?
Dec 16, 2014
David Rutten
Interesting stuff. I want to improve the field data type in GH2 (support for both scalar and vector fields, haven't considered frame fields yet; what are they for?) and make it a far more fundamental data type. A lot of number/vector input parameter could be converted to fields as the component has a locality (for example the Radius input for the Circle component should be a field instead of a number, the Length and Direction inputs of Line SDL should be fields instead of vectors and numbers, etc.).
A big part of fields in my opinion needs to be modifiers and operators. Basically types of field which take one or more subfields and tweak the values, just like a Photoshop filter.
I'd love to hear any insights about how to make fields useful and performant.
Dec 16, 2014
Andrew Heumann
incredible!
Dec 16, 2014
Daniel Piker
Thanks. @David - I'm thinking of frame fields for specifying orientations and anisotropic material properties. Great to hear fields will be improved in GH2, and agreed that modifiers could be an important part of that.
Dec 16, 2014
Daniel González Abalde
Problem solved!
I've been playing with his example with text as curves (a word) and has broken down the laptop four times :( .But with simpler curves worked very well, fast.
Thanks, that's awesome :)
Dec 16, 2014
David Rutten
@Daniel,
So the former is for transformations and spacemorphs? Or is it a more general application of 'orientation'?
Dec 16, 2014
Daniel Piker
@David, yes I mean orientation in the sense of geometric transformations.
Dec 16, 2014
David Stasiuk
into the Æther!! super exciting...so many possibilities here.
Dec 16, 2014
Rasmus Holst
Very cool :) The small mention about anisotropic material properties sounds very interesting as well to me, as you might know Daniel :)
Seems like you guys didn't just have beers at CITA last friday then...
Dec 16, 2014
ng5 Alex
great stuff!
thank you Daniel
Dec 16, 2014
GuangYang
nice work!
Dec 16, 2014
taz
"Newton-Raphson truncated octahedral voxel isosurface" is quite a mouthful.
I think at this point you get to coin it a "Piker Surface" (patent pending).
Dec 16, 2014
Marios Tsiliakos
Ty Daniel :)
Dec 16, 2014
David Reeves
Most excellent.
Dec 17, 2014
panhao
Mar 4, 2015
panhao
Well I Found that an octahedra can be cut into 8*6+6*2 tetrahedrons.Why not use tetrahedrons to polygonising the scalar field?I made one showed below.
Mar 4, 2015
Kim hauer
Interesting topic! I have been exploring IsoSurfaces using K3dsurf for some time.
I recently Tried a Houdini Demo with also has a Python math engine to create IsoSurface Geometry, but I found it extremely slow. Below are some examples from a site.
http://www.z-way.org/script-and-gizmo/houdini/isosurface/isocubes
The Math is similar to K3dsurf functions, with some function definitions specific to Houdini input format.
I'm curious if a complex IsoFormula: for example,
min( abs( abs( min( min( abs($X), abs($Y)), abs($Z))) -2)* cos( deg($PI/4))+ abs( abs( min( min( max( abs($X), abs($Y)), max( abs($X), abs($Z))), max( abs($Y), abs($Z)) ))-2)*sin(deg($PI/4))-0.4 , min( abs( abs( min( min( abs($X), abs($Y)), abs($Z)))-2), abs( abs( min( min( max( abs($X), abs($Y)), max( abs($X), abs($Z))), max( abs($Y), abs($Z)) ))-2)) )^2+(max(max(abs($X),abs($Y)),abs($Z))-3.4)^2-0.0225
can simply be copied into a Grasshopper Panel, attached to the remaining Generalized IsoFormula.gh for evaluation?
Mar 9, 2015
Joris Hoogeboom
This is amazing! Somehow I manage to let it crash rhino very easily (even with small amount of points, 4-5 or so), hard to do anything with it this way. Any idea?
Mar 17, 2015
Moonbeast
Daniel,
Excellent work, you are changing the game, so many props. I am having issues loading Aether on 9.076, it is not asking for the assembly location. I went and double checked the component in the Manage assemblies and it seems as though it is referencing the proper .dll, of course, also unblocked, any advice for other means of getting it working?
Also, new kangaroo is amazing, killing it
Apr 7, 2015
Daniel Piker
Hi Moonbeast. Is it giving some error message? Sometimes the COFF loading causes problems when passing custom types between scripted components like this. Try switching it off in GrasshopperDeveloperSettings, then restarting Rhino.
Apr 7, 2015
Jens Pedersen
Quick question, what valuea would you suggest to get the mesh around curves and points? I have scaled my geometry down to the size of your examples, and would assume that by plugin in my curves and points would produce similar results?
any suggestions?
Jun 3, 2015
AJ
is this plug still working? or.. hot to install it...
unless there is another way since 2014 to create curve based mesh...
would love some advice here
thnx
Dec 27, 2016