Проверьте, находится ли точка внутри конуса в 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 находится внутри конуса, я должен положить <= вместо = в Формуле. Правильно ли это?

использование этого: я разрабатываю игру, где пулемет должен начать стрелять, когда объект находится в его "вид". Этот вид будет конусом. Вершина конуса будет находиться в пулемете, основание конуса будет на некотором известном расстоянии впереди. Любой объект, входящий в этот конус, пулемет будет стрелять в него.

2 ответов


Я полностью согласен с Тимом: нам нужен "угол" (апертура) конуса, чтобы получить ответ.

давайте тогда сделаем кодирование! Я буду использовать некоторые термины из здесь.

функция, дающая результат:

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

удачи!


вам нужно проверить, является ли угол между вашим вектором разности (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) к длине, давая вам конус с круглым дном (для которого, я уверен, существует имя, но я его не знаю).

Edit: забыли обратный Косинус, точечное произведение равно norm(U)*norm(V)*cos(Angle) поэтому мы должны инвертировать эту операцию, чтобы сравнить углы. В этом случае acos должен быть в порядке, потому что мы хотим, чтобы положительные и отрицательные углы сравнивались одинаково, но следите за этим.

Edit: Радианы.