Super Duper Ellipse

I was hanging out at the Mathworld Superellipse page the other day and found an interesting function called the superformula. Since it is really just a generalization of the super-ellipse, I prefer the name super duper ellipse. It is given in polar coordinates as:
4-1 (1)

Shaderization

This is an explicit form of the function and we need it in the implicit form to use in a shader. If you don't understand what I mean by the implicit form, check out ImplicitFunction. Maybe I'll write about that sometime. Anyway, to make Equation (1) implicit, we just have to rearrange it.
4-2 (2)
or
4-3 (3)

Quick Note: Be very careful of blowing up equations like this when there variables in the denominator that can go to 0. For example, an alternative way of expressing Equation (2) would have 4-4 as the denominator instead of the numerator. This would be bad because 4-5 near the center.)

The shader code might look like this (for simplicity I'm sticking everything in one function for now).

 1 
 2 /* super duper ellipse (aka superformula) */
 3 float supdupell(float x, y; float a, b, m, n1, n2, n3;)
 4 {
 5   /* polar coordinate conversion */
 6   float r = sqrt(x*x + y*y);
 7   float theta = atan(y, x);
 8 
 9   /* superformula */
10   return r * pow(pow(abs(cos(1/4*m*theta)/a), n2) +
11 	         pow(abs(sin(1/4*m*theta)/b), n3), 1/n1);
12 }

So what does this function look like? Let's find out...
1 
2 Ci = supdupell(s, t, 1, 1, m, n1, n23, n23);
(3, 5, 10)
(4, 12, 15)
(7, 10, 6)
(5, 4, 4)
(5, 2, 13)

Quick Note: From the paper, these examples have 4-6 , 4-7 and the tuple under each image is 4-8

Hey, notice that each of these have a nice gradient on them? That is a product of their representation with an implicit function. Since they have a nice gradient, Let's chuck some profiles onto them...
(7, 10, 6) (5, 4, 4) (5, 2, 13)
Ci = prof_step(0.8, f);
Ci = prof_filterring(0.6, 0.8, f);
Ci = prof_cos(f);
Ci = prof_round(f);

Alternative View

I know what you're thinking. You're thinking "So now we've got this groovy unit shape function based on the super duper ellipse, let's stick it in our shape library". Well, actually, let's not. Based on our discussion in the last note many interesting shapes can be made from simple coordinate transformations and simple shapes. From the description of the super duper ellipse we should already have a good idea of what the transformations and shape should be. The super duper ellipse is a generalization of the super-ellipse, which in turn is a generalization of the the ellipse, which in turn... well you get the idea. This seems to indicate the unit shape should be a circle. Let's see if we can get there more quantitatively.

Let's start with Equation (3) again. Since we'll eventually want to linearize the function, let's keep track of 4-9 as well. First, get rid of the nasty 4-10 exponent
4-11 (4)

and then suck 4-12 into the brackets and distribute it to the resulting terms
4-13 (5)

Hey, this is starting to look familiar. Let's pull an arbitrary 2 out of the exponents.
4-14 (6)

Now just make the substitution
4-15 (7)

and we get a circle!
4-16 (8)

This whole exercise just reiterates what I talked about in the previous note on Coordinate Transformation. Many complicated, nasty functions can be represented by a simple shape with coordinate transformations.

Let's implement that super duper ellipse shader again, this time using the unit circle. This time the super duper ellipse functionality is implemented as a set of coordinate transformations. Also, we'll need to linearize the function since the results in the last implementation are linearized. The linearization function can be found from Equation (7).
4-17 (9)
4-18 (10)

Since Equation (8) will be implemented with the unit_circle function we don't have to worry about linearizing it.

 1 
 2 /* super duper ellipse (aka superformula) */
 3 void xform_supdupell(float x, y; float a, b, m, n1, n2, n3;
 4                      output float x_new, y_new)
 5 {
 6   /* polar coordinate conversion */
 7   float r = sqrt(x*x + y*y);
 8   float theta = atan(y, x);
 9 
10   /* superformula coordinate transform */
11   x_new = pow(r, n1/2) * pow(abs(cos(1/4*m*theta)/a), n2/2);
12   y_new = pow(r, n1/2) * pow(abs(sin(1/4*m*theta)/b), n3/2);
13 }
14 
15 /* super duper ellipse (aka superformula) linearization function */
16 float xform_supdupell_lin(float r_new; float a, b, m, n1, n2, n3)
17 {
18   return pow(r_new, 2/n1);
19 }
1 
2 float x_new, y_new;
3 xform_supdupell(s, t, 1, 1, m, n1, n23, n23, x_new, y_new);
4 float r_new = unit_circle(x_new, y_new);
5 Ci = xform_supdupell_lin(r_new, a, b, m, n1, n2, n3);
(3, 5, 10)
(4, 12, 15)
(7, 10, 6)
(5, 4, 4)
(5, 2, 13)

Super Shapes

Yes, splitting up the transformation into two portions is really ugly. Maybe I'll come up with a better way of expressing this later. For now, though, let me point out that we could put any shape through this transformation, not just the circle.

1 
2 float x_new, y_new;
3 xform_supdupell(s, t, 1, 1, m, n1, n23, n23, x_new, y_new);
4 float r_new = func(x_new, y_new);
5 float g = xform_supdupell_lin(r_new, 1, 1, m, n1, n23, n23);
Ci = prof_step(0.8, g);
Ci = prof_filterring(0.6, 0.8, g);
Ci = prof_cos(g);
Ci = prof_round(g);
circle box stripe

Whoa, That's crazy! We went from the superformula which is a generic form for a super-ellipse, into a form that gives us arbitrary generic super-shapes. That, my friends, is a thing of beauty.

This page last edited: 2006/03/05 14:47:09
Copyright © 2003-2006 Peter Stuart