# Cartesian

## Cartesian Functions as Voxels

- Intro
- Cartesian Functions (this post)
- Spherical Functions
- 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.

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 *z* ≤ *z*‘ 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

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.

- Intro (this post)
- Cartesian Functions
- Spherical Functions
- Curve functions

## Comparing implicit and parametric functions

In an earlier post I talked about Sederberg *et al.’s* paper that uses Elimination Theory to demonstrate how parametric curves can be represented in implicit form. Reading through the literature it quickly becomes clear that this is important work if you’re interested in rendering parametric curves or surfaces. Unfortunately it can be difficult to get to grips with the theory without also being able to play around with the functions themselves. Consequently I’d expected to spend much of my summer writing software to render the different types of curves for exploring them and play around with their different representations.

That was, until I realised Functy was quite capable of doing it already. Functy’s parametric curves are already perfectly suited to the rendering of parametric equations. This part might be obvious. Less obvious for me was that the colouring of a flat Cartesian surface is perfect for the rendering of the implicit form.

Above are a couple of screenshots showing the two types of function. These are both taken from the example in another paper by Sederberg, Anderson and Goldman about “Implicitization, Inversion, and Intersection of Planar Rational Cubic Curves” (available from ScienceDirect). The curve is a quartic monoid which can be expressed parametrically and implicitly as follows.

Implicitly:

(*x*^{4} - 2*x*^{3}*y* + 3*x*^{2}*y*^{2} - *xy*^{3} +*y*^{4}) + (2*x*^{3} - *x*^{2}*y* + *xy*^{2} + 3*y*^{3}) = 0

Parametrically:

*x* = - (3*t*^{3} + *t*^{2} - *t* + 2) / (*t*^{4} - *t*^{3} + 3*t*^{2} - 2*t* + 1)

*y* = - (3*t*^{4} + *t*^{3} - *t*^{2} + 2*t*) / (*t*^{4} - *t*^{3} + 3*t*^{2} - 2*t* + 1)

In the screenshots the red line is the parametric version of the curve for *t* in the interval (0, 1). The other colours on the surface represent the values of the implicit function. Note that the implicit function actually lies at the boundary of the yellow and blue areas. You can see this slightly better in the 3D version, where the height represents the value of the implicit function. The actual curve occurs only where this is zero - in other words where the surface cuts through the plane *z* = 0.

It was reassuring to see that the parametric curve matches the implicit version. It’s also interesting to note that the implicit version is rendered entirely using the shaders in a resolution-independent way. It’s possible to zoom in as much as you like without getting pixelisation. This is exciting for me since, although it’s not what I’m really trying to achieve (that would be *too* easy!), it hints at the possibility.