2012-05-26 5 views
6

considerare:Verificare se il punto è all'interno di un cono nello spazio 3D

  • X(x1,y1,z1) punto ho bisogno di verificare se è all'interno di un cono.
  • M(x2,y2,z2) il vertice del cono. (il punto più alto del cono)
  • N(x3,y3,z3) il punto nel mezzo della base del cono.

ho scoperto che se un punto X è sul cono, è necessario verificare questa equazione:

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

Dove punto è il prodotto scalare dei 2 vettori, e alfa è l'angolo tra queste 2 vettori.

sulla base della formula, ho calcolato che:

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

Quindi,

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) 

Purtroppo i calcoli di cui sopra sembrano darmi risultati errati. Che cosa sto facendo di sbagliato?

Inoltre sospetto che per controllare se X è all'interno del cono, devo mettere <= anziché = nella formula. È corretto?

L'utilizzo di questo è: sviluppo un gioco in cui una mitragliatrice deve iniziare a sparare quando un oggetto è nella sua "vista". Questa vista sarà un cono. Il vertice del cono si troverebbe nella mitragliatrice, la base del cono si troverà ad una certa distanza nota. Qualsiasi oggetto che entra in questo cono, la mitragliatrice gli sparerà.

risposta

11

Sono assolutamente d'accordo con Tim: abbiamo bisogno di "angolo" (apertura) del cono per ottenere la risposta.

Facciamo un po 'di codice allora! Utilizzerò alcuni termini da here.

Risultato donazione funzione:

/** 
* @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; 
} 

Qui di seguito sono i miei implementazioni veloci di funzioni di base, richieste dal codice superiore per manipolare vettori.

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

Buon divertimento!

+0

Questa è un'ottima risposta, e molto più vicina a ciò che mi aspetterei che una soluzione di produzione assomigli! – Tim

+0

Grazie furikuretsu! Funziona come un fascino. –

+0

Molto contento che ti piaccia, ragazzi! – fyodorananiev

1

È necessario verificare se l'angolo tra il vettore di differenza (X-M) e il vettore centrale (N) è minore o uguale all'angolo del cono (che non è stato specificato nella domanda). Questo ti dirà se il vettore di posizione (X) si trova all'interno del cono infinito, e puoi anche controllare la distanza (se vuoi). Così,

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

Per le prestazioni, si potrebbe anche normalizzare N (convertirlo in un vettore di lunghezza 1) e conservare la lunghezza separatamente. È quindi possibile confrontare norm(X-M) per la lunghezza, dandovi un cono con fondo arrotondato (per il quale sono sicuro che esiste un nome, ma non lo conosco).

Modifica: dimenticato il coseno inverso, il prodotto punto è uguale a norm(U)*norm(V)*cos(Angle) quindi dobbiamo invertire tale operazione per confrontare gli angoli. In questo caso, l'aco dovrebbe andare bene perché vogliamo che gli angoli positivi e negativi siano paragonabili, ma attenzione.

Modifica: radianti.

+0

30 radianti sembra un po 'grande per il mezzo angolo di un cono. –

+0

@AndrewMorton Grazie! Oggi indosso il mio cappello pensante. – Tim

+0

La formula rappresenta un cono con due parti? Presumibilmente la pistola non spara all'indietro allo stesso tempo;) –