簡體   English   中英

找到一個矩形和一個圓的交點

[英]Find the intersection points between a rectangle and a circle

有一個矩形和一個圓,我想要一個 function 它將返回它們碰撞點的坐標,如下所示

let myrect = { x: 100, y: 100, w: 100, h: 50 };
let mycircle = { x: 156, y: 156, r: 100 };

function detectCoords(rect, circle) {
    //do something
    return [{ x: 5, y: 2}, { x: 3, y: 7}] //example
}

在此處輸入圖像描述

這是一個檢查矩形坐標是否存在於圓內的簡單方法。 只需使用簡單循環增加值即可。

let rect = { x: 40, y: 100, w: 100, h: 50 }
let mycircle = { x: 156, y: 156, r: 100 }

function isInside(circle_x, circle_y, rad, x, y) {
  //formula x^2+y^2=r^2
  if (
    (x - circle_x) * (x - circle_x) + (y - circle_y) * (y - circle_y) <=
    rad * rad
  )
    return true
  else return false
}
function getIntersectionPoint(circle, rect) {
  let coor = []
  let notFullyInside = false
  //for top
  for (let index = rect.x; index < rect.x + rect.w; index++) {
    if (isInside(circle.x, circle.y, circle.r, index, rect.y)) {
      if (notFullyInside) {
        coor.push({ x: index, y: rect.y })
        break
      }
    } else {
      notFullyInside = true
    }
  }
  notFullyInside = false
  //for left
  for (let index = rect.y; index < rect.y + rect.h; index++) {
    if (isInside(circle.x, circle.y, circle.r, rect.x, index)) {
      if (notFullyInside) {
        coor.push({ x: rect.x, y: index })
        break
      }
    } else {
      notFullyInside = true
    }
  }
  notFullyInside = false
  //for right
  for (let index = rect.y; index < rect.y + rect.h; index++) {
    if (isInside(circle.x, circle.y, circle.r, rect.x + rect.w, index)) {
      if (notFullyInside) {
        coor.push({ x: rect.x + rect.w, y: index })
        break
      }
    } else {
      notFullyInside = true
    }
  }
  notFullyInside = false
  //for bottom
  for (let index = rect.x; index < rect.x + rect.w; index++) {
    if (isInside(circle.x, circle.y, circle.r, index, rect.y + rect.h)) {
      if (notFullyInside) {
        coor.push({ x: index, y: rect.y + rect.h })
        break
      }
    } else {
      notFullyInside = true
    }
  }
  return coor
}

console.log(getIntersectionPoint(mycircle, rect))

基於Wyck 的評論 唯一需要的代碼是intersections function (這是您的detectCoords函數)。 如果您有任何疑問,請發表評論:-)

 function intersections (rect, circle) { var y_top = rect.y - rect.h / 2; var x_right = rect.x + rect.w / 2; var y_bottom = rect.y + rect.h / 2; var x_left = rect.x - rect.w / 2; return [ // absolute coordinates // of all the `rect` edges /* 0 even */ y_top, /* 1 odd */ x_right, /* 2 even */ y_bottom, /* 3 odd */ x_left ].map(function (x_or_y, i) { // relative coordinate // of one `rect` edge return x_or_y - ( // `i & 1` is 0 or 1 circle["yx"[i & 1]] ); }).map(function (x_or_y, i) { // edge out of circle if (Math.abs(x_or_y) > circle.r) { return []; } // edge and circle intersect else { let y_or_x, x1, y1, x2, y2; y_or_x = Math.sqrt( circle.r ** 2 - x_or_y ** 2 ); i = i & 1; // 0 or 1 x1 = [y_or_x, x_or_y][i]; y1 = [x_or_y, y_or_x][i]; x2 = x1 * (i? +1: -1); y2 = y1 * (i? -1: +1); // two elligible points // with absolute coordinates return [{ x: circle.x + x1, y: circle.y + y1 }, { x: circle.x + x2, y: circle.y + y2 }]; } }).reduce(function (acc, xys, i) { var k, min, max; i = i & 1; // 0 or 1 k = "xy"[i]; min = [x_left, y_top][i]; max = [x_right, y_bottom][i]; return acc.concat(xys.filter(function (xy) { // `xy` is on the edge? yes: no return xy[k] >= min && xy[k] <= max; })); }, []); } // optional code onload = function () { var canvasEl = getEl("canvas"); var ctx = canvasEl.getContext("2d"); canvasEl.width = 400; canvasEl.height = 300; draw( ctx, readRect(), readCircle() ); onSubmit("form", function (ev) { ev.preventDefault(); ctx.clearRect( 0, 0, canvasEl.width, canvasEl.height ); draw( ctx, readRect(), readCircle() ); }); } function readPair (el) { return el.value.split(" ").map( (x) => parseInt(x, 10) ); } function readRect () { var x, y, w, h; var rectXyEl = getEl("rect-xy"); var rectWhEl = getEl("rect-wh"); [x, y] = readPair(rectXyEl); [w, h] = readPair(rectWhEl); return { x: x, y: y, w: w, h: h }; } function readCircle () { var x, y, r; var circleXyEl = getEl("circle-xy"); var circleREl = getEl("circle-r"); [x, y] = readPair(circleXyEl); r = parseInt(circleREl.value, 10); return { x: x, y: y, r: r }; } function draw (ctx, rect, circle) { drawRect(ctx, rect); drawCircle(ctx, circle); drawIntersections(ctx, rect, circle); } function drawRect (ctx, rect) { ctx.beginPath(); ctx.rect( rect.x - rect.w / 2, rect.y - rect.h / 2, rect.w, rect.h ); ctx.stroke(); } function drawCircle (ctx, circle) { ctx.beginPath(); ctx.arc( circle.x, circle.y, circle.r, 0, 2 * Math.PI ); ctx.stroke(); } function drawIntersections (ctx, rect, circle) { for (let xy of intersections(rect, circle)) { ctx.beginPath(); ctx.arc(xy.x, xy.y, 3, 0, 2 * Math.PI, true); ctx.stroke(); } } function getEl (id) { return document.getElementById(id); } function onSubmit (id, f) { getEl(id).addEventListener("submit", f); }
 body { margin: .5em; background: #ddd; } input[type=text] { width: 60px; } input[type=submit] { margin-top: .5em; }.column { float: left; }.column:first-child { background: white; margin-right: .5em; padding: .5em; width: 90px; }
 <div class="column"> <form id="form"> rect xy <input type="text" id="rect-xy" value="100 100" > rect wh <input type="text" id="rect-wh" value="130 130" > circle xy <input type="text" id="circle-xy" value="100 100" > circle r <input type="text" id="circle-r" value="75" > <input type="submit"> </form> </div> <div class="column"> <canvas id="canvas" style="background:white"></canvas> </div>

表達式n & 1在基數 2 中選擇n的“最右邊”位。

> | 0 & 1 // 0b00
< | 0
> | 1 & 1 // 0b01
< | 1
> | 2 & 1 // 0b10
< | 0
> | 3 & 1 // 0b11
< | 1

您可以使用n & 1檢查數字是否為奇數。

> | 4 & 1 ? "odd" : "even"
< | "even"

有效實施指南:

WLOG 圓的中心是原點(如果不是你可以平移所有點)。 然后假設在縱坐標 Y 和橫坐標 X0 < X1 的水平邊。

如果 Y < -R 或 Y > R,則沒有交集。

否則,檢查 X0² > R² - Y² 和 X1² > R² - Y²。 如果兩者都為假,則該段完全在內部。 如果一假一真,則有一個交集。 如果兩個為真,則有兩個交集。

交叉點的縱坐標為 Y,因此橫坐標為 ±√(R² - Y²)。 根據上述內部/外部條件取負號和/或正號。 (真/假是-,假/真是+,真/真是兩者。)

重復四個側面。 (您可以將水平邊的條件重用於垂直邊的條件。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM