2012-05-26 24 views
6

考慮:驗證,如果點在3D空間中的錐體內

  • X(x1,y1,z1)我需要驗證,如果它是一個圓錐體裏面的點。
  • M(x2,y2,z2)錐體的頂點。 (圓錐體的頂點)
  • N(x3,y3,z3)圓錐體底部中間的點。

我發現,如果一個點X是在圓錐,它需要驗證該方程:

cos(alfa) * ||X-M|| * ||N|| = dot(X-M,N) 

其中點是2個向量的標量積,和阿爾法是它們之間的角度2個載體。

根據公式,我計算過:

X-M = (x1-x2,y1-y2,z1-z2) 

所以,

cos(alfa) 
    * Math.sqrt((x1-x2)^2+(y1-y2)^2+(z1-z2)^2) 
    * Math.sqrt(x3^2 + y3^2+z3^2) 
= x3(x1-x2) + y3(y1-y2) + z3(z1-z2) 

可惜的是上述計算似乎給了我錯誤的結果。我究竟做錯了什麼?

另外我懷疑要檢查X是否在圓錐內,我必須在公式中輸入<=而不是=。它是否正確?

這個用法是:我開發一個遊戲,機槍在物體處於「視圖」時必須開始射擊。這個觀點將是一個圓錐體。錐體的頂點將在機槍中,錐體的底部將位於前方的某個已知距離處。任何物體進入這個錐體,機槍都會射擊它。

回答

11

我完全同意蒂姆:我們需要錐角的「角度」(光圈)來得到答案。

讓我們來做一些編碼吧! 我將使用here的一些術語。

結果-給予功能:

/** 
* @param x coordinates of point to be tested 
* @param t coordinates of apex point of cone 
* @param b coordinates of center of basement circle 
* @param aperture in radians 
*/ 
static public boolean isLyingInCone(float[] x, float[] t, float[] b, 
            float aperture){ 

    // This is for our convenience 
    float halfAperture = aperture/2.f; 

    // Vector pointing to X point from apex 
    float[] apexToXVect = dif(t,x); 

    // Vector pointing from apex to circle-center point. 
    float[] axisVect = dif(t,b); 

    // X is lying in cone only if it's lying in 
    // infinite version of its cone -- that is, 
    // not limited by "round basement". 
    // We'll use dotProd() to 
    // determine angle between apexToXVect and axis. 
    boolean isInInfiniteCone = dotProd(apexToXVect,axisVect) 
           /magn(apexToXVect)/magn(axisVect) 
           > 
           // We can safely compare cos() of angles 
           // between vectors instead of bare angles. 
           Math.cos(halfAperture); 


    if(!isInInfiniteCone) return false; 

    // X is contained in cone only if projection of apexToXVect to axis 
    // is shorter than axis. 
    // We'll use dotProd() to figure projection length. 
    boolean isUnderRoundCap = dotProd(apexToXVect,axisVect) 
           /magn(axisVect) 
           < 
           magn(axisVect); 
    return isUnderRoundCap; 
} 

下面是我的基本功能快速實現方式中,由上部代碼操作矢量所需要。

static public float dotProd(float[] a, float[] b){ 
    return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; 
} 

static public float[] dif(float[] a, float[] b){ 
    return (new float[]{ 
      a[0]-b[0], 
      a[1]-b[1], 
      a[2]-b[2] 
    }); 
} 

static public float magn(float[] a){ 
    return (float) (Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2])); 
} 

玩得開心!

+0

這是一個很好的答案,更接近我期望的生產解決方案的樣子! – Tim

+0

謝謝你furikuretsu!奇蹟般有效。 –

+0

很高興你喜歡它,夥計們! – fyodorananiev

1

您需要檢查差異向量(X-M)和中心向量(N)之間的角度是否小於或等於您的錐體角度(您未在問題中指定)。這會告訴你位置矢量(X)是否位於無限錐體內,然後可以檢查距離(如果需要)。所以,

float theta = PI/6; //half angle of cone 
if (acos(dot(X-M, N)/(norm(X-M)*norm(N)) <= theta) doSomething(); 

對於性能,你也可以規範N(將其轉換爲長度爲1的矢量)並分別存儲長度。然後,您可以將norm(X-M)與長度進行比較,從而爲您提供一個圓錐形的圓錐體(爲此我確定存在一個名稱,但我不知道它)。

編輯:忘了反餘弦,點積等於norm(U)*norm(V)*cos(Angle)所以我們必須反轉該操作來比較角度。在這種情況下,acos應該沒問題,因爲我們希望正面和負面的角度進行比較,但要小心。

編輯:Radians。

+0

30弧度對於錐體的半角看起來有點大。 –

+0

@AndrewMorton謝謝!今天我穿着我的思維帽。 – Tim

+0

你的公式是否包含兩部分的錐體?大概這把槍不會同時向後燃燒;) –