gpt4 book ai didi

c++ - 3点之间的角度?

转载 作者:IT老高 更新时间:2023-10-28 22:08:28 24 4
gpt4 key购买 nike

给定点ABC,我怎么能找到角ABC?我正在为 vector 绘图应用程序制作一个feehand工具,并尽量减少它生成的点数,除非鼠标位置的角度和最后两个点的角度大于某个阈值,否则我不会添加点。谢谢

我有什么:

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;


}

最佳答案

关于您的方法的初步建议:

你所谓的ac其实就是cb。不过没关系,这才是真正需要的。接下来,

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

这是你的第一个错误。两个 vector 的实数点积是:

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

现在,

float rslt = acos(dacos);

这里您应该注意,由于计算过程中的一些精度损失,理论上 dacos 可能会变得大于 1(或小于 -1)。因此 - 你应该明确地检查这一点。

加上性能说明:您调用了两次重的 sqrt 函数来计算两个 vector 的长度。然后将点积除以这些长度。相反,您可以在两个 vector 的长度平方相乘上调用 sqrt

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

计算角度的一种正确方法是“oslvbo”建议:

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

(我刚刚将 atan 替换为 atan2)。

这是最简单的方法,总能得出正确的结果。这种方法的缺点是你实际上调用了一个重三角函数 atan2 两次。

我建议以下方法。它有点复杂(需要一些三角函数才能理解),但是从性能的角度来看它是优越的。它只调用一次三角函数atan2。并且没有平方根计算。

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


}

编辑:

最近我一直在研究一个相关的主题。然后我意识到有更好的方法。它实际上或多或少是相同的(在幕后)。然而,恕我直言,它更简单。

这个想法是旋转两个 vector ,使第一个 vector 与(正)X 方向对齐。显然旋转两个 vector 不会影响它们之间的角度。 OTOH 在这样的旋转之后,只需找出第二个 vector 相对于 X 轴的角度。这正是 atan2 的用途。

旋转是通过将 vector 乘以以下矩阵来实现的:

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

曾经可以看到 vector a乘以这样一个矩阵确实是向X轴正方向旋转。

注意:严格来说,上面的矩阵不仅仅是旋转,它也是缩放。但这在我们的例子中是可以的,因为唯一重要的是 vector 方向,而不是它的长度。

旋转 vector b变成:

  • a.x * b.x + a.y * b.y = ab
  • -a.y * b.x + a.x * b.y = a 交叉 b

最后,答案可以表示为:

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

关于c++ - 3点之间的角度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3486172/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com