如何随机化这个2D像素传播函数来模仿火蔓延?

时间:2018-04-16 02:01:34

标签: javascript algorithm 2d

所以我有一个函数,它应该模仿代表位置的2D数组的火焰的增长和传播。 这是它的工作原理:

  • 在特定位置开火,例如(x,y)=(1,1)
  • 未访问的像素的值为0.
  • 1 - 9是对象(如墙壁等)的保留号码。
  • 着火时的像素的强度值为10到40。
  • 10到20从浅红色变为深红色以显示增加 强度。
  • 20至30仍为深红色以显示停滞的强度。 30到40 从红色到黑暗,显示垂死的火焰。
  • 函数findingNeighbors找到a的邻居像素 像素强度增加,强度增加。

到目前为止,该功能有效,但它非常线性,并且不像我想的那样工作。我以为它会以更放射的方式传播。我不确定我做错了什么。我希望它看起来像真火蔓延。我想要一个更径向的随机增长模式。

所以我的问题是我该如何对函数进行随机化以显示火势从一个点传播而不会影响性能?寻找邻居的函数应该随机一点吗?

下面包含可运行代码

function simulateFire() {
  for (let y = 0; y < mainGrid.length; y++) {
    for (let x = 0; x < mainGrid[y].length; x++) {
      let intensity = mainGrid[y][x];
      if (intensity >= 10 && intensity < 40) {
        findingNeighbors(y, x);
        $(`#cell_${y}_${x}`).empty().addClass(`i-${intensity}`).html(intensity);
      }

    }
  }
}

function findingNeighbors(i, j) {
  let rowLimit = mainGrid.length - 1;
  let columnLimit = mainGrid[0].length - 1;

  for (let x = Math.max(0, i - 1); x <= Math.min(i + 1, rowLimit); x++) {
    for (let y = Math.max(0, j - 1); y <= Math.min(j + 1, columnLimit); y++) {
      if (x !== i || y !== j) {
        increaseIntensity(y, x);
      }
    }
  }
}

function increaseIntensity (y,x) {  
    if (mainGrid[y][x] === 0 ) {
        mainGrid[y][x] = 10;
    } else if (mainGrid[y][x] > 9 && mainGrid[y][x] < 40) {
        mainGrid[y][x]++;
    }
}

&#13;
&#13;
let mainGrid = Array(200).fill().map(() => Array(200).fill(0)); // 200 x 200

$(document).ready(() => {
  let fireInterval;
  startFire(1, 1); // start fire at location (1,1)

  addTable(mainGrid, 'table');
  $('#start-btn').on('click', () => {
    fireInterval = setInterval(() => {
      simulateFire();
    }, 500);
  });
  $('#stop-btn').on('click', () => {
    clearInterval(fireInterval);
  })
});


function increaseIntensity(y, x) {
  if (mainGrid[y][x] === 0) {
    mainGrid[y][x] = 10;
  } else if (mainGrid[y][x] > 9 && mainGrid[y][x] < 40) {
    mainGrid[y][x]++;
  }
}


function addTable(dataArray, id) {
  $(`#${id}`)
    .empty()
    .append(`<table id="scene-arr-table" style="border-collapse:collapse;"></table>`);
  dataArray.forEach((el, r) => {
    let row = $(`<tr></tr>`);
    $(`#scene-arr-table`).append(row);
    el.forEach((el, c) => {
      $(row).append(`<td id="cell_${r}_${c}">${el}</td>`)
    });

  });
}

function startFire(y, x) {
  mainGrid[y][x] = 10; // intensity starts at 10
}

function simulateFire() {
  for (let y = 0; y < mainGrid.length; y++) {
    for (let x = 0; x < mainGrid[y].length; x++) {
      let intensity = mainGrid[y][x];
      if (intensity >= 10 && intensity < 40) {
        findingNeighbors(y, x);
        $(`#cell_${y}_${x}`).empty().addClass(`i-${intensity}`).html(intensity);
      }

    }
  }
}

function findingNeighbors(i, j) {
  let rowLimit = mainGrid.length - 1;
  let columnLimit = mainGrid[0].length - 1;

  for (let x = Math.max(0, i - 1); x <= Math.min(i + 1, rowLimit); x++) {
    for (let y = Math.max(0, j - 1); y <= Math.min(j + 1, columnLimit); y++) {
      if (x !== i || y !== j) {
        increaseIntensity(y, x);
      }
    }
  }
}
&#13;
.i-0 {
  background: transparent;
}

.i-10 {
  background: red;
}

.i-11 {
  background: rgba(255, 0, 0, 0.9);
}

.i-12 {
  background: rgba(255, 0, 0, 0.8);
}

.i-13 {
  background: rgba(255, 0, 0, 0.7);
}

.i-14 {
  background: rgba(255, 0, 0, 0.6);
}

.i-15 {
  background: rgba(255, 0, 0, 0.5);
}

.i-16 {
  background: rgba(255, 0, 0, 0.4);
}

.i-17 {
  background: rgba(255, 0, 0, 0.3);
}

.i-18 {
  background: rgba(255, 0, 0, 0.2);
}

.i-19 {
  background: rgba(255, 0, 0, 0.1);
}

.i-20 {
  background: red;
}

.i-21 {
  background: red;
}

.i-22 {
  background: red;
}

.i-23 {
  background: red;
}

.i-24 {
  background: red;
}

.i-25 {
  background: red;
}

.i-26 {
  background: red;
}

.i-27 {
  background: red;
}

.i-28 {
  background: red;
}

.i-29 {
  background: red;
}

.i-30 {
  background: red;
}

.i-31 {
  background: #e60404;
}

.i-32 {
  background: #cd0808;
}

.i-33 {
  background: #b40c0c;
}

.i-34 {
  background: #9b1010;
}

.i-35 {
  background: #821414;
}

.i-36 {
  background: #691818;
}

.i-37 {
  background: #501c1c;
}

.i-38 {
  background: #372020;
}

.i-39 {
  background: #1e2424;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Test</title>
</head>

<body>
  <button id="start-btn" type="button">START SIM</button>
  <button id="stop-btn" type="button">STOP SIM</button>
  <div id="table" style="font-size:2px;">

  </div>
</body>

</html>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:1)

使其更不规则的一种方法是为其添加一些随机性。将increaseIntensity(x,y)替换为随机参数,可能使用非线性滤波器和反距离。

increaseIntensity(x, y, sqrt(rand(0.,1.)) / ((x-i) * (x-i) + (y-j) * (y-j))

另一种方法是使用内核过滤器。你现在所拥有的类似于一个3x3滤波器,所有值都是1.我会尝试使用5x5滤波器,外部像素值减少一些。这应该给它一个更径向的外观。您需要增加过滤器尺寸以提高防火墙的质量。通过这种方式,您还可以在某个方向上构建具有更高值的滤波器,从而产生风影响的幻觉。

在这两种情况下,请确保您不要在同一帧中重复使用新值,否则您的火力会在某个方向上消失。

为了提高性能,请检查GPU计算支持。这些类型的操作(内核)得到了很好的支持并且非常快。