3点之间的角度?

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

我有什么:

int CGlEngineFunctions::GetAngleABC( POINTFLOAT a, POINTFLOAT b, POINTFLOAT c ) { POINTFLOAT ab; POINTFLOAT ac; ab.x = bx - ax; ab.y = by - ay; ac.x = bx - cx; ac.y = by - cy; 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); 

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

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

现在,

 float rslt = acos(dacos); 

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

还有一个性能说明:为了计算两个向量的长度,你会调用一个重的sqrt函数两次。 然后将点积除以这些长度。 相反,你可以在两个向量长度的平方乘法上调用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; 

(我刚用atan2取代了atan )。

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

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

 int CGlEngineFunctions::GetAngleABC( POINTFLOAT a, POINTFLOAT b, POINTFLOAT c ) { POINTFLOAT ab = { bx - ax, by - ay }; POINTFLOAT cb = { bx - cx, by - cy }; // 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); } 

编辑:

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

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

通过将矢量乘以以下矩阵来实现旋转:

  • 斧头,唉
  • - 斧头

一旦可以看到该向量乘以这样的矩阵确实向正X轴旋转。

注意:严格来说,上面的矩阵不只是旋转,它也是缩放。 但在我们的情况下这是可以的,因为唯一重要的是矢量方向,而不是它的长度。

旋转的矢量b变为:

  • ax * bx + ay * by = a b
  • -ay * bx + ax * by = a cross b

最后,答案可以表达为:

 int CGlEngineFunctions::GetAngleABC( POINTFLOAT a, POINTFLOAT b, POINTFLOAT c ) { POINTFLOAT ab = { bx - ax, by - ay }; POINTFLOAT cb = { bx - cx, by - cy }; 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); } 

β= arccos((a ^ 2 + c ^ 2 – b ^ 2)/ 2ac)

其中a是角度α相反的一侧,b是角度β,c是角度γ。 所以β就是你所说的角度ABC。

使用arccos方法很危险,因为我们冒险让它的参数等于,例如1.0000001,最终会出现EDOMAIN错误。 即使atan方法也很危险,因为它涉及分裂,这可能导致零误差的分裂。 更好地使用atan2 ,将dxdy值传递给它。

以下是计算直角值的快速且正确的方法:

 double AngleBetweenThreePoints(POINTFLOAT pointA, POINTFLOAT pointB, POINTFLOAT pointC) { float a = pointB.x - pointA.x; float b = pointB.y - pointA.y; float c = pointB.x - pointC.x; float d = pointB.y - pointC.y; float atanA = atan2(a, b); float atanB = atan2(c, d); return atanB - atanA; } 

无关? 但你可以用余弦定律来做到这一点:

找到A和B之间的距离(称为x),B和C之间的距离(称为y),以及A和C之间的距离(称为z)。

然后你知道z ^ 2 = x ^ 2 + y ^ 2-2 * x y cos(你想知道的)

因此,该角度为cos ^ -1((z ^ 2-x ^ 2-y ^ 2)/(2xy))= ANGLE

 float angba = atan((ay - by) / (ax - bx)); float angbc = atan((cy - by) / (cx - by)); float rslt = angba - angbc; float rs = (rslt * 180) / 3.141592; 

这是一个OpenCV方法来获得3点(A,B,C)与B作为顶点之间的角度:

 int getAngleABC( cv::Point2d a, cv::Point2d b, cv::Point2d c ) { cv::Point2d ab = { bx - ax, by - ay }; cv::Point2d cb = { bx - cx, by - cy }; 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. / M_PI + 0.5); } 

基于@valdo的出色解决方案