2015-05-02 115 views
0

我想要做的是这样的:
我有两个(x,y)点,p1和p2以及一个来自p1的旋转值(以弧度表示的角度)。 P2还有其他两个变量,一个宽度和一个高度,我将称之为p2w和p2h。我想检查p1的角度是否与p2的边界相交,在宽度和/或高度的半径内。换句话说,如果角度“穿透”中心p2,宽度p2w和高度p2h的平方。检查一个矢量/角度是否与区域相交

这里有一个更好的理解图:
http://i.imgur.com/Y7WFD36.png

我一直试图做的是这样的:

if(p1.rot > (Math.atan2(p2.y-p2h, p2.x-p2w)) 
&& p1.rot < (Math.atan2(p2.y+p2h, p2.x+p2w))) 
//There's an intersection 

但正如你猜想的那样,预期它不工作;有没有另一种方法来做到这一点?

回答

3

数学队长来救援!

你在问光线是否与矩形相交。这是我们需要做的。首先,使用点和矢量或者点和角来定义射线。由于使用矢量更容易,因此我们将角度转换为矢量。使用毕达哥拉斯定理,你的角度φ与矢量n = {x: Math.cos(phi), y: Math.sin(phi)}相同。

我会重命名您的变量以使表示法更容易。我会用p表示你的p1,用r表示你的隐式定义的矩形。

现在,任何随机点M躺在射线,下列公式必须持有:

M.x = p.x + n.x * alpha (1) 
M.y = p.y + n.y * alpha 

对于一些真正的阿尔法。类似地,对于任何随机点M位于该矩形内,下面的不等式必须成立:

M.x >= r.x 
M.x <= r.x + r.w 
M.y >= r.y 
M.y <= r.y + r.h 

对于指向躺在两个射线和矩形两个方程中和不等式必须成立。代上面的价值观,我们得到:

p.x + n.x * alpha >= r.x 
p.x + n.x * alpha <= r.x + r.w 
p.y + n.y * alpha >= r.y 
p.y + n.y * alpha <= r.y + r.h 

求解α和我们得到:

alpha >= (r.x - p.x)/n.x 
alpha <= (r.x + r.w - p.x)/n.x 
alpha >= (r.y - p.y)/n.y 
alpha <= (r.y + r.h - p.y)/n.y 

上面的系统当且仅当一个解决方案:

var lowerLimitX = (r.x - p.x)/n.x; 
var lowerLimitY = (r.y - p.y)/n.y; 
var upperLimitX = (r.x + r.w - p.x)/n.x; 
var upperLimitY = (r.y + r.h - p.y)/n.y; 
var minAlpha = Math.max(lowerLimitX, lowerLimitY); 
var maxAlpha = Math.min(upperLimitX, upperLimitY); 
var hasSolution = minAlpha<= maxAlpha; 

现在,如果上面的系统有一个解决方案,至少有一个点位于射线和矩形上,换句话说,它们相交。

编辑:这是working demo。移动鼠标以查看结果。请注意,由于Y轴在HTML画布API中向下增加,因此必须交换Y轴的下限和上限。

编辑2:如果您关心@pfannkuchen_gesicht建议的交点段(注意一般而言,交点将是线段而不是点),那么也很容易。正如我们已经知道的那样,对于交点上的点,射线方程必须成立。要找到这些点本身,只需将alpha替换为范围[minAlpha; (1)中的maxAlpha]。例如,最接近的点是p + minAlpha * n,最远的是p + maxAlpha * n,并且中间的随机点是p +(minAlpha + Math.random() * (maxAlpha - minAlpha)) * n

+0

@markE看我的编辑。 –

+0

Upvote,简洁的工作! – markE

+0

真的很不错! 我唯一缺少的是一个交叉点(OP没有要求这样做,但对于某些应用程序会很有趣),但我认为这是一个很好的快速预测方法。 –

1

以下是测试线段(ray?)和矩形相交的一种方法。

只要测试线段/射线是否与矩形的2条对角线中的任何一条相交。

enter image description here

示例代码和演示:

var canvas=document.getElementById("canvas"); 
 
var ctx=canvas.getContext("2d"); 
 
var cw=canvas.width; 
 
var ch=canvas.height; 
 
ctx.lineWidth=3; 
 
function reOffset(){ 
 
    var BB=canvas.getBoundingClientRect(); 
 
    offsetX=BB.left; 
 
    offsetY=BB.top;   
 
} 
 
var offsetX,offsetY; 
 
reOffset(); 
 
window.onscroll=function(e){ reOffset(); } 
 

 

 
var ray1={x:30,y:250,angle:-Math.PI/3.5}; 
 
var ray2={x:30,y:250,angle:-Math.PI/6}; 
 
var r={x:100,y:100,w:40,h:40}; 
 

 
ctx.strokeStyle='black'; 
 
ctx.strokeRect(r.x,r.y,r.w,r.h); 
 
// this ray intersects the rect 
 
drawRay(ray1,r); 
 
// this ray doesn't intersect the rect 
 
drawRay(ray2,r); 
 

 

 
function drawRay(ray,rect){ 
 
    var intersects=rayRectIntersect(ray,rect); 
 
    ctx.beginPath(); 
 
    ctx.moveTo(ray.x,ray.y); 
 
    ctx.lineTo(ray.x+1000*Math.cos(ray.angle),ray.y+1000*Math.sin(ray.angle)); 
 
    ctx.strokeStyle=(intersects)?'red':'green'; 
 
    ctx.stroke(); 
 
} 
 

 
function rayRectIntersect(ray,rect){ 
 
    d0={x:rect.x,y:rect.y}; 
 
    d1={x:rect.x+rect.w,y:rect.y+rect.h}; 
 
    d2={x:rect.x,y:rect.y+rect.h}; 
 
    d3={x:rect.x+rect.w,y:rect.y}; 
 
    ray0={x:ray.x,y:ray.y}; 
 
    ray1={x:ray.x+1000*Math.cos(ray.angle),y:ray.y+1000*Math.sin(ray.angle)}; 
 
    var diag1Test=line2lineIntersection(ray0,ray1,d0,d1); 
 
    var diag2Test=line2lineIntersection(ray0,ray1,d2,d3); 
 
    return(diag1Test || diag2Test); 
 
} 
 

 
// Get interseting point of 2 line segments (if any) 
 
// Attribution: http://paulbourke.net/geometry/pointlineplane/ 
 
function line2lineIntersection(p0,p1,p2,p3) { 
 

 
    var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x); 
 
    var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x); 
 
    var denominator = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);   
 

 
    // Test if Coincident 
 
    // If the denominator and numerator for the ua and ub are 0 
 
    // then the two lines are coincident.  
 
    if(unknownA==0 && unknownB==0 && denominator==0){return(true);} 
 

 
    // Test if Parallel 
 
    // If the denominator for the equations for ua and ub is 0 
 
    //  then the two lines are parallel. 
 
    if (denominator == 0) return false; 
 

 
    // If the intersection of line segments is required 
 
    // then it is only necessary to test if ua and ub lie between 0 and 1. 
 
    // Whichever one lies within that range then the corresponding 
 
    // line segment contains the intersection point. 
 
    // If both lie within the range of 0 to 1 then 
 
    // the intersection point is within both line segments. 
 
    unknownA /= denominator; 
 
    unknownB /= denominator; 
 

 
    var isIntersecting=(unknownA>=0 && unknownA<=1 && unknownB>=0 && unknownB<=1) 
 

 
    if(!isIntersecting){return(false);} 
 

 
    return({ 
 
    x: p0.x + unknownA * (p1.x-p0.x), 
 
    y: p0.y + unknownA * (p1.y-p0.y) 
 
    }); 
 
}
body{ background-color: ivory; } 
 
#canvas{border:1px solid red;}
<h4>Ray is red if intersecting, green if not</h4> 
 
<canvas id="canvas" width=300 height=300></canvas>