Visualization of the Recaman Sequence
Firstly, as others have pointed out, you want to avoid asking p5.js to redraw the canvas from scratch every iteration. While we're at it, we'll drop the frame rate to something slow (1 per second) to prevent the browser going crazy. So, make the following changes to setup()
:
function setup() {
// Move these two lines from draw():
createCanvas(600, 400);
background(50, 50, 50);
// Add this line to slow things down
frameRate(1);
}
Next, let's get rid of the loop where we call draw()
and drawStep()
repeatedly. p5.js will already be calling draw()
repeatedly, there's no need for us to repeatedly call these functions. Let's also make the animation stop after a certain number of steps:
function draw() {
step();
drawStep();
if (count >= 20) {
noLoop();
}
}
So, we make these changes to your code, and what happens? We see some semicircles, but not many.
To investigate why, take a look at the first line of your drawStep
function:
var x = (S[S.indexOf(active_num)-1] + active_num ) /2;
I think you are intending S[S.indexOf(active_num)-1]
to be the previous number in the sequence, but this expression will not work as you expect:
If
active_num
appears somewhere else withinS
(this does happen: 42 is the first number to appear twice), thenS[S.indexOf(active_num)-1]
will return the number that was before the first occurrence ofactive_num
, not the current occurrence.If
active_num
doesn't occur anywhere else withinS
,S.indexOf(active_num)
will be-1
, and henceS[S.indexOf(active_num)-1]
evaluates toS[-2]
, which isundefined
.
In the first case you will get arbitrary incorrect semicircles, in the second you will get nothing.
The previous number in the sequence is in fact the last one in S
, and you can get that using S[S.length-1]
. So, let's replace S.indexOf(active_num)
with S.length
in both places it occurs in drawStep()
:
var x = (S[S.length-1] + active_num ) /2;
var y = height / 2;
var w = active_num - S[S.length-1];
var h = w;
Finally, I'd suggest replacing the line:
if (S.indexOf(active_num - count) > 0) {
with
if (S.indexOf(active_num - count) >= 0) {
The intention of this line is to say 'if active_num - count
is already in S
then ...', but if active_num - count
is zero, S.indexOf(active_num - count)
will be zero because 0
is in S
at index 0. It happens that the else
block correctly handles the case where active_num - count
is zero, so you don't have a bug, but it's worth being aware that S.indexOf
can return 0
to indicate an element is in S
.
Finally, you may want to consider scaling the drawing up to match the image you included in your question. That should just be a case of multiplying the x
offset and the width and height of your semi-circular arcs by a scale factor. I'll leave that up to you.
The first step to solving the problem in this:
function setup() {
createCanvas(600, 400);
noLoop();
}
By default draw
is used to draw an animation, and it is called repeatedly, and is the reason you're seeing flickering shapes. noLoop()
causes draw
to be called only once.
Even with this change, I couldn't get your code working.
While draw
is called continuously in every frame, setup
is only called once at startup.
Create the canvas only once and clear the back ground only once at startup. This will cause that semicircles continuously added to the canvas, with out clearing the semicircles from the previous frames:
function setup() {
createCanvas(600, 400);
background(50, 50, 50);
}
function draw() {
for(i = 0; i < 20; i++) {
step();
drawStep();
}
}
Further there is an issue in your algorithm. When you search for the index of active_num
, you may not find active_num
in the array S
, because it is not added yet, but it will be added in the next loop. See step
, where active_num
is added to S
at the begin of the function, but increment later.
In the function drawStep
you want to read the last element of the array, which you can get by:
var prev_num = S[count-1];
The relevant values for the next arc
are the last element of the array S
and active_num
.
To solve the issue, you may change the code like this:
function drawStep() {
var prev_num = S[count-1];
var x = (prev_num + active_num) /2;
var y = height / 2;
var w = abs(active_num - prev_num);
var h = w;
stroke(255);
noFill();
arc(x, y, w, h, (count % 2 == 0) ? 0 : PI, (count % 2 == 0) ? PI : TWO_PI);
}
Note, you can completely skip the loop in the function draw
, so you can better "see" the "animation":
function draw() {
step();
drawStep();
}
and you can manually set the frames per second by frameRate
:
function setup() {
createCanvas(600, 400);
background(50, 50, 50);
frameRate(20);
}
If you would scale the size of the semicircles, then the result may look like this:
function drawStep() {
var scale = 10;
var prev_num = scale * S[count-1];
var num = scale * active_num;
var x = (prev_num + num) /2;
var y = height / 2;
var w = abs(num - prev_num);
var h = w;
stroke(255);
noFill();
arc(x, y, w, h, (count % 2 == 0) ? 0 : PI, (count % 2 == 0) ? PI : TWO_PI);
}
let S = [];
let count = 0;
let active_num = 0;
function setup() {
createCanvas(600, 400);
background(50, 50, 50);
frameRate(20);
}
function draw() {
step();
drawStep();
}
function drawStep() {
var scale = 10;
var prev_num = scale * S[count-1];
var num = scale * active_num;
var x = (prev_num + num) /2;
var y = height / 2;
var w = abs(num - prev_num);
var h = w;
stroke(255);
noFill();
arc(x, y, w, h, (count % 2 == 0) ? 0 : PI, (count % 2 == 0) ? PI : TWO_PI);
}
function step() {
count++;
S.push(active_num);
console.log('active_num: ' + active_num +' count: ' + count + ' ' + S);
if (S.indexOf(active_num - count) > 0) {
active_num += count;
} else {
if (active_num - count <= 0) {
active_num += count;
} else {
active_num -= count;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>