2015-05-02 27 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>