
User functions with parameters
It is now possible to declare user functions that have extra parameters.
#declare Gravity_Well =
function(x,y,z,Strength) {
x*x Strength/y*y +z*z
}
declares a function that takes an extra "Strength" parameter.
We don't tell the function what the value of "Strength" is when we declare it,
but only when we reference it
isosurface {
function { Gravity_Well(x, y, z, 0.002) }

#declare S = function {
spline {
linear_spline
1.0, < 0.5, 0, 0>,
0.5, < 0, 0, 0>,
0.01,< 0.2, 0, 0>,
0.5, <0.2, 0, 0>,
1, <0.2, 0, 0>
}
}
isosurface {
function { y  S(x).x }

Spline Functions
It is now possible to use a spline function to generate an isosurface.
In this case I'm using the first column of the spline only.
As x goes from 1 to +1 S(x) goes from <0.5,0,0> to <0.2,0,0> along the spline.
For an isosurface, we don't want a vector function, so we choose the S(x).x component
of the function.
We can see that the isosurface follows the x coordinate of the spline, which is shown in red in the attached image.
Warning: There are known bugs in spline functions in POV 3.5b1.
For example, if we replace the 0.01 point in the above example by
0.0, < 0.2, 0, 0>,
the spline behaves in an unexpected manner.

#declare S = function {
spline {
natural_spline
1, < 0.5, 0, 0.0>,
0.5, < 0.2, 0, 0.4>,
0.01, < 0.2, 0, 0.2>,
0.5, < 0.4, 0, 0.4>,
1, < 0.0, 0,0.6>
}
}
isosurface {
function { y  S(x).x  S(z).z }

But we're not restricted to using just one dimension of the spline, or to using
linear splines.
Here's a natural spline function.
We can see that the surface follows both the x and z coordinates of the spline,
which are shown in red and green respectively in the attached image.
In this way, it's possible to generate a 3d isosurface sheet from two 2d splines.
The new POV 3.5 "cubic_spline" is not very suitable for isosurface work because
it can be undefined at the endpoints, which causes strange effects in the surface.


Another thing that we can do with spline functions is to sweep along a 3d spline,
in a similar manner to a sphere_sweep.
#declare S = function {
spline {
natural_spline
1, < 0, 0.5, 0.0>,
0.5, < 0, 0.2, 0.4>,
0.01, < 0, 0.2, 0.2>,
0.5, < 0, 0.4, 0.4>,
1, < 0, 0.0,0.6>
}
}
isosurface {
function { pow(y  S(x).y),2)
+ pow(z  S(x).z,2)  0.05 }
What you get isn't exactly the same as a sphere_sweep, it's more like
a "hoop sweep" with the hoop always oriented in the same direction
rather than turning as the spline turns.
It's also possible to write it in a way that more clearly separates
the "hoop" function from the spline function, making it easier to replace the
circular hoop with some other shape.
#declare Hoop = function(x,y,z,r){y*y + z*z  r*r}
isosurface {
function {Hoop(x, yS(x).y, zS(x).z, 0.223)}
In the upper image, I've made the isosurface slightly transparent and indicated the path
of the spline itself in yellow.
In the lower image I've added a ripple to the isosurface, so now you can
drape intestines along a 3d spline path.

parametric {
function {S(u).x + sin(v)}
function {S(u).y + cos(v)}
function {S(u).z}
<0,pi><17,pi>
contained_by{box
{min_extent(The_Path)<1,1,0.1>
max_extent(The_Path)+<1,1,0.1>}
}

But that previous technique doesn't work with splines that have loops in them.
The problem is that we were using the spline control points as our x coordinate
and expressing y and z in terms of that x.
The sequence of control points can't loop back on itself.
The way to follow 3d splines that contain loops is to use all three of the spline
dimensions to specify the path of the spline, and express x, y, and z in terms of
a fourth variable, call it "u", which follows the control points.
So what we need is a parametric isosurface.
In this case "u" follows the spline control points, and x, y, z are expressed in terms
of u like S(u).x, S(u).y, S(u).z. On its own that would give an infinitely
thin string that follows the spline path. We can fatten it out by adding some terms in a second
variable "v" which describe how far the surface is from the path.
Once again I have made the isosurface transparent and drawn the actual spline path
inside it in yellow.
For this image, I didn't know how large to make the contained_by box, so I've
calculated it during the parsing of the scene by taking the min_extent() and
max_extent() vectors of the yellow spline path and added something to allow for
the thickness of the wrapping.


You may have noticed that in the previous examples, the swept shape always faces
in the same direction. In the attached zip file I've included the SweepSpline macro that
sweeps a shape in such a way that it turns to always be perpendicular to the
spline that it is being swept along.
It's not actually an isosurface at all, but a mesh. I just stuck it here since it's
doing a similar job to some of the isosurface examples on this page.

