简体   繁体   中英

Find the intersection points between a rectangle and a circle

having a rectangle and a circle, I would like to have a function that would return me the coordinates of the points where they collide something like this

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
}

在此处输入图像描述

Here is a simple method that checks coordinate of rectangle exists within the circle or not. just by incrementing the values using simple loop.

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

Based on Wyck's comment . The only required code is the intersections function (which is your detectCoords function). If you have questions, drop a comment:-)

 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>

The expression n & 1 picks the "rightmost" bit of n in base 2.

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

You can use n & 1 to check whether a number is odd.

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

Guidelines for an efficient implementation:

WLOG the center of the circle is the origin (if not you can translate all points). Then assume an horizontal edge at ordinate Y and abscissas X0 < X1.

If Y < -R or Y > R, there is no intersection.

Else, check X0² > R² - Y² and X1² > R² - Y². If both false, the segment is wholly inside. If one false and one true, there is a single intersection. If two true, there are two intersections.

The intersections have ordinate Y, so abscissa ±√(R² - Y²). Take the negative and/or positive sign depending on the above inside/outside conditions. (true/false is -, false/true is +, true/true is both.)

Repeat for the four sides. (You can reuse the conditions on the horizontal sides for the vertical ones.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM