如何在 javascript 中获取正方形边缘附近的随机点

How to get random point near edges of a square in javascript

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

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>

澄清一下,此 'Not to scale' 图表中的黑色区域是我想让点生成的地方。该黑色区域的宽度/高度是代码段中的边框 属性。

为什么我的功能不起作用?

如果您将在边缘附近获取随机点的问题视为在四个边缘矩形之一中获取随机点的问题,那么这个问题就更容易分解了:

  1. 获取边缘矩形。
  2. 随机选择一个边缘矩形。
  3. 在边缘矩形中生成一个随机点。

要生成边缘矩形,我们需要一个最大距离(点可以离边缘多远?):

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

并将所有内容放在一起:

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();
}
<canvas width="500" height="500"/>

随机均匀分布。

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

理想的随机位置应该平均分布在相关区域上,如果盒子的高度小于宽度,那么双方获得分数的机会就会减少。

下面的示例提供了更快更好的分发。我也添加了给定的答案解决方案,以便您进行比较。

获取随机pos的函数。参数x,y top left inside edge of rectangle, w,h inside width and height of the rectangle minDist, maxDist the min 和 max dist 随机点可以从框的内边缘开始。您还可以使用负值让点位于矩形之外。请注意,距离始终是从框的内边缘开始的。当 return(可以很容易地删除并且仍然有效)

时,这些值也会下降
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 与周长有关

要比较的示例

该示例有两个 canvas。绘制了许多随机点。单击顶部 canvas 以添加更多点。注意底部 canvas 边和角是如何由于随机点的偏差而变暗的。

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  */

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);
<canvas id="canvas" width="500" height="200"></canvas>
<canvas id="canvas1" width="500" height="200"></canvas>

对于像我这样的任何人来说,寻找一个简短的解决方案,这个 post 是最接近的我发现它不是在谈论三角学..虽然我想出的可能不是直接的解决方案OP问题,也许有人会觉得这个有用..

方法相当简单。

Math.random() 一个介于 0 和 800 之间的数字。利用模数将剩下的除以 200 以获得随机边和轴点。一直推随机侧,将随机值分配给另一个轴,是的,就是这样..这是一个例子:

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
                                | |       | |
                                | |       | |

我所需要的只是抵消一些字母..我发现的大部分内容似乎都太过分了..这实际上工作得很好,希望它有所帮助。