Как да определите кога правоъгълник и завъртян правоъгълник се пресичат с помощта на JavaScript?

За моето приложение имам правоъгълник, начертан в SVG, който използвам като hitbox, и се стремя да направя JavaScript функция, която връща булево значение true, когато hitbox докосне друг правоъгълник, който е бил завъртян около центъра си под някакъв ъгъл използвайки 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, той връща размерите на правоъгълника в неговата незавъртяна форма. Въпросът ми е как да получа правилните размери за моя завъртян правоъгълник, така че да мога да открия, когато се сблъска с моя правоъгълник с hitbox? Има ли друг метод за мен да открия, когато моят хитбокс докосне моя завъртян правоъгълник? Всяка помощ ще бъде много ценена. въведете описание на изображението тук


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 изглежда работи перфектно в правоъгълника, който рисувам без ротация. Въпреки това, след завъртане, изглежда не задейства true, когато е дадена точка вътре в него. Опитах да използвам и lineTo(), но той работи само върху самата действителна тънка линия и когато увеличих lineWidth, той все още работи само върху оригиналната линия, не и върху новата област на щриха. Има ли начин да се преодолее това и в двата случая?   -  person Jack Buckley    schedule 22.03.2018
comment
Ще се забъркам малко с това, но мисля, че отговорът ви ще дойде от действително проследяване на правоъгълника и използване на трансформационна матрица за ротациите, вместо просто използване на вградените rect и rotate методи   -  person jmcgriz    schedule 22.03.2018


Отговори (2)


Има много грешки в спецификацията на SVG, но ето работещ пример за това, което поискахте. Бъдете предупредени, това е централен!!

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
Много хитро, но малко мамиш хаха. Въпреки че това потенциално може да спести много процесорна мощност, ако използвате два линейни сегмента като граници на правоъгълника и можете да използвате кръгъл hitbox за вашите движещи се елементи. - 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