Как определить, когда прямоугольник и повернутый прямоугольник пересекаются, используя JavaScript?

Для моего приложения у меня есть прямоугольник, нарисованный SVG, который я использую в качестве хитбокса, и я хочу создать функцию JavaScript, которая возвращает логическое значение true всякий раз, когда хитбокс касается другого прямоугольника, который был повернут вокруг своего центра на некоторый угол. с помощью CSS-преобразований. Я обнаружил этот код на http://www.inkfood.com/collision-detection-with-svg/:

function intersectRect(r1, r2) {
var r1 = r1.getBoundingClientRect();    //BOUNDING BOX OF THE FIRST OBJECT
var r2 = r2.getBoundingClientRect();    //BOUNDING BOX OF THE SECOND OBJECT

//CHECK IF THE TWO BOUNDING BOXES OVERLAP
 return !(r2.left > r1.right || 
       r2.right < r1.left || 
       r2.top > r1.bottom ||
       r2.bottom < r1.top);
}

Это отлично работает для обычных прямоугольников, но я не уверен, как использовать это для работы с моим повернутым прямоугольником. Когда я проверяю повернутый прямоугольник с помощью инструментов разработчика в своем веб-браузере, я вижу, что он правильно выделяет прямоугольник. При этом, когда я пытаюсь извлечь ограничивающий прямоугольник клиента с помощью JavaScript, он возвращает размеры прямоугольника в его неповернутой форме. Мой вопрос: как мне получить правильные размеры моего повернутого прямоугольника, чтобы я мог определить, когда он сталкивается с моим прямоугольником хитбокса? Есть ли другой способ определить, когда мой хитбокс касается моего повернутого прямоугольника? Любая помощь приветствуется. введите здесь описание изображения


person Jack Buckley    schedule 22.03.2018    source источник
comment
Если вместо этого вы готовы использовать canvas, вам, вероятно, будет легче. isPointInPath, в частности, может избавить вас от головной боли: w3schools.com/tags/canvas_ispointinpath.asp   -  person jmcgriz    schedule 22.03.2018
comment
@jmcgriz Спасибо за ваш ответ! Я играю с холстом в W3Schools и вижу, что isPointInPath отлично работает в прямоугольнике, который я рисую без поворота. Однако после поворота он, похоже, не срабатывает, когда внутри него указана точка. Я также пробовал использовать lineTo(), но он работает только с самой тонкой линией, и когда я увеличил ширину линии, он по-прежнему работает только с исходной линией, а не с новой областью штриха. Есть ли способ преодолеть это в любом случае?   -  person Jack Buckley    schedule 22.03.2018
comment
Я собираюсь немного повозиться с этим, но я думаю, что ваш ответ будет исходить от фактического отслеживания прямоугольника и использования матрицы преобразования для поворотов вместо простого использования встроенных методов rect и rotate   -  person jmcgriz    schedule 22.03.2018


Ответы (2)


В спецификации SVG много ick, но вот рабочий пример того, что вы просили. Будьте осторожны, это центральное место!

https://codepen.io/josephscottstevens/pen/XEgbrP

window.onmousemove = function(e) {
  var c = document.getElementById("circle");
  var l = document.getElementById("line");
  var displayDiv = document.getElementById("isColiding");
  var circle = { radius : c.r, center : { x:c.cx, y:c.cy } };
  var line =   { p1 : { x:l.x1, y:l.y1 }, p2 : { x:l.x2, y:l.y2 } };
  displayDiv.textContent = circle.radius; //(distance == 0) ? "coliding" : "not coliding";
  moveSection("circle", e.clientX - 20, e.clientY - 20);
  var distance = circleDistFromLineSeg(circle, line);


}
person JosephStevens    schedule 22.03.2018
comment
Очень умно, но ты немного жульничаешь, ха-ха. Хотя это потенциально может сэкономить много вычислительной мощности, если вы используете два сегмента линии в качестве границ прямоугольника и можете использовать круговой хитбокс для ваших движущихся элементов. - person jmcgriz; 22.03.2018
comment
Это как раз то, что мне было нужно. Спасибо! - person Jack Buckley; 24.03.2018

Хорошо, так что это потребует много оптимизации, прежде чем это станет практичным для вашего варианта использования, но это действительно служит доказательством концепции, что isPointInPath будет работать с вращением, пока вы рисуете прямоугольник по точкам (вы также можете использовать fillRect и rotate, чтобы нарисовать его, и просто обведите контур с помощью lineTo, но без stroke, чтобы создать ограничивающую рамку для проверки).

Чтобы проверить isPointInPath, отредактируйте значения checkX и checkY.

var ctx = document.querySelector('canvas').getContext('2d')

function rotate(x, y, a, b, degrees){
	let angle = degrees * (Math.PI / 180),
  	sinA = Math.sin(angle),
  	cosA = Math.cos(angle)
    
  return [
  	cosA * (a - x) - sinA * (b - y) + x,
    sinA * (a - x) + cosA * (b - y) + y
  ]
}

function drawRect(x, y, w, h, rotation){
	var manipFn = rotation ? rotate : function(a,b){ return [a,b]; }
  
  var coords = {
  	bottomLeft: manipFn(x,y,x,y,rotation),
    bottomRight: manipFn(x,y,x+w,y,rotation),
    topRight: manipFn(x,y,x+w,y+h,rotation),
    topLeft: manipFn(x,y,x,y+h,rotation)
  }
  
  var checkX = 106,
    checkY = 18
  
  ctx.moveTo(...coords.bottomLeft)
  ctx.strokeStyle = "#3a1"
  ctx.lineWidth = 1
  ctx.lineTo(...coords.bottomRight)
  ctx.lineTo(...coords.topRight)
  ctx.lineTo(...coords.topLeft)
  ctx.lineTo(...coords.bottomLeft)
  console.log(ctx.isPointInPath(checkX,checkY))
  ctx.stroke()
  ctx.closePath()
  
  ctx.fillStyle = "#000"
  ctx.fillRect(checkX, checkY, 2, 2)
}


//drawRect(10, 10, 20, 30)


drawRect(100, 10, 20, 30, 45)
canvas {
  width: 500px;
  height: 500px;
  border: 1px solid black;
}
<canvas></canvas>

person jmcgriz    schedule 22.03.2018