3点之间的角度? [英] Angle between 3 points?

查看:203
本文介绍了3点之间的角度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于分ABC,我怎么能找到角度ABC?我正在做feehand工具的矢量绘图应用程序,并尽量减少其生成,我不会增加一个点,除非鼠标位置和最后2个点的角度大于特定阈值的点的数量。 谢谢

我有:

  INT CGlEngineFunctions :: GetAngleABC(POINTFLOAT一个,POINTFLOAT B,POINTFLOAT C)
{
    POINTFLOAT AB;
    POINTFLOAT交流;

    ab.x = b.x  -  a.x;
    ab.y = b.y  -  a.y;

    ac.x = b.x  -  c.x;
    ac.y = b.y  -  c.y;

    浮dotabac =(ab.x * ab.y + ac.x * ac.y);
    浮lenab =开方(ab.x * ab.x + ab.y * ab.y);
    浮lenac =开方(ac.x * ac.x + ac.y * ac.y);

    浮DACOS = dotabac / lenab / lenac;

    浮RSLT = ACOS(DACOS);
    浮RS =(RSLT * 180)/ 3.141592;
     RoundNumber(RS);
     返程(INT)RS;


}
 

解决方案

关于你的方法首先建议:

你叫什么 C 其实就是 CB 。但它的确定,这是真正需要的。 接下来,

 浮动dotabac =(ab.x * ab.y + ac.x * ac.y);
 

这是你的第一个错误。在真正的点的两个向量的产品是:

 浮动dotabac =(ab.x * ac.x + ab.y * ac.y);
 

现在,

 浮法RSLT = ACOS(DACOS);
 

在这里,你应该注意的是,由于计算是理论上的可能在一些precision损失 DACOS 将成为大于1(或小于比-1)。所以 - 你应该明确地检查这种

另外一个表现记:你调用一个沉重的开方函数两次,计算两个向量的长度。然后,通过这些长度划分点积。 相反,你可以调用开方两个向量的长度的平方的乘积。

最后,你要注意,你的结果是精确到标志。也就是说,你的方法将不区分20°和-20°,因为两者的余弦是相同的。 你的方法将产生相同的角度为ABC和CBA。

一种正确的方法来计算的角度为oslvbo建议:

 浮动昂巴= ATAN2(ab.y,ab.x);
浮angbc = ATAN2(cb.y,c​​b.x);
浮RSLT =昂巴 -  angbc;
浮RS =(RSLT * 180)/ 3.141592;
 

(我刚刚更换反正切 ATAN2 )。

这是最简单的方法,它总是产生正确的结果。这种方法的缺点是,你实际上调用了沉重的三角函数 ATAN2 键两次。

我建议以下方法。这是一个有点复杂(需要一些三角技能来理解),但它是从性能的角度来看优越。 它只是再次呼吁三角函数 ATAN2 。而且没有平方根计算。

  INT CGlEngineFunctions :: GetAngleABC(POINTFLOAT一个,POINTFLOAT B,POINTFLOAT C)
{
    POINTFLOAT AB = {b.x  -  a.x,b.y  -  a.y};
    POINTFLOAT CB = {b.x  -  c.x,b.y  -  c.y};

    //积
    浮动点=(ab.x * cb.x + ab.y * cb.y);

    //长两个向量的平方
    浮abSqr = ab.x * ab.x + ab.y * ab.y;
    浮cbSqr = cb.x * cb.x + cb.y * cb.y;

    //所需角度的余弦的平方
    浮cosSqr =点*点/ abSqr / cbSqr;

    //这是一个已知三角函数的平等:
    //余弦(阿尔法* 2)= [COS(阿尔法)] ^ 2 * 2  -  1
    浮cos2的= 2 * cosSqr  -  1;

    //这是重功能的唯一调用。
    //这是明确检查一个好主意,如果cos2的是在[-1 ... 1]范围

    常量浮PI = 3.141592f;

    浮动的α2=
        (cos2的&其中; = -1)? PI:
        (cos2的> = 1)? 0:
        acosf(cos2的);

    浮RSLT =α2/2;

    浮RS = RSLT * 180 / PI;


    //现在旋转的多义性。
    // 1.如果两个向量的点积为负 - 的角度为肯定
    90度以上//。不过,我们有关于角的符号的信息。

    //注:这种不确定性是我们的方法的结果是:计算余弦
    //双角。这使我们能够摆脱叫开方的。

    如果(DOT< 0)
        RS = 180  -  RS;

    // 2.确定的标志。为此,我们将使用两个向量的决定因素。

    浮DET =(ab.x * cb.y  -  ab.y * cb.y);
    如果(DET℃,)
        RS = -rs;

    返回(INT)地板(RS + 0.5);


}
 

编辑:

最近我一直工作在一个相关的主题。然后,我意识到有一个更好的办法。它实际上更多或更少相同的(幕后)。然而,它的更直接恕我直言。

我们的想法是旋转两种载体,使得第一个对齐(正)X方向。显然旋转两种载体不影响它们之间的角度。 OTOH这样旋转后的一个只是必须找出第二矢量相对于X轴的角度。而这正是 ATAN2

旋转由由以下矩阵乘以一个向量来实现的:

  • a.x,a.y
  • -a.y,a.x

在可以看到的vector A 乘这样的矩阵确实朝正X轴旋转。

注意:严格地说上述矩阵不仅仅是旋转,它也扩展。但是,这是确定在我们的情况下,由于唯一重要的事情是矢量方向,而不是它的长度。

旋转矢量 B 变为:

  • AX * BX + AY *通过= 点的 B
  • -ay * BX + AX​​ *通过= 交叉的 B

最后,答案可以是pssed作为前$ P $:

  INT CGlEngineFunctions :: GetAngleABC(POINTFLOAT一个,POINTFLOAT B,POINTFLOAT C)
{
    POINTFLOAT AB = {b.x  -  a.x,b.y  -  a.y};
    POINTFLOAT CB = {b.x  -  c.x,b.y  -  c.y};

    浮动点=(ab.x * cb.x + ab.y * cb.y); //积
    浮跨=(ab.x * cb.y  -  ab.y * cb.x); //积

    浮阿尔法= ATAN2(横,点);

    返程(INT)地板(阿尔法* 180 / PI + 0.5);
}
 

Given points ABC, how could I find angle ABC? I'm making a feehand tool for a vector drawing application and to minimize the number of points it generates, I wont add a points unless the angle of the mouse position and the last 2 points is greater than a certain threshold. Thanks

what I had:

int CGlEngineFunctions::GetAngleABC( POINTFLOAT a, POINTFLOAT b, POINTFLOAT c )
{
    POINTFLOAT ab;
    POINTFLOAT ac;

    ab.x = b.x - a.x;
    ab.y = b.y - a.y;

    ac.x = b.x - c.x;
    ac.y = b.y - c.y;

    float dotabac = (ab.x * ab.y + ac.x * ac.y);
    float lenab = sqrt(ab.x * ab.x + ab.y * ab.y);
    float lenac = sqrt(ac.x * ac.x + ac.y * ac.y);

    float dacos = dotabac / lenab / lenac;

    float rslt = acos(dacos);
    float rs = (rslt * 180) / 3.141592;
     RoundNumber(rs);
     return (int)rs;


}

解决方案

First suggestions regarding your method:

What you call ac is actually cb. But it's ok, this is what really needed. Next,

float dotabac = (ab.x * ab.y + ac.x * ac.y);

This is your first mistake. The real dot product of two vectors is:

float dotabac = (ab.x * ac.x + ab.y * ac.y);

Now,

float rslt = acos(dacos);

Here you should note that due to some precision loss during the calculation it's theoretically possible that dacos will become bigger than 1 (or lesser than -1). Hence - you should check this explicitly.

Plus a performance note: you call a heavy sqrt function twice for calculating the length of two vectors. Then you divide the dot product by those lengths. Instead you could call sqrt on the multiplication of squares of length of both vectors.

And lastly, you should note that your result is accurate up to the sign. That is, your method won't distinguish 20° and -20°, since the cosine of both are the same. Your method will yield the same angle for ABC and CBA.

One correct method for calculating the angle is as "oslvbo" suggests:

float angba = atan2(ab.y, ab.x);
float angbc = atan2(cb.y, cb.x);
float rslt = angba - angbc;
float rs = (rslt * 180) / 3.141592;

(I've just replaced atan by atan2).

It's the simplest method, which always yields the correct result. The drawback of this method is that you actually call a heavy trigonometry function atan2 twice.

I suggest the following method. It's a bit more complex (requires some trigonometry skills to understand), however it's superior from the performance point of view. It just calls once a trigonometry function atan2. And no square root calculations.

int CGlEngineFunctions::GetAngleABC( POINTFLOAT a, POINTFLOAT b, POINTFLOAT c )
{
    POINTFLOAT ab = { b.x - a.x, b.y - a.y };
    POINTFLOAT cb = { b.x - c.x, b.y - c.y };

    // dot product  
    float dot = (ab.x * cb.x + ab.y * cb.y);

    // length square of both vectors
    float abSqr = ab.x * ab.x + ab.y * ab.y;
    float cbSqr = cb.x * cb.x + cb.y * cb.y;

    // square of cosine of the needed angle    
    float cosSqr = dot * dot / abSqr / cbSqr;

    // this is a known trigonometric equality:
    // cos(alpha * 2) = [ cos(alpha) ]^2 * 2 - 1
    float cos2 = 2 * cosSqr - 1;

    // Here's the only invocation of the heavy function.
    // It's a good idea to check explicitly if cos2 is within [-1 .. 1] range

    const float pi = 3.141592f;

    float alpha2 =
        (cos2 <= -1) ? pi :
        (cos2 >= 1) ? 0 :
        acosf(cos2);

    float rslt = alpha2 / 2;

    float rs = rslt * 180. / pi;


    // Now revolve the ambiguities.
    // 1. If dot product of two vectors is negative - the angle is definitely
    // above 90 degrees. Still we have no information regarding the sign of the angle.

    // NOTE: This ambiguity is the consequence of our method: calculating the cosine
    // of the double angle. This allows us to get rid of calling sqrt.

    if (dot < 0)
        rs = 180 - rs;

    // 2. Determine the sign. For this we'll use the Determinant of two vectors.

    float det = (ab.x * cb.y - ab.y * cb.y);
    if (det < 0)
        rs = -rs;

    return (int) floor(rs + 0.5);


}

EDIT:

Recently I've been working on a related subject. And then I've realized there's a better way. It's actually more-or-less the same (behind the scenes). However it's more straightforward IMHO.

The idea is to rotate both vectors so that the first one is aligned to (positive) X-direction. Obviously rotating both vectors doesn't affect the angle between them. OTOH after such a rotation one just has to find out the angle of the 2nd vector relative to X-axis. And this is exactly what atan2 is for.

Rotation is achieved by multiplying a vector by the following matrix:

  • a.x, a.y
  • -a.y, a.x

Once may see that vector a multiplied by such a matrix indeed rotates toward positive X-axis.

Note: Strictly speaking the above matrix isn't just rotating, it's also scaling. But this is ok in our case, since the only thing that matters is the vector direction, not its length.

Rotated vector b becomes:

  • a.x * b.x + a.y * b.y = a dot b
  • -a.y * b.x + a.x * b.y = a cross b

Finally, the answer can be expressed as:

int CGlEngineFunctions::GetAngleABC( POINTFLOAT a, POINTFLOAT b, POINTFLOAT c )
{
    POINTFLOAT ab = { b.x - a.x, b.y - a.y };
    POINTFLOAT cb = { b.x - c.x, b.y - c.y };

    float dot = (ab.x * cb.x + ab.y * cb.y); // dot product
    float cross = (ab.x * cb.y - ab.y * cb.x); // cross product

    float alpha = atan2(cross, dot);

    return (int) floor(alpha * 180. / pi + 0.5);
}

这篇关于3点之间的角度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆