如何测试一个点是否在一个旋转角度的矩形区域? [英] How to test if a point is in a rectangle area which rotates an angle?

查看:107
本文介绍了如何测试一个点是否在一个旋转角度的矩形区域?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试测试一个点是否位于围绕(x,y)旋转角度的矩形区域内,如下图所示。这是语言无关的问题,但我现在正在使用HTML5 canvas。

I am trying to test if a point is inside a rectangle area that rotates an angle around (x, y), like the image below. This is language agnostic problem but I am working with HTML5 canvas now.

假设我们需要测试的点是(x1,y1),矩形的宽度是100,高度是60.在正常的笛卡尔坐标系中,矩形ABCD左上角A是(canvas.width / 2,canvas.height / 2 -rect.height / 2)。我假设(canvas.width / 2,canvas.height / 2)位于AB行的中间,其中B是(canvas.width / 2,canvas.height / 2 + rect.height / 2)

Suppose the point we need to test is (x1, y1), the width of the rectangle is 100 and the height is 60. In normal cartesian coordinate system the rectangle ABCD top left point A is (canvas.width / 2, canvas.height / 2 -rect.height/2). I assume that (canvas.width / 2, canvas.height / 2) is at the middle of line AB where B is (canvas.width / 2, canvas.height / 2 + rect.height /2).

我读过一些资源在哪里并编写了一个测试项目,但它没有测试正确的区域。在我的测试项目中我想要这个效果:

I have read some resources here and wrote a test project, but it doesn't test the correct area. In my test project I want the this effect:

如果鼠标位于测试矩形区域范围内的点上,鼠标周围会显示一个点。如果它在矩形之外,则不会显示任何内容。

if the mouse is on a point that is within the range of the testing rectangle area a dot will be displayed around the mouse. If it is outside the rectangle nothing will be displayed.

然而,我的测试项目如下所示:(请注意,虽然我使用基于矢量的技术来测试旋转矩形区域中的点,但测试区域仍然是之前的矩形旋转)

However my test project looks like this: (Note that although I used the vector based technique to test the point in a rotated rectangle area, the test area remains the rectangle before rotation)

// Detecting a point is in a rotated rectangle area
// using vector based method
const canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext('2d');

class Rectangle {
	constructor(x, y, width, height) {
  	this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.searchPoint = { x: 0, y: 0};
    this.binding();
  }
  
  binding() {
  	let self = this;
    window.addEventListener('mousemove', e => {
      if (!e) return;
      let rect = canvas.getBoundingClientRect();
      let mx = e.clientX - rect.left - canvas.clientLeft;
      let my = e.clientY - rect.top - canvas.clientTop;
      self.searchPoint = { x: mx, y: my };
    });
	}
}

let rect = new Rectangle(canvas.width /2, canvas.height /2 - 30, 100, 60);

function vector(p1, p2) {
    return {
            x: (p2.x - p1.x),
            y: (p2.y - p1.y)
    };
}

function point(x, y) {
	return { x, y };
}

// Vector dot operation
function dot(a, b) {
  return a.x * b.x + a.y * b.y;
}

function pointInRect(p, rect, angle) {
	let a = newPointTurningAngle(0, -rect.height / 2, angle);
	let b = newPointTurningAngle(0, rect.height / 2, angle);
  let c = newPointTurningAngle(rect.width, rect.height / 2, angle);
	let AB = vector(a, b);
  let AM = vector(a, p);
  let BC = vector(b, c);
  let BM = vector(b, p);
  let dotABAM = dot(AB, AM);
  let dotABAB = dot(AB, AB);
  let dotBCBM = dot(BC, BM);
  let dotBCBC = dot(BC, BC);
  
  return 0 <= dotABAM && dotABAM <= dotABAB && 0 <= dotBCBM && dotBCBM <= dotBCBC;
}

function drawLine(x, y) {
	ctx.strokeStyle = 'black';
	ctx.lineTo(x, y);
  ctx.stroke();
}

function text(text, x, y) {
	ctx.font = "18px serif";
  ctx.fillText(text, x, y);
}

function newPointTurningAngle(nx, ny, angle) {
	return {
  	x: nx * Math.cos(angle) - ny * Math.sin(angle),
    y: nx * Math.sin(angle) + ny * Math.cos(angle)
  };
}

function animate() {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.setTransform(1, 0, 0, 1, 0, 0);
	ctx.moveTo(canvas.width / 2, 0);
  drawLine(canvas.width /2, canvas.height / 2);
  
  ctx.moveTo(0, canvas.height / 2);
  drawLine(canvas.width / 2, canvas.height /2);
  
	let angle = -Math.PI / 4;
	ctx.setTransform(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), canvas.width / 2, canvas.height / 2);
  //ctx.setTransform(1, 0, 0, 1, canvas.width/2, canvas.height / 2);
	ctx.strokeStyle = 'red';
  ctx.strokeRect(0, -rect.height / 2, rect.width, rect.height);
  
	let p = newPointTurningAngle(rect.searchPoint.x - canvas.width / 2, rect.searchPoint.y - canvas.height / 2, angle);

	let testResult = pointInRect(p, rect, angle);
	if (testResult) {
  	ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.beginPath();
  	ctx.fillStyle = 'black';
  	ctx.arc(rect.searchPoint.x, rect.searchPoint.y, 5, 0, Math.PI * 2);
    ctx.fill();
  }
  
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  text('searchPoint x: ' + rect.searchPoint.x + ', y: ' + rect.searchPoint.y, 60, 430);
  text('x: ' + canvas.width / 2 + ', y: ' + canvas.height / 2, 60, 480);
  
  requestAnimationFrame(animate);
}

animate();

<canvas id='canvas'></canvas>

更新后的解决方案

我仍在使用基于矢量的方法,如下所示:

I am still using the vector based method as followed:

0 <= dot(AB,AM) <= dot(AB,AB) &&
0 <= dot(BC,BM) <= dot(BC,BC)

现在我已经改变了点的旋转角度和角点坐标,因此可以在矩形中检测到点。角点已经在旋转坐标系中,因此它们不需要被平移,但是在矩形区域中测试之前需要翻译鼠标位置的点。

Now I have changed the point's rotated angle and the corner point coordinates so the point can be detected in the rectangle. The corner points are already in the rotated coordinate system so they don't need to be translated, however the point of the mouse location needs to be translated before testing it in the rectangle area.

setTransform 方法中,顺时针旋转时旋转的角度为正,表格为:

In setTransform method the angle rotated is positive when rotated clockwise, the form is :

ctx.setTransform(angle_cosine,angle_sine,-angle_sine,angle_cosine,x,y);

因此,在旋转角度后计算点的新坐标时,公式需要更改为此值,以便顺时针旋转时角度也为正:

So when calculating the point's new coordinate after rotating an angle, the formula need to change to this so that the angle is also positive when rotated clockwise:

 new_x = x * angle_cosine + y * angle_sine;
 new_y = -x * angle_sine + y * angle_cos;

// Detecting a point is in a rotated rectangle area
// using vector based method
const canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const ctx = canvas.getContext('2d');

class Rectangle {
	constructor(x, y, width, height) {
  	this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this.searchPoint = { x: 0, y: 0};
    this.binding();
  }
  
  binding() {
  	let self = this;
    window.addEventListener('mousemove', e => {
      if (!e) return;
      let rect = canvas.getBoundingClientRect();
      let mx = e.clientX - rect.left - canvas.clientLeft;
      let my = e.clientY - rect.top - canvas.clientTop;
      self.searchPoint = { x: mx, y: my };
    });
	}
}

let rect = new Rectangle(canvas.width /2, canvas.height /2 - 30, 100, 60);

function vector(p1, p2) {
    return {
            x: (p2.x - p1.x),
            y: (p2.y - p1.y)
    };
}

function point(x, y) {
	return { x, y };
}

// Vector dot operation
function dot(a, b) {
  return a.x * b.x + a.y * b.y;
}

function pointInRect(p, rect) {
  let a = { x: 0, y: -rect.height / 2};
  let b = { x: 0, y: rect.height / 2};
  let c = { x: rect.width, y: rect.height / 2};
  text('P x: ' + p.x.toFixed() + ', y: ' + p.y.toFixed(), 60, 430);
  text('A x: ' + a.x.toFixed() + ', y: ' + a.y.toFixed(), 60, 455);
  text('B x: ' + b.x.toFixed() + ', y: ' + b.y.toFixed(), 60, 480);
	let AB = vector(a, b);
  let AM = vector(a, p);
  let BC = vector(b, c);
  let BM = vector(b, p);
  let dotABAM = dot(AB, AM);
  let dotABAB = dot(AB, AB);
  let dotBCBM = dot(BC, BM);
  let dotBCBC = dot(BC, BC);
  
  return 0 <= dotABAM && dotABAM <= dotABAB && 0 <= dotBCBM && dotBCBM <= dotBCBC;
}

function drawLine(x, y) {
	ctx.strokeStyle = 'black';
	ctx.lineTo(x, y);
  ctx.stroke();
}

function text(text, x, y) {
	ctx.font = "18px serif";
  ctx.fillText(text, x, y);
}

function newPointTurningAngle(nx, ny, angle) {
	let cos = Math.cos(angle);
  let sin = Math.sin(angle);
	return {
  	x: nx * cos + ny * sin,
    y: -nx * sin + ny * cos
  };
}

function animate() {
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	ctx.setTransform(1, 0, 0, 1, 0, 0);
	ctx.moveTo(canvas.width / 2, 0);
  drawLine(canvas.width /2, canvas.height / 2);
  
  ctx.moveTo(0, canvas.height / 2);
  drawLine(canvas.width / 2, canvas.height /2);
  
    let angle = - Math.PI / 4;
	ctx.setTransform(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), canvas.width / 2, canvas.height / 2);
	ctx.strokeStyle = 'red';
  ctx.strokeRect(0, -rect.height / 2, rect.width, rect.height);
  
  	let p = newPointTurningAngle(rect.searchPoint.x - canvas.width / 2, rect.searchPoint.y - canvas.height / 2, angle);
    
  ctx.setTransform(1, 0, 0, 1, 0, 0);
	let testResult = pointInRect(p, rect);
  
	if (testResult) {
    ctx.beginPath();
  	ctx.fillStyle = 'black';
  	ctx.arc(rect.searchPoint.x, rect.searchPoint.y, 5, 0, Math.PI * 2);
    ctx.fill();
  }
  
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  text('searchPoint x: ' + rect.searchPoint.x + ', y: ' + rect.searchPoint.y, 60, 412);
  text('x: ' + canvas.width / 2 + ', y: ' + canvas.height / 2, 60, 510);
  
  requestAnimationFrame(animate);
}

animate();

<canvas id='canvas'></canvas>

推荐答案

假设您知道如何检查点是否在矩形中,解决方案的方法是旋转并将所有内容(点和矩形)转换为标准化协调系统(笛卡尔坐标系我们很熟悉)然后仔细检查。

Assuming that you know how to check whether a dot is in the rectangle the approach to solution is to rotate and translate everything (dot and rectangle) to "normalized" coordinating system (Cartesian coordinate system that is familiar to us) and then to check it trivially.

有关更多信息,请检查仿射变换。你可以开始的好链接是

For more information you should check Affine transformations. The good link where you could start is

http://www.mathworks.com/discovery/affine-transformation.html?requestedDomain=www.mathworks.com

这篇关于如何测试一个点是否在一个旋转角度的矩形区域?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆