Визуализация последовательности Recaman
Я видел видео о последовательности Recaman по Numberphile. Если вы не знаете алгоритм, вы можете посмотреть на эту ссылку:https://www.youtube.com/watch?v=FGC5TdIiT9U или этот: https://blogs.mathworks.com/cleve/2018/07/09/the-oeis-and-the-recaman-sequence/
Я написал небольшую часть программного обеспечения с обработкой и p5.js для визуализации последовательности. Мой алгоритм делает шаги, в которых определяется следующий прыжок, а затем я пытаюсь нарисовать полукруг от последней точки к новой. Моя проблема в том, что полукруг исчезает после его рисования, так что всегда виден только один полукруг. Я хочу, чтобы ни один из нарисованных полукругов не исчез.
вот ссылка на CodePen, где вы можете увидеть мой код и вывод:https://codepen.io/stefan_coffee/pen/QBBKgp
let S = [];
let count = 0;
let active_num = 0;
function setup() {
}
function draw() {
createCanvas(600, 400);
background(50, 50, 50);
for(i = 0; i < 20; i++) {
step();
drawStep();
}
}
function drawStep() {
var x = (S[S.indexOf(active_num)-1] + active_num ) /2;
var y = height / 2;
var w = active_num - S[S.indexOf(active_num)-1];
var h = w;
if (count % 2 == 0) {
stroke(255);
noFill();
arc(x, y, w, h, PI, 0)
} else {
stroke(255);
noFill();
arc(x, y, w, h, 0, 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>
3 ответов
пока draw
вызывается непрерывно в каждом кадре,setup
вызывается только один раз при запуске.
создайте холст только один раз и очистите заднюю часть только один раз при запуске. Это приведет к тому, что полукруги непрерывно добавляются на холст, без очистки полукругов от предыдущих кадров:
function setup() {
createCanvas(600, 400);
background(50, 50, 50);
}
function draw() {
for(i = 0; i < 20; i++) {
step();
drawStep();
}
}
далее есть проблема в вашем алгоритме. При поиске индекса active_num
, вы можете не найти active_num
в массиве S
, потому что он еще не добавлен, но он будет добавлен в следующую петлю. См.step
, где active_num
добавляется S
в начале функции, но увеличение позже.
функции drawStep
вы хотите прочитать последний элемент массива, который вы можете получить:
var prev_num = S[count-1];
соответствующие значения для следующего arc
являются последним элементом массива S
и active_num
.
чтобы решить проблему, вы можете изменить код, как это:
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);
}
Примечание, Вы можете полностью пропустить цикл в функции draw
, так что вы можете лучше "видеть" "анимация":
function draw() {
step();
drawStep();
}
и вы можете вручную задать количество кадров в секунду frameRate
:
function setup() {
createCanvas(600, 400);
background(50, 50, 50);
frameRate(20);
}
если вы масштабируете размер полукругов, то результат может выглядеть так:
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>
во-первых, как указывали другие, вы хотите избежать запроса p5.js, чтобы перерисовать холст с нуля каждую итерацию. Пока мы на нем, мы снизим частоту кадров до чего-то медленного (1 в секунду), чтобы браузер не сошел с ума. Итак, внесите следующие изменения в 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);
}
Далее, давайте избавимся от петли, где мы называем draw()
и drawStep()
неоднократно. П5.js уже будет звонить draw()
повторно, никакая потребность для нас повторно вызвать эти функции. Давайте также остановим анимацию после определенного количества шагов:
function draw() {
step();
drawStep();
if (count >= 20) {
noLoop();
}
}
Итак, мы внести эти изменения в ваш код, и что происходит? Мы видим несколько полукругов, но их немного.
чтобы исследовать, почему, взгляните на первую строку вашего drawStep
функция:
var x = (S[S.indexOf(active_num)-1] + active_num ) /2;
я думаю, вы намереваетесь S[S.indexOf(active_num)-1]
для предыдущего числа последовательности, но это выражение не будет работать, как ожидайте:
если
active_num
появляется где-то еще внутриS
(это происходит: 42-первое число, которое появляется дважды), затемS[S.indexOf(active_num)-1]
возвращает число, которое было до первого вхожденияactive_num
, не текущее событие.если
active_num
не происходит нигде в пределахS
,S.indexOf(active_num)
будет-1
, и поэтомуS[S.indexOf(active_num)-1]
оценивает вS[-2]
, которыйundefined
.
в первом случае вы получите произвольные неправильные полукруги, во втором вы ничего не получите.
предыдущий номер в последовательности фактически является последним в S
, и вы можете получить это, используя S[S.length-1]
. Итак, давайте заменим S.indexOf(active_num)
С S.length
в обоих местах это происходит в 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;
наконец, я бы предложил заменить строку:
if (S.indexOf(active_num - count) > 0) {
С
if (S.indexOf(active_num - count) >= 0) {
в цель этой строки-сказать: "если active_num - count
уже в S
затем ...но если active_num - count
равен нулю, S.indexOf(active_num - count)
будет ноль, потому что 0
находится в S
при индексе 0. Бывает, что else
блок правильно обрабатывает случай, когда active_num - count
равно нулю, поэтому у вас нет ошибки, но стоит знать, что S.indexOf
может возвратить 0
чтобы указать, что элемент находится в S
.
наконец, вы можете рассмотреть возможность масштабирования чертежа до совпадения изображения включено в ваш вопрос. Это должен быть просто случай умножения x
смещение и ширина и высота ваших полукруглых дуг по масштабному коэффициенту. Оставляю это на ваше усмотрение.
первый шаг к решению проблемы в этом:
function setup() {
createCanvas(600, 400);
noLoop();
}
по умолчанию draw
используется для рисования анимации, и она вызывается неоднократно, и это причина, по которой вы видите мерцающие фигуры. noLoop()
причины draw
вызывается только один раз.
даже с этим изменением я не мог заставить ваш код работать.