如何在javascript

时间:2018-03-30 19:02:46

标签: javascript canvas html5-canvas point

我想创建一个函数,在一个点的矩形边缘附近给我一个随机点。这是我到目前为止所提出的,但我完全不知道它为什么不起作用。

function Point(x, y) {
  this.x = x;
  this.y = y;
}

function randomNumber(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

function getRandomPointNearEdges(rectPos, width, height, border) {
  var point = new Point(rectPos.x, rectPos.y);
  if (randomNumber(0, 1) == 0) {
    point.x = randomNumber(rectPos.x, rectPos.x + border);
    if (randomNumber(0, 1) == 0) {
      point.y = randomNumber(rectPos.y, rectPos.y + border);
    }
    else {
      point.y = randomNumber(rectPos.y + height, (rectPos.y + height) + border);
    }
  }
  else {
    point.y = randomNumber(rectPos.y, rectPos.y + border);
    if (randomNumber(0, 1) == 0) {
      point.y = randomNumber(rectPos.x, rectPos.x + border);
    }
    else {
      point.y = randomNumber(rectPos.x + height, (rectPos.x + width) + border);
    }
  }
  return point;
};

window.onload = function() {
  canvas = document.getElementById("canvas");
  canvas.width = 700;
  canvas.height = 700;
  var ctx = canvas.getContext("2d");
  ctx.strokeRect(130, 130, 500, 500);
  
  for (var i = 0; i < 30; i++) {
    var point = getRandomPointNearEdges(new Point(130, 130), 500, 500, 100);
    ctx.fillRect(point.x, point.y, 2, 2);
  }
};
<canvas id="canvas"></canvas>

只是为了澄清,这个'不按比例'图表中的黑色区域是我想让点生成的地方。该黑色区域的宽度/高度是代码段中的border属性。

Diagram

为什么我的功能不起作用?谢谢你提前。

3 个答案:

答案 0 :(得分:1)

如果你想到在边缘附近获得随机点的问题是在四个边缘矩形之一中获得一个随机点,这个问题变得更容易分解:

  1. 获取边缘矩形。
  2. 选择一个随机边长矩形。
  3. 在边缘矩形中生成随机点。
  4. 要生成边缘矩形,我们需要一个最大距离(该点可以离边缘多远?):

    function getBorderRects(rect, distance) {
      const { x, y, width, height } = rect;
      return [
        {x: x, y: y, width: width, height: distance}, // top
        {x: x, y: y + height - distance, width: width, height: distance}, // bottom
        {x: x, y: y, width: distance, height: height}, // left
        {x: x + width - distance, y: y, width: distance, height: height}, // right
      ];
    }
    

    要从我们的边缘矩形数组中选择一个随机矩形,我们可以定义一个sample函数:

    function sample(array) {
      return array[Math.floor(Math.random() * array.length)];
    }
    

    然后在矩形中选择一个随机点,我们只需要一些Math.random

    function randomPointInRect({x, y, width, height}) {
      return {
        x: x + (Math.random() * width),
        y: y + (Math.random() * height),
      };
    }
    

    把所有东西放在一起:

    &#13;
    &#13;
    const canvas = document.querySelector('canvas');
    const context = canvas.getContext('2d');
    
    const rect = {
      x: 10, y: 20,
      width: 300, height: 200,
    };
    
    drawRect(rect);
    
    drawPoint(
      randomPointInRect(
        sample(
          getBorderRects(rect, 10)
        )
      )
    );
    
    
    function getBorderRects(rect, distance) {
      const { x, y, width, height } = rect;
      return [
        {x: x, y: y, width: width, height: distance}, // top
        {x: x, y: y + height - distance, width: width, height: distance}, // bottom
        {x: x, y: y, width: distance, height: height}, // left
        {x: x + width - distance, y: y, width: distance, height: height}, // right
      ];
    }
    
    function sample(array) {
      return array[Math.floor(Math.random() * array.length)];
    }
    
    function randomPointInRect({x, y, width, height}) {
      return {
        x: x + (Math.random() * width),
        y: y + (Math.random() * height),
      };
    }
    
    function drawRect({x, y, width, height}) {
      context.strokeRect(x, y, width, height);
    }
    
    function drawPoint({x, y}) {
      context.arc(x, y, 1, 0, Math.PI * 2);
      context.fill();
    }
    &#13;
    <canvas width="500" height="500"/>
    &#13;
    &#13;
    &#13;

答案 1 :(得分:1)

随机均匀分布。

只是要指出SimpleJ的答案在统计上存在缺陷,随机位置的分布偏向角落然后偏向较短边,即使它们覆盖的面积要小得多。

理想的随机位置应该平均分布在相关区域上,如果盒子的高度小于宽度,那么双方得到一个点的几率就会降低。

以下示例提供了更快,更好的分发。我已经添加了给定的答案解决方案,因此您可以进行比较。

获取随机pos的函数。参数xy位于矩形内边缘的左上角,wh内部宽度和高度为minDistmaxDist min和max dist随机点可以来自框的内边缘。您还可以使用具有矩形外部点的负值。请注意,距离始终来自框的内边缘。返回时,这些值也会被覆盖(可以很容易地删除并且仍然有效)

function randomPointNearRect(x, y, w, h, minDist, maxDist) {
  const dist = (Math.random() * (maxDist - minDist) + minDist) | 0;
  x += dist;
  y += dist;
  w -= dist  * 2
  h -= dist  * 2
  if (Math.random() <  w / (w + h)) { // top bottom
    x = Math.random() * w + x;
    y = Math.random() < 0.5 ? y : y + h -1;
  } else {
    y = Math.random() * h + y;
    x = Math.random() < 0.5 ? x: x + w -1;
  }
  return [x | 0, y | 0];
}

注意盒子里面有一点偏差。它可以通过一点微积分去除,偏差率为f'(x) = 8*x每像素8像素,反导数f(x)=4*(x**2) + c将直接与分布相关。其中x与边缘分开,c与周长相关

比较示例

这个例子有两个画布。绘制了许多随机点。单击顶部画布以添加更多点。请注意由于随机点的偏差,底部画布的边和角会变暗。

&#13;
&#13;
const ctx = canvas.getContext("2d");
canvas.onclick = ()=>{
  getRandomPointsForBox(200, box,4, 18);
  getRandomPoints(200);
}
const edgeClear = 30;
var box = {
  x: edgeClear,
  y: edgeClear,
  w: canvas.width - edgeClear * 2,
  h: canvas.height - edgeClear * 2,
  edge: 4,
}

function drawBox(box) {
  ctx.fillRect(box.x, box.y, box.w, box.h);
  ctx.clearRect(box.x + box.edge, box.y + box.edge, box.w - box.edge * 2, box.h - box.edge * 2);
}

function drawPixel(x, y) {
  ctx.fillRect(x, y, 1, 1);
}

function getRandomPointsForBox(count, box, min, max) {
  min += box.edge;
  max += box.edge;
  while (count--) {
    const [x, y] = randomPointNearRect(box.x, box.y, box.w, box.h, min, max);
    drawPixel(x, y);
  }
  
}

drawBox(box);
getRandomPointsForBox(200, box,4, 18);
ctx.font = "18px arial"
ctx.textAlign = "center"
ctx.textBaseline = "middle"
ctx.fillText("Click to add more random points.",canvas.width / 2, canvas.height / 2);



function randomPointNearRect(x, y, w, h, minDist, maxDist) {
  const dist = (Math.random() * (maxDist - minDist) + minDist) | 0;
  x += dist;
  y += dist;
  w -= dist  * 2
  h -= dist  * 2
  if (Math.random() <  w / (w + h)) { // top bottom
    x = Math.random() * w + x;
    y = Math.random() < 0.5 ? y : y + h -1;
  } else {
    y = Math.random() * h + y;
    x = Math.random() < 0.5 ? x: x + w -1;
  }
  return [x | 0, y | 0];
}









/* The following is from the answer provided by SimpleJ https://stackoverflow.com/a/49581326/3877726 */

const ctx1 = canvas1.getContext('2d');

const rect = {
  x: box.x, y: box.y,
  width: box.w, height: box.h,
};

drawRect(rect);

ctx1.font = "18px arial"
ctx1.textAlign = "center"
ctx1.textBaseline = "middle"
ctx1.fillText("SimpleJ's method.",canvas1.width / 2, canvas1.height / 2);
ctx1.fillText("Note density of sides and corners.",canvas1.width / 2, canvas1.height / 2 + 20);

function getRandomPoints(count) {
  while (count--) {
    drawPoint(randomPointInRect(sample(rects)));
  }
}


var rects = getBorderRects(rect, 10);




function getBorderRects(rect, distance) {
  const { x, y, width, height } = rect;
  return [
    {x: x, y: y, width: width, height: distance}, // top
    {x: x, y: y + height - distance, width: width, height: distance}, // bottom
    {x: x, y: y, width: distance, height: height}, // left
    {x: x + width - distance, y: y, width: distance, height: height}, // right
  ];
}

function sample(array) {
  return array[Math.floor(Math.random() * array.length)];
}

function randomPointInRect({x, y, width, height}) {
  return {
    x: x + (Math.random() * width),
    y: y + (Math.random() * height),
  };
}
function drawRect({x, y, width, height}) {
  ctx1.strokeRect(x, y, width, height);
}
function drawPoint({x, y}) {
  ctx1.fillRect(x, y, 1,1);
}
  getRandomPoints(200);
&#13;
<canvas id="canvas" width="500" height="200"></canvas>
<canvas id="canvas1" width="500" height="200"></canvas>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

对于像我这样的人,寻找一个简短的解决方案,我最接近的帖子是我不谈论三角函数。觉得有用。

方法很简单。

Math.random()一个介于0到800之间的数字。利用模数并将剩余的数除以200得到一个随机的边和轴点。一直推动随机面,将随机值分配到另一个轴,是的,就是这样..这是一个ex:

let rndm = Math.floor(Math.random()*800-1);
let offset = rndm % 200;
let side = (rndm - offset) / 200; // 0:top 1:right 2:btm 3:left
let y = side % 2 > 0 ? offset+1 : 100 * side ;
let x = side % 2 < 1 ? offset+1 : 100 * (side - 1) ;

point.y = y - 100;
point.x = x - 100;

在我的情况下,我需要带正点的负值和正值。 而且,如果您想在边框内产生一个点,只需执行另一个跨越边框宽度的随机数即可。

只需记住调整角点即可。

offset += rndmBorder * 2;         // creates an inward line in the corners
point.x = x - 100 + rndmBorder;   // still keeping the origin point nice and center
                                _____________
                                |\_________/|  <-// inward line
                                | |       | |
                                | |       | |

我所需要的只是抵消一些字母..而我发现的大部分内容似乎都过分了..实际上效果很好,希望能有所帮助。