简体   繁体   English

验证点是否在3D空间中的圆锥内

[英]Verify if point is inside a cone in 3D space

Consider: 考虑:

  • X(x1,y1,z1) the point I need to verify if it is inside a cone. X(x1,y1,z1)我需要验证它是否在锥体内。
  • M(x2,y2,z2) the vertex of the cone. M(x2,y2,z2)圆锥的顶点。 (the top point of the cone) (锥体的顶点)
  • N(x3,y3,z3) the point in the middle of the cone's base. N(x3,y3,z3)锥体底部中间的点。

I found out that if a point X is on the cone, it needs to verify this equation: 我发现如果一个点X在锥体上,它需要验证这个等式:

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

Where dot is the scalar product of 2 vectors, and alfa is the angle between these 2 vectors. 其中dot是2个向量的标量积,而alfa是这两个向量之间的角度。

Based on the formula, I calculated that: 根据公式,我计算出:

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

So, 所以,

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)

Unfortunatelly the above calculations seem to give me wrong results. 不幸的是,上述计算似乎给我错误的结果。 What am I doing wrong? 我究竟做错了什么?

Also I suspect that to check if X is inside the cone, I have to put <= instead of = in the formula. 另外我怀疑要检查X是否在锥体内部,我必须在公式中放置<=而不是= Is this correct? 它是否正确?

The usage of this is: I develop a game where a machine gun has to start firing when an object is in its 'view'. 这个的用法是:我开发了一种游戏,当一个物体进入“视野”时,机枪必须开始射击。 This view will be a cone. 这个视图将是一个圆锥体。 The cone's vertex would be in the machine gun, the base of the cone will be at some known distance ahead. 锥体的顶点将位于机枪中,锥体的底部将位于前方的某个已知距离处。 Any object entering this cone, the machine gun will shoot it. 进入这个锥体的任何物体,机枪都会射击它。

I totally agree with Tim: we need "angle" (aperture) of cone to get the answer. 我完全赞同蒂姆:我们需要锥形的“角度”(光圈)才能得到答案。

Let's do some coding then! 那我们做一些编码吧! I'll use some terminology from here . 我将从这里使用一些术语。

Result-giving function: 结果给予功能:

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

Below are my fast implementations of basic functions, required by the upper code to manipulate vectors. 下面是我的基本函数的快速实现,上层代码需要操作向量。

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

Have fun! 玩得开心!

You need to check whether the angle between your difference vector (XM) and your center vector (N) is less than or equal to the angle of your cone (which you haven't specified in the question). 您需要检查差异向量(XM)与中心向量(N)之间的角度是否小于或等于锥体的角度(您未在问题中指定)。 This will tell you if the position vector (X) is inside the infinite cone, and you can then also check for distance (if you want). 这将告诉您位置向量(X)是否在无限锥内,然后您还可以检查距离(如果需要)。 So, 所以,

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

For performance, you could also normalize N (convert it to a vector with length 1) and store the length separately. 为了提高性能,您还可以将N标准化(将其转换为长度为1的向量)并分别存储长度。 You could then compare norm(XM) to the length, giving you a round-bottom cone (for which I'm sure a name exists, but I don't know it). 然后你可以将norm(XM)与长度进行比较,给你一个圆底锥(为此我确定存在一个名字,但我不知道它)。

Edit: Forgot the inverse cosine, the dot product is equal to norm(U)*norm(V)*cos(Angle) so we have to invert that operation to compare the angles. 编辑:忘记反余弦,点积等于norm(U)*norm(V)*cos(Angle)所以我们必须反转该操作来比较角度。 In this case, the acos should be fine because we want positive and negative angles to compare equally, but watch out for that. 在这种情况下,acos应该没问题,因为我们想要正负角度进行相同的比较,但要注意这一点。

Edit: Radians. 编辑:弧度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM