I think I just made up a thing. Usually when I think that, it just means fewer than a few hundred people have thought about it before me, so who knows.
Let’s start with cardioids. A cardioid is a heart-shaped curve. One way to create a cardiod is to roll a circle around another circle of the same size, tracing the path of a single point on the moving circle. Like so:
I discovered another neat way to create a cardioid while checking out the math art challenge.
#MathArtChallenge Day 7: Cardioids!
In this method, you divide a circle into an arbitrary number of points around the radius. Then, for each point n
, you draw a line from point n
to point n*2
. Point 1 to 2, point 2 to 4, point 3 to 6, etc.
Here’s some JavaScript/Canvas code showing this in action:
context.translate(400, 400);
const radius = 350;
const res = 100;
const slice = Math.PI * 2 / res;
const mult = 2;
context.beginPath();
for(let i = 1; i < res; i++) {
let a1 = slice * i;
let a2 = slice * i * mult;
context.moveTo(Math.cos(a1) * radius, Math.sin(a1) * radius);
context.lineTo(Math.cos(a2) * radius, Math.sin(a2) * radius);
}
context.stroke();
And here’s what that gives you:
You’ll notice in the code that I’ve divided the circle into 100 points, which is is rather low-res. If I up that to 360, we get something nicer:
So I’m calculating the points by getting two angles, a1
and a2
, calculating those with slice * i
and slice * i * mult
as described above. slice
being a full circle divided by res
and mult
equals 2 for now.
let a1 = slice * i;
let a2 = slice * i * mult;
What if I change that to mult = 3
instead?
Or, mult = 4
?
You see that for each multiplier, m
, you get m-1
nodes in the cardioid. Let’s just go crazy and see what happens when we set res
to 1440 points and mult
to 25:
Here’s the code for those of you following along at home:
context.translate(400, 400);
const radius = 350;
const res = 1440;
const slice = Math.PI * 2 / res;
const mult = 25;
context.lineWidth = 0.25;
context.beginPath();
for(let i = 1; i < res; i++) {
let a1 = slice * i;
let a2 = slice * i * mult;
context.moveTo(Math.cos(a1) * radius, Math.sin(a1) * radius);
context.lineTo(Math.cos(a2) * radius, Math.sin(a2) * radius);
}
context.stroke();
All very interesting, but I wanted to start changing things up even more. I decided that rather than using a simple circle, what if I varied the radius of the circle with a sine wave? Here’s the code I came up with:
context.translate(400, 400);
const radius = 300;
const res = 1440;
const slice = Math.PI * 2 / res;
const mult = 5;
const waves = 6;
context.lineWidth = 0.25;
context.beginPath();
for(let i = 1; i < res; i++) {
let a1 = slice * i;
let a2 = slice * i * mult;
let r1 = radius + Math.sin(a1 * waves) * 100;
let r2 = radius + Math.sin(a2 * waves) * 100;
context.moveTo(Math.cos(a1) * r1, Math.sin(a1) * r1);
context.lineTo(Math.cos(a2) * r2, Math.sin(a2) * r2);
}
context.stroke();
First I created a waves
constant that controls how many sine waves will go around the circle. Then an r1
variable that is based on radius, multiplied by the sine of a1 * waves
times 100. And an r2
variable based on a2 * waves
. So for each point, it’s radius will get larger and smaller as they progress around the circle. The result (setting mult
back to 5):
You can get all kinds of interesting shapes by varying how many nodes and how many waves and the size of the waves and the resolution.
Of course, I had to have a go at animating these. The first idea was to vary the height of that radial wave. Here, it’s going back and forth from -80 to +80:
I’m not going to give the source for the animation examples, because it was written in another system entirely, but if you’ve followed along so far, you’ll be able to figure it out.
Next, I thought about varying the phase of that radial wave, so that the wave itself seemed to be animating around in a circle. This produced some really striking animations. I’ll close the article by posting animations for 2, 3, 4, 5 and 6 wave animated sinosoidal cardiodids. Enjoy!
Hey Keith. It’s been for-ev-er. I think we ment once at some conference or another. I just re-purchased your book “Foundation Actionscript 3.0 Animation: Making Things Move” for, I think, the third time. (BTW, last copy available at Amazon.) I’ve moved on to Dart/Flutter these days, but it’s easy enough to translate you code and ideas from AS3.
BTW, I plan to use your springs and balls code to recreate an Android app I made ages ago called “Ballance”. I’ll let you know when it’s available.
It’s really good to see you’re still turning out amazing visuals. Thanks. You’re in inspiration.
I have an unopened case of that book in my basement. I hope it’s not moldy.
I almost forgot. Speaking of “carotids” I coincidentally watched this related youtube video last night:
Times Tables, Mandelbrot and the Heart of Mathematics
https://www.youtube.com/watch?v=qhbuKbxJsk8
found you YouTube channel, had to do aa bit of googling to make sure you weren’t dead, your last video is 3 years old, why is that?
Because I haven’t made any videos in 3 years? 🙂 It’s a ton of work and I’m busy with other things.