Визуализация последовательности 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>

Я хочу, чтобы мой выход, чтобы выглядеть так: Visualization using ellipses

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);
}

preview

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 вызывается только один раз.

даже с этим изменением я не мог заставить ваш код работать.