Coding Curves 13: Superellipses and Superformulas

coding curves

Chapter 13 of Coding Curves

In this chapter we’ll be talking about some interesting shapes. Superellipses are quite useful in design and UI work, especially a specific superellipse we’ve come to know affectionately as the “squircle”. These are basically rounded rectangles, but with some neat properties. Superformulas are an extension of superellipses. They are more complicated, probably less useful, but interesting in their own right. Let’s dive in.

Superellipses

Sometimes you need to draw a rectangle for something. We’ve all been there. Drawing a rectangle is easy enough in almost any graphics package. So you draw a rectangle.

But that’s kind of boring. Those square corners are just so… square. So you come up with a rounded rectangle. Some drawing apis have this built in, but it’s not too hard to hand code. You draw a 90-degree arc at each corner and a line between each arc. This is ok.

In fact, you can make those corners have a relatively small radius, or you can make it quite round:

But you might not love this. There’s kind of a break in continuity in the edges. It’s going along nice and straight and then it abruptly turns into an arc. Maybe you want something that smoothly transitions into that round corner. Enter the superellipse.

You can think of a superellipse as a blend between an ellipse and a rectangle. Like an ellipse, and many of the other curves we’ve drawn in this series, we’ll consider this shape as a center point and a varying radius drawn from a t of 0 to 2 * PI. This is different from a lot of rectangle functions which take an x, y location (usually top, left of the shape) and a width and height. But you can adjust the function as you want.

While there are different ways to express a superellipse mathematically, we’ll dig around until we find one that is parameterized with that 0 to 2 PI t value. And here is is:

x = pow(abs(cos(t)), 2 / n) * a * sign(cos(t))
y = pow(abs(sin(t)), 2/ n) * b * sign(sin(t))

Hmm… more complicated than you might have expected?

There is a somewhat simpler formula that looks like this:

x = pow(cos(t), 2 / n) * a
y = pow(sin(t), 2 / n) * b

But the problem with this is that it only handles one quadrant: 0 to PI / 2. Rather than running a for loop four times, once for each corner, and figuring out the signs of x and y, we do some fancy math with abs and sign.

Your language probably has an abs function, but it just returns the absolute value of a value. You could easily write your own like:

function abs(val) {
  if (val < 0) {
    return -val
  }
  return val
}

This will always return zero or a positive value.

You might not have a sign function though. This just returns -1 if the given value is negative, and +1 if it’s positive. Returns 0 for 0. You can write this like so:

function sign(val) {
  if (val < 0) {
    return -1
  }
  if (val > 0) {
    return 1
  }
  return 0
}

You might be tempted to get fancy and do something like:

function sign(val) {
  return val / abs(val)
}

This works wonderfully. Until val is 0. Then it crashes. So you’ll still need a conditional in there to catch that case.

Now in the formula I gave above, there’s some duplication and some not very clear variables, and there’s no way to position the shape. So here’s an actual function that’s more usable:

function superellipse(xc, yc, rx, ry, n) {
  for (t = 0; t < PI * 2; t += 0.01) {
    c = cos(t)
    s = sin(t)
    x = pow(abs(c), 2 / n) * rx * sign(c)
    y = pow(abs(s), 2 / n) * ry * sign(s)
    lineTo(xc + x, yc + y)
  }
  closePath()
}

Most path drawing apis have some sort of closePath function that draws a final line back to the starting point.

And there you go. Let’s recreate our above rounded rectangle using a superellipse.

width = 600
height = 400
canvas(width, height)

// set color to orange however you do that...
superellipse(300, 200, 250, 150, 10)
fill()

You might or might not like that better than the simple rounded rectangle, but let’s explore it some more. That last parameter, n, controls how curved the corners are. The higher the value, the closer you get to a rectangle. Here is an n of 20:

And here is an n of 4:

This looks like the screen shape of an old fashioned TV set.

And this is a good time to bring up squircles.

Squircle

A superellipse where the x and y radii equal is sometimes also known as a supercircle. And has also been called a squircle. A combination of a square and circle. Some definitions of squircle state that it must have an n of 4. It looks like this:

This shape looks very satisfying, and has become very popular recently in user interface design. It’s often used for icons, especially for those used to launch apps on mobile devices. Sometimes the n value might be not quite 4 as above, but close enough.

There is actually an alternate formula for a squircle which John Cook wrote about at https://www.johndcook.com/blog/2022/10/27/variant-squircle/. His site is a great resource by the way. I can’t say I always understand all of what he is talking about, but he’s sent me down many very interesting paths of exploration.

Back to Superellipses

Let’s explore that n parameter a bit more. We already saw that as we make it larger, the corners get tighter. When we get an n of 2, something interesting happens. We just get an ellipse (or a circle if the height and width are equal).

When we go from 2 down to 1, we see the straight edges turn to corners, and the corners become more straight. Here’s 1.5:

When we get down to n = 1, corners and edges have completely reversed and we get a diamond shape.

Then as we go below 1, the corners start curving inwards. Here’s 0.75:

At 0.2, the superellipse is almost disappearing.

It will be essentially invisible with an n just a bit lower than that.

And when you go negative, things get really weird. Here’s n at -4. I had to reduce the size a bit so you could see what’s going on – we get a filled rect and some inverted superellipses extending out of each corner.

Not sure there’s any use for that last part, but there it is.

And that’s about it for superellipses. A great shape to have in your graphics toolbox. But let’s move on.

Superformulas

A superformula is a generalization or extension of s superellipse. Again, we have a radius that differs as we go from 0 to 2 * PI. The formula for that radius is:

That’s straight from the Wikipedia article on superformulas. It was easier to copy and paste that than it would be to try and type out the formula in a way that made sense. We’ll convert that to pseudocode shortly, but you should see some similarities between this and the superellipse formula. You see that we take the cosine and sine of something, divide it by a value, then take a power of the absolute value of that. The squiggly symbol there is the Greek letter phi, and it’s what we will call t. So we’re saying the radius at angle t is …

Then we have some other variables, m, n1, n2 and n3 as well as a and b which influence the x and y radii. In my implementation I’m going simplify it by just having a single radius. So I’ll set a and b to 1, which means we can just ignore them in the code and multiply the result by the radius we want.

Since this is so complicated, I’ll break down some of these parts into separate variables and then combine them for the final result.

First we’re taking the cosine and sine of the same value, so we can precalculate that. And I’ll call m symmetry.

angle = symmetry * t / 4

Then in between the big parentheses we have two terms we are adding together. We’ll calculate each of those:

term1 = pow(abs(cos(angle), n2)
term2 = pow(abs(sin(angle), n3)

Again, we’re considering a and b to be 1, so we can ignore them here.

Next we can plug these terms into the rest of the formula to get the final radius. Remember, we multiply that by the overall radius we want:

r = pow(term1 + term2, -1/n1) * radius

And finally, use the radius and angle to get the next point to draw to:

x = xc + cos(t) * r
y = yc + sin(t) * r

Put all together, this is our function:

function superformula(xc, yc, radius, symmetry, n1, n2, n3) {
  for (t = 0; t < 2 * PI; t += 0.01) {
    angle = symmetry * t / 4
    term1 = pow(abs(cos(angle), n2)
    term2 = pow(abs(sin(angle), n3)
    r = pow(term1 + term2, -1/n1) * radius
    x = xc + cos(t) * r
    y = yc + sin(t) * r
    lineTo(x, y)
  }
  closePath()
}

OK, now what the heck can we do with this? And what are all those parameters supposed to control?

When I’m faced with something like this, I usually start by finding some parameters that create some relatively simple, stable configuration and then start tweaking just one of the parameters to see what it changes. Then move on to another, and eventually look at how various parameters interact. Fortunately, the Wikipedia page on superformulas gives us this nice chart as a starting point:

The numbers on top of each image are the parameters m (what we call symmetry), n1, n2 and n3.

So we can immediately see that the symmetry parameter controls how many “nodes” will be in the shape.

So here’s a symmetry of 3, with all the n parameters set to 1:

And symmetry 5:

And 8:

Now, keeping symmetry at 8, let’s change n1 to 10:

Then down to 3:

And then below 1, down to 0.2:

OK, so we get that n1 makes the sides of the shape bulge in or out.

Alright, now we’ll stick with symmetry 8 and put n1 back to 1. Then we’ll up n2 to 1.5:

We still have 8 nodes, but every other one is a bit rounder, and the in-between nodes are a bit sharper. This gets more obvious at n2 = 2:

At n2 = 5, the rounder nodes have started to double, and the sharper ones are receding:

And here we are at n2 = 10. I had to reduce the radius because the shape was outgrowing the canvas:

Finally, setting n1 and n2 back to 1, we can try increasing n3 to 3:

This has a similar effect to that of n2 but changes alternate nodes, as if the whole shape was rotated 45 degrees.

At this point, I have a pretty good idea of what’s going on and I can just start trying some random parameters:

Symmetry 16, n1=0.5, n2=0.75, n3=2

Symmetry 32, n1=0.9, n2=0.2, n3=-0.3

Anyway, you don’t need me to choose random numbers for you at this point. Play with it. Use big numbers, small numbers, fractions, negatives, primes, numbers that are divisible by each other or not. And see what happens.

Remember, too, that we removed the a and b values from the original formula and used a single radius. You might want to try putting those back in and create some elliptical superformulas. The Wikipedia page also gives an alternate formula where the m parameter is split into two separate values, y and z:

This can allow for even more complex shapes.

I played around with this a little and came up with shapes like this, which I quite like:

But I’ll leave the coding of that up to an exercise for the reader.

Coding Curves 12: Guilloche Patterns

coding curves, misc

Chapter 12 of Coding Curves

Guilloche patterns are very intricate and fascinating patterns. You’ll often find them on bank notes and other official documents as well as watches and other intricate machinery. Because they are so intricate and complex, and often engraved into metal, they are usually done by machines themselves. Imagine a high end spirograph machine with a metal etching tool rather than a ball point pen. The name “Guilloche pattern” is rather vague and can apply to all kinds of similar patterns. I’m going to explain how to make a pattern like the one you see here:

three ringed guilloche pattern

This is a lot like the kind of thing you’d see on some kind of certificate or bank note, and once you understand what’s going on, you can adapt the code to make other similar patterns.

Step One – A Simple Ring

We’ll start by making a single, simple ring that looks like this:

a circle composed of many looping waves

This is very much like a trochoid or a rose curve. In fact, you could probably make this with those formulas, but I’m going to do it a bit differently to set it up for the more complex stuff we’ll do later on.

This is essentially a sine wave that’s wrapped around a circle. Note that it has an inner radius and an outer radius. The sine wave has 80 nodes, but it overlaps itself. To simplify what’s happening here, I’ll increase the inner radius and make it so it doesn’t overlap:

a circle drawn with a sine wave instead of a single curve

Now you can see more clearly that this is, as I said, a sine wave wrapped around a circle. Now I’ll add a little bit of overlap:

a circle drawn with an overlapping sine wave

Here, you should still be able to see the sine wave even with the overlap. The first image is the same idea, but with a lower inner radius and more overlap. Now let’s look how to draw this.

We start with an inner and outer radius. With a bit of easy math we can figure out a “mid” radius. This will be the the zero-point of the sine wave. And we’ll need a range, which is how much the sine wave will have to extend to either side of this radius to hit the inner and outer limits. You might also call this the amplitude of the wave.

width = 600
height = 600
canvas(width, height)

translate(width / 2, height / 2)

inner = 50
outer = 250
range = (outer - inner) * 0.5
mid = inner + range

Next, we need values for how many cycles in the wave, and how much it overlaps. I’ll call these nodes and div. These should be whole numbers and should not be evenly divisible. This is very similar to the n and d parameters in the rose curves we made in the last chapter, but I’ll name them differently to keep things clear.

nodes = 80
div = 11

Now we can loop t from 0 to 2 * PI * div and draw some line segments. The angle of each new point for the next segment is simply t and the radius will be computed as shown. We need to multiply 2 * PI by div to make sure we go around the circle enough times to meet back up correctly.

for (t = 0; t < 2 * PI * div; t += 0.01) {
  radius = mid + sin(t * nodes / div) * range
  x = cos(t) * radius
  y = sin(t) * radius
  lineTo(x, y)
}
stroke()

If you look back at the roses chapter, you’ll see this is very similar to how we got the radius there, but rather than figuring the radius as a sine and single multiplication, we’re using mid and range to fit the radius between inner and outer.

You can play with this a bit. Try different inner and outer radii and number of nodes and values for div. For a good Guilloche pattern, you probably want to keep nodes rather high, and div lower. But Most importantly, they should not be evenly divisible. An easy formula for decent patterns is to make div a small prime number and make sure nodes is not a multiple of div.

For example, if div is 17, you wouldn’t want to use 170 for nodes, or you’ll get this:

a rounded star kind of shape with 10 nodes

But changing nodes to 171 gives you a much better pattern:

a circle drawn with many overlapping loops

If you want to play with an interactive version of this, I made this a few years back:

https://bit101.github.io/lab/dailies/170120.html

Step 2 – A Complex Ring

Next, we’ll add some complexity to the mix. Rather than having our sine wave go back and forth between fixed inner and outer radii, we’ll have these radii themselves vary with a separate sine wave each! The result will look like this:

a single ring of a guilloche pattern

We’ll need a few more parameters for this. To calculate the final outer radius at any point, we’ll need the base outer radius, how many cycles of that sine wave and how far the final radius will differ from the base outer radius. And we’ll need the same three values for the inner radius. And of course we’ll still need nodes and div. The values I used for the above image are:

inner = 100.0
n0 = 7.0
h0 = 10.0

outer = 250.0
n1 = 17.0
h1 = 20.0

nodes = 142.0
div = 89.0

So the inner radius will have 7 nodes, and the radius will vary from 90 to 110, which means a base of 100, plus or minus 10. Similarly the outer radius will have 17 nodes and vary between 230 and 270.

All the code we did early to find the “mid radius” and range for the sine wave will have to be redone for each iteration of the for loop now, using these values and the dynamic inner and outer radii.

for (t = 0; t < 2 * PI * div; t += 0.01) {
  r0 = inner + sin(t * n0) * h0
  r1 = outer + sin(t * n1) * h1

  range = (r1 - r0) * 0.5
  mid = r0 + range

  radius = mid + sin(t * nodes / div) * range
  x = cos(t) * radius
  y = sin(t) * radius
  lineTo(x, y)
}
stroke()

You can surely simplify this code, but I wanted to write it all out explicitly for the sake of clarity.

Again this is a good stopping point to play with some of the parameters and see the different types of shapes you can make. We have one more level of complexity to tackle next.

Here’s an interactive version of this step:

https://bit101.github.io/lab/dailies/170121.html

Step 3 – Multiple Rings

The next step is to create multiple rings that will exactly fit together, like the image that appears at the start of this chapter:

a three ringed guilloche pattern

This is easier than it might seem. If you draw one smaller ring, and then one larger one around that, you just have to make sure that the parameters for the larger ring’s inner radius match the parameters for the smaller ring’s outer radius. Since we’ll be calling the same code multiple times, this would be a good time to turn it into a reusable function. For the most part, this just means wrapping the for loop and stroke call in a function with all the parameters passed in. I also added x and y parameters, so you can center the pattern anywhere on the canvas.

function guilloche(x, y, ir, n0, h0, or, n1, h1, nodes, div) {
  for (t = 0; t < 2 * PI * div; t += 0.01) {
    r0 = ir + sin(t * n0) * h0
    r1 = or + sin(t * n1) * h1

    range = (r1 - r0) * 0.5
    mid = r0 + range

    radius = mid + sin(t * nodes / div) * range
    lineTo(x +cos(t) * radius, y +sin(t) * radius)
  }
  stroke()
}

I upped the canvas size to 800×800 and then used this set of function calls:

guillloche(400, 400, 50, 6, 10, 120, 12, 20, 137, 37)
guillloche(400, 400, 120, 12, 20, 220, 18, 30, 141, 41)
guillloche(400, 400, 220, 18, 30, 350, 24, 20, 164, 53)

This resulted in the following image:

another three ringed guilloche pattern

Note that in the first call, the outer radius params are 120, 12 and 20. These correspond to the inner radius params in the next call. And the outer radius params for the second call, 220, 18 and 30, correspond to the inner radius params on the final call. This way, each ring lines up perfectly.

As usual, play around with this. You can make as many ring as you want. You might want to set up a custom data type that encapsulates all of the radius parameters to make that part more reusable. I’ll leave it to you.

Another thing you might want to try is making each ring a different color:

a colored three ringed guilloche pattern

Here’s an interactive version of the final product:

https://bit101.github.io/lab/dailies/170122.html

Note that there’s no rule that says the rings have to match exactly. Try putting some space between them or letting the overlap and see what kind of interesting patterns you can create that way. This next example has the first and last rings the same as in the previous example, but the middle ring is defined quite differently:

guillloche(400, 400, 20, 4, 5, 120, 8, 10, 137, 37)
guillloche(400, 400, 160, 5, 24, 160, 11, 24, 80, 17)
guillloche(400, 400, 220, 18, 16, 350, 10, 20, 164, 53)

Here, the inner and outer radius is the same, 160, which makes some interesting blobs rather than a discrete ring. All is fair in Guilloche patterns!

a three ringed guilloche pattern. the inner ring does not tightly interlock with the others

And of course, don’t forget to animate!

an animated guilloche pattern

Summary

Do an image search for Guilloche patterns and you’ll find things that are much different, and often more complex than what I’ve presented here. This is just one take on them.

You might try altering the code do draw something other than circles. Ellipses would be a good place to start. And then move onto other shapes. But all that is a bit beyond the scope of this chapter.

Coding Curves 11: Roses

coding curves

Chapter 11 of Coding Curves

Now we come to another one of my favorite types of curves – roses or rose curves. To me, these look a lot like circular Lissajous curves, or very regular harmonographs. In fact, they are a special instance of hypotrochoids, but special enough to look at on their own. Just to give you some instant visuals, here’s a rose curve:

Like many other curves we’ve looked at, we can get a parameterized formula that will take a t value that goes from 0 to 2 * PI and give us back a value that will let us plot the curve. Let’s look back at the formula for a circle first…

function circle(x, y, r) {
  for (t = 0; t < 2 * PI; t += 0.01) {
    x1 = x + cos(t) * r
    y1 = y + sin(t) * r
    lineTo(x1, y1)
  }
}

You could simplify that into a single line within the for loop, but I wanted to spread it out for clarity.

A rose curve uses the same strategy, but instead of a fixed radius, that radius is constantly changing, also based on the t value, as well as another parameter. Here’s the formula for the radius:

r = a * cos(n * t)

So we have two new variables here. a is the overall radius of the rose, and n controls the number of petals in the rose (although the petal count gets a bit complicated, so we’ll come back to that shortly). So we can make a rose function like this:

function rose(x, y, a, n) {
  for (t = 0; t < 2 * PI; t += 0.01) {
    r = a * cos(n * t)
    x1 = x + cos(t) * r
    y1 = y + sin(t) * r
    lineTo(x1, y1)
  }
}

And now, if you want to, you can clean this up a bit:

function rose(x, y, a, n) {
  for (t = 0; t < 2 * PI; t += 0.01) {
    r = a * cos(n * t)
    lineTo(x + cos(t) * r, y + sin(t) * r)
  }
}

For now, let’s just say that n should be a positive whole number. But we’ll explore ranges beyond that of course.

Now we can draw our first rose like so:

width = 800
height = 800
canvas(800, 800)

rose(width / 2, height / 2, width * 0.45, 5)
stroke()

I’ll be using the width * 0.45 a lot here. It just makes the radius a bit less than half the size of the canvas, so the curve will go almost to the edge of the canvas, but never hit it.

And this gives us a 5-petal rose:

The first example at the top of this page used an n of 7. And here is a rose with an n of 11:

So far we’re seeing a good correlation between n and the number of petals. At least for odd values of n. But what if we use an n of 4?

Interesting. This gives us eight petals. This holds true for any value of n. Odd values create n petals. Even values create 2 * n petals. Just to go way out in one direction, here’s one with n = 40, which gives 80 petals. I had to up the resolution – incrementing t in the for loop by 0.001 to keep it from getting jagged.

In the opposite direction, going down to n = 1, gives you a single node:

A bit strange, but it works out mathematically. You’ll find that for negative values, the rose looks the same as for positive values of n. Here’s 5 on the left and -5 on the right:

Unsurprisingly, n = 0 gives us nothing. And so that covers all the whole number roses. If that’s all there was to roses, it would be nice, but there’s a lot more to go.

An Alternate Rose

Actually, before I move beyond whole numbers of n, I want to just mention an alternate rose formula. Instead of using cosine in the radius formula, you can use sine instead:

r = a * sin(n * t)

This gives you the same roses as the original, but rotated. Here’s a 5-petal rose using the original cosine on the left and sine on the right:

And the same for a 8-petal rose (n = 4):

The actual amount of rotation is PI / (2 * n) radians, or 90 / n degrees. For odd values of n, this always has the visual effect of rotating the rose by 90 degrees (the actual rotation may be different, but due to rotational symmetry, it appears to rotate 90 degrees). For even values of n, it rotates the rose so the petals will now be where the spaces between the petals were in the original version.

Fractional values of n

Things start to get more interesting when we start using fractional values for n. We can try it generating a rose with:

rose(width/2, height/2, width * 0.45, 5.0 / 4.0)
stroke()

But this gives us, rather disappointingly, the following:

The problem is that it’s going to have to go beyond 2 * PI to finish it’s cycle. How far beyond? Well, to figure that out programatically, we’ll need to first ensure that the n value is rational. If it’s an irrational number, the rose will continue forever without reaching its exact starting point. We’ll also need to know both the numerator and denominator of that fraction. We can adjust the rose function to take an extra value, so we have n and d for numerator and denominator.

rose(x, y, a, n, d) {
  for (t = 0; t < 2 * PI; t += 0.01) {
    r = a * cos(n / d * t)
    lineTo(x + cos(t) * r, y + sin(t) * r)
  }
}

This doesn’t solve the problem yet, but gets us the first step. If you want you can enforce n and d to be integers to make sure you’re getting a rational fraction, but make sure you convert them so the division in line 3 returns a floating point value.

Now we need to change the for loop limit from 2 * PI to the actual value we need. That limit value is:

limit = PI * d * m

But what is this new m value there? Well, m should be equal to 1 if d * n is odd. And m should be 2 if d * n is even. Woo! A bit complex. But we can simplify it.

We usually test for evenness by taking a number modulo 2. If the result is 0, that means the number is even. If the result is 1, the original number is odd. So we want:

m = 1 when d * n % 2 == 1

and

m = 2 when d * n % 2 == 0

So we can say:

m = 2 - d * n % 2

This gives us:

rose(x, y, a, n, d) {
  m = 2 - d * n % 2
  limit = PI * d * m
  for (t = 0; t < limit; t += 0.01) {
    r = a * cos(n / d * t)
    lineTo(x + cos(t) * r, y + sin(t) * r)
  }
}

Remember, if you are enforcing integers for n and d, you might need to do some casting or conversion to make everything work correctly. I’ll leave that to you. Now we can redo the fractional one like so:

rose(width/2, height/2, width * 0.45, 5, 4)
stroke()

And now we get something much nicer:

This time, the rose continued all the way around and completed itself.

Now you can go to town trying different fractions. I find that things get really interesting when you use higher numbers that are very close to each other. For example, n = 22, d = 21:

Or even 81 and 80:

Roses with fractions less than 1

Things become a whole different type of interesting when you get fractions that are less 1.0. For example, here are roses with n and d of 1,2 on the left, 1,3 in the middle, and 1,4 on the right.

A trick to find interesting patterns is to take a pair of numbers that would usually reduce down, like 17 / 51 will reduce to 1 / 3, giving us the middle figure above. But then shift one of the values a bit. Here’s 17 and 52:

A big difference for just a shift of 1.

Named Roses

Some of these rose curves have special names. I’ll share some of them.

Limaçon Trisectrix

This has a ratio of 1 / 3. We already saw this one above.

Dürer Folium

With a ratio of 1 / 2. Also seen previously.

Quadrifolium

Ratio is 2 / 1

Trifolium

Ratio of 3 / 1

Maurer Roses

If you thought we were almost done, wrong! There’s a whole other type of rose curve to explore – Maurer roses!

Maurer roses start with the basic rose function, but instead of just drawing the curve all the way around, it draws a series of line segments to points along the rose curve. Although it doesn’t have to be so, this is often done with 360 segments and the angles used are specified in degrees. We construct a rose, here using a ratio of 4 / 1, and then pick a degree value to step by. In this case, I chose 49. Then we loop t from 0 to 360 and multiply t by that degree value. So the degrees goes from 0, to 49, 98, 147, 196 and so on. We use that value in our rose (converting to radians of course) and use that at the next point. Here’s what it looks like in action for the first 30 iterations:

To put it a different way, in a normal rose curve, we are incrementing in very tiny increments, so we get a very smooth curve. Here, we are incrementing in gigantic jumps, so we get what looks like is going to be a chaotic mess. But, if we let it finish its full path through to 360 iterations, we get…

Aha! Not a chaotic mess after all! In fact, quite nice. Actually, above I’ve drawn the regular rose on top of the Maurer rose. Here is the Maurer all by itself:

I think the two combined look really nice.

So how do we do this?

Well, again, we start out with the basic rose function. But in this case, we’ll just stick to a single integer value. So just n rather than n and d. But we also want to specify how many degrees to jump on each iteration. To avoid confusion with the earlier d parameter, I’ll call this deg. So the signature is:

function maurer(x, y, a, n, deg) 

Again, we want to loop from 0 to 360 for our initial t value. And then we want to get that value that is t multiplied by deg. This is the degree value shown in the animation above. We’ll call it k but at this point we’re done with degrees, so we’ll convert it to radians by multiplying by PI and dividing by 180

function maurer(x, y, a, n, deg) {
  for (t = 0; t < 360; t++) {
    k = t * deg * PI / 180
    r = a * cos(n * k)
    lineTo(x + cos(k) * r, y + sin(k) * r)
  }
}

We’ll then just execute the rose algorithm, but using k instead of t.

Now we can set something up like the following.

width = 800
height = 800
canvas(800, 800)

maurer(width / 2, height / 2, width * 0.45, 5, 37)
stroke()

// drawing the regular rose is optional
rose(width / 2, height / 2, width * 0.45, 5, 1)
stroke()

And get this:

Play around with different values for n and deg. You’ll find that n works the same way it did for regular roses. But minor variations in deg can create radically different images. For example here is n = 7 and deg = 23:

But moving deg up to 24 gives you this:

Not nearly as nice.

Generally, you’ll find that even numbers for deg will have a lower chance of being interesting than odd numbers (with exceptions).

And anything that divides evenly into 360 is not going to be great. For example, here’s 4, 120:

I drew the full rose too, but the Maurer is just the triangle on the right hand side. Increase that to 121 though, and you get this beauty:

Also, lower prime numbers usually always work pretty well. I’ve noticed that the lower values of n let you get away with higher prime numbers for deg. But it’s something I haven’t tested very thoroughly. Something to play around with.

One more thing you might want to try is fractional Maurer roses. You don’t even have to alter the code at this point. You can just enter the fraction. Because we are always looping from 0 to 360, we don’t need to adjust for a different number of loops. Here’s one to start with. Make sure you put both fraction values into the rose function separately, if you are using that.

maurer(0, 0, width * 0.45, 5.0 / 4.0, 229)
stroke()
rose(0, 0, width * 0.45, 5, 4)
stroke

See what you can find among all the possible variations.