November 2014 Archives

Spherical Functions as Voxels

  • Posted on November 29, 2014 at 6:07 pm
Voxel posts

  1. Intro
  2. Cartesian Functions
  3. Spherical Functions (this post)
  4. Curve functions

To convert spherical functions into a voxel volume is a bit, but not much, more coplicated than for Cartesian functions.

In the case of Cartesian functions the key criterion was the height above the x-y plane. In the case of a spherical function, it’s the distance from the centre point of the function that we’ll need to perform the comparison on.

We define our spherical functions using angles θ and φ circumlocating this point. The angle θ represents the angle around the z-axis through the centre point of the object. The angle φ represents the elevation above the x-y plane also through the centre.
Converting to a triangulation involves stepping through θ and φ with a given step distance and calculating the radius r = f(θ, φ) for the function. This radius acts like a spine emerging from the centre at the given angle and with length r as defined by the function. These spines are used to define the vertices of the triangles that form the surface of the object.

When voxelating the volume, the question becomes one of whether a given voxel, defined by its (x, y, z) position, is inside or outside the object. We do this by considering the spine that runs between the voxel co-ordinates and the centre of the object, and calculating the θ and φ values that would apply in this case.

The contrasting nature of the two approaches shows itself here. To create triangles you have to calculate the (x, y, z) position of a triangle vertex from the θ and φ values. In both cases the θ and φ are needed to calculate the radius.

Calculating θ (rotation) and φ (elevation) values isn’t hard with a bit of trigonometry.

θ = tan-1 (y / x)

and

φ = tan-1 (z / (x2 + y2)1/2)

Here’s the code we use to do this.

fZFunc = psFuncData->fZMin + (nSlice * fZStep);
for (nX = 0; nX < nResolution; nX++) {
	fXFunc = psFuncData->fXMin + (nX * fXStep);
	for (nY = 0; nY < nResolution; nY++) {
		fYFunc = psFuncData->fYMin + (nY * fYStep);

		// Calculate the angles
		fOpposite = (fXFunc - psSphericalData->fXCentre);
		fAdjacent = (fYFunc - psSphericalData->fYCentre);
		fElevation = (fZFunc - psSphericalData->fZCentre);
		fAFunc = atan2 (fOpposite, fAdjacent);
		fDiag = sqrt ((fAdjacent * fAdjacent) + (fOpposite * fOpposite));
		fPFunc = atan2 (fElevation, fDiag);

		if (psSphericalData->psVariableA) {
			SetVariable (psSphericalData->psVariableA, fAFunc);
		}

		if (psSphericalData->psVariableP) {
			SetVariable (psSphericalData->psVariableP, fPFunc);
		}

		fRFunc = ApproximateOperation (psFuncData->psFunction);
		fDistance = sqrt ((fAdjacent * fAdjacent) + (fOpposite * fOpposite) + (fElevation * fElevation));

		if ((fDistance < fRFunc) && ((psFuncData->boMaterialFill == TRUE) || (fDistance > (fRFunc - psFuncData->fMaterialThickness)))) {
			ucFill = 255u;
		}
		else {
			ucFill = 0u;
		}
	}
}

Once we have them, we can plug them into the function that defines our spherical object to calculate the radius at that point r‘ = f(θ, φ). We compare this with r, the length of the spine from the centre of the object to the position of our voxel.

r = (x2 + y2 + z2)1/2.

Notice we’re pretending here the co-ordinates of our object are at the origin. If they aren’t, we need to subtract the centre co-ordinates component-wise from our voxel co-ordinates first.

The comparison is then simply rr‘ if we’re inside the object and r > r‘ if we’re outside.

Calculating the inside and outside of the spherical volume (so, okay, it's an area here)

Calculating the inside and outside of the spherical volume (so, okay, it's an area here)

We perform these calculations and comparison for each voxel in our voxel space. As before, if we’re inside we set the voxel to be filled, otherwise we leave it empty.

That deals with Cartesian and spherical functions, which are the easy ones, because the mapping between voxels and the co-ordinate system is one-to-one. Sadly this isn’t the case for curve functions, which I’ll tackle in the next post.

Cartesian Functions as Voxels

  • Posted on November 28, 2014 at 10:06 pm
Voxel posts

  1. Intro
  2. Cartesian Functions (this post)
  3. Spherical Functions
  4. Curve functions

Of the three, the easiest class of functions to turn into voxels was the Cartesian functions. In order to understand the process the key thing I eventually realised was that voxelisation is almost the reverse of triangulation.

For the latter, we step through the x and y co-ordinates at a given resolution and calculate the consequent z-values to define the vertex co-ordinates of the triangle.

In contrast, for the former we step through not just the x and y co-ordinates, but also the z co-ordinate. The question we then have to answer is whether this (x, y, z) point is inside or outside the volume.

In practice, we can’t answer this question without defining the boundaries of the volume. Functy has an option to specify the thickness for a function, but the simplest case is to say anything on or below the function that defines the surface is inside the volume, and anything above is outside.

surfacevolume02

Inside and outside a Cartesian surface volume

The z-value generated by the function for given x and y co-ordinates represents the height of the function above the x-y plane. For a given voxel position (x, y, z) we therefore calculate the height of the function z‘ = f(x, y) at the point x, y and compare this to our actual position (x, y, z). If zz‘ then we’re inside the volume, so should fill in the voxel. If z > z‘ on the other hand, we’re outside the volume, so should leave the voxel empty.

And that’s it. To voxelise the function we check each of the points that make up the voxel space, perform the comparison, and either fill in or leave empty the voxels as we go. There are a lot of voxels to go through because we’re working with cubic dimensions (so even a low resolution of 100 steps per dimension gives us a million separate voxels to consider), but the comparison to perform is pretty straightforward in itself, as is clear from the code that performs the check.

fZSlice = psFuncData->fZMin + (nSlice * fZStep);

for (nX = 0; nX < nResolution; nX++) {
	fXFunc = psFuncData->fXMin + (nX * fXStep);
	for (nY = 0; nY < nResolution; nY++) {
		fYFunc = psFuncData->fYMin + (nY * fYStep);

		if (psCartesianData->psVariableX) {
			SetVariable (psCartesianData->psVariableX, fXFunc);
		}
		if (psCartesianData->psVariableY) {
			SetVariable (psCartesianData->psVariableY, fYFunc);
		}

		fZFunc = ApproximateOperation (psFuncData->psFunction);

		if ((fZFunc < fZSlice) && ((psFuncData->boMaterialFill == TRUE) || (fZFunc > (fZSlice - psFuncData->fMaterialThickness)))) {
			ucFill = 255u;
		}
		else {
			ucFill = 0u;
		}
	}
}

Spherical functions are a little more complicated, but not much. I’ll write about them in the next post.

Voxel Volumes

  • Posted on November 28, 2014 at 8:37 pm

One of the main feature additions of the latests version of Functy has been the ability to export as SVX files. Functy could already export in PLY and STL, but both of these are triangle based. They represent the 3D functions as surfaces defined by carefully aligned triangle meshes. Rendering objects using a graphics card also uses the same triangulation process, so exporting as PLY or STL is a very natural extension of the existing rendering.
The SVX format is different though. It stores the models as a voxel image (a voxel being a three dimensional pixel, for those who didn’t grow up through the 90s demo scene). As a result, SVX doesn’t just store the surface, but also the volume of a function.
Turning a triangulated surface into a voxelated volume isn’t necessarily straightforward, but Functy has the advantage of having all its objects originate as purely mathematical forms. In theory, this means voxel rendering them as volumes should be quite easily.
What I found in practice is that for Cartesian functions and spherical functions this is true: they can be turned into voxel volumes in a very natural way. Curve functions are a different story though. In the next few posts I’ll go through each of the processes separately, to give an idea about how the solutions for each of the three function types were coded.

Voxel posts

  1. Intro (this post)
  2. Cartesian Functions
  3. Spherical Functions
  4. Curve functions

Functy 0.33 now available

  • Posted on November 2, 2014 at 9:03 pm

This latest version has two main new features. First it allows animations to be exported as a series of PNG frames. Second you can also now export in Simple Voxels (SVX) format. This is a neat new format that represents a model as a volume rather than a surface as would normally be the case with STL or PLY files. Functy is ideally set up for this, since the objects are mathematically defined anyway. SVX files are also ideally suited to 3D printing, which ultimately is working with 3D volumes too.

Even though it’s new, Shapeways already allow upload of models in SVX format, and you can see an example - my first attempt - on the Shapeways site.

There have been some more minor improvements too. For the first time ever, Functy now has an About window. Only a small thing, but long overdue. It’s been on the back-burner for a while, since until recently Ubuntu’s version of Glade crashed when creating dialogues. Ironically I didn’t end up including it in the gtk-builder file anyway. Never mind. There’s also a new Progress window to make long exports bearable. SVX export can take some time, so this became a necessity. But it also helps with exporting of animations too, since these can also take a surprising length of time when every frame has to be generated as a separate file.

Please get yourself a copy and try out the new features. As with every release of Functy since the dawn of time, this is still a beta version, so please bear this in mind and let me know if things go wrong.

As always, the binary and source is available direct from Sourceforge or via the downloads page.

Functy with new export progress window

Functy with new export progress window

Top