1
votes

For my application, I have a SVG-drawn rectangle that I am using as a hitbox and I'm looking to make a JavaScript function that returns a boolean of true whenever the hitbox touches another rectangle that has been rotated about its centre by some angle using CSS transformations. I have discovered this code from 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);
}

This works great for regular rectangles, but I am unsure how to build upon this to work with my rotated rectangle. When I inspect the rotated rectangle using developer tools in my web browser, I can see that it highlights the rectangle correctly. That being said, when I try to extract the bounding client rectangle using JavaScript, it returns the dimensions of the rectangle in its unrotated form. My question is how do I receive the correct dimensions for my rotated rectangle so I can detect when it collides with my hitbox rectangle? Is there another method for me to detect when my hitbox touches my rotated rectangle? Any help would be much appreciated. enter image description here

2
If you're open to using canvas instead, you're probably going to have an easier time. isPointInPath in particular may save you a lot of headaches: w3schools.com/tags/canvas_ispointinpath.aspjmcgriz
@jmcgriz Thanks for your response! I'm playing around with the canvas on W3Schools, and I can see that isPointInPath seems to work perfectly in the rectangle that I draw without rotation. However, after rotation, it does not seem to trigger true when a point inside of it is given. I've tried using lineTo() as well, but it only works on the actual thin line itself, and when I increased the lineWidth, it still only works on the original line, not on the new stroke area. Is there a way to overcome this in either case?Jack Buckley
I'm going to mess with it a little, but I think your answer's going to come from actually tracing the rectangle and using a transformation matrix for the rotations instead of just using the built in rect and rotate methodsjmcgriz

2 Answers

1
votes

Alright so this is going to take a lot of optimization before it's practical for your use case, but it does serve as a proof of concept that isPointInPath will work with a rotation, as long as you draw the rectangle by points (you can also use fillRect and rotate to draw it, and just trace the outline with lineTo but no stroke to create your bounding box to check against).

To test out the isPointInPath, edit the values of checkX and 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>
1
votes

There is a lot of ick in the SVG spec, but here is a working example of what you requested. Be warned, this is gotcha central!!

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


}