There is a very handy set of 2d geometry utilities here.
尽管如此,angleBetweenLines存在问题。结果总是积极的。我需要检测正角度和负角度,所以如果一条线在另一条线“上方”或“下方”15度,则形状显然看起来不同。
我的配置是一条线保持静止,而另一条线旋转,我需要通过将它与固定线进行比较来了解它旋转的方向。
编辑:为了回应下面的swestrup评论,情况实际上是我有一条线,我记录了它的起始位置。然后该线从其起始位置旋转,我需要计算从其起始位置到当前位置的角度。例如,如果它顺时针旋转,则为正旋转;如果逆时针,则为负。 (反之亦然。)如何改进算法,使其根据线的位置返回正负角度?
答案 0 :(得分:20)
这是brainjam建议的实现。 (它符合我的约束,线条之间的差异保证足够小,不需要对任何东西进行标准化。)
CGFloat angleBetweenLinesInRad(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
CGFloat a = line1End.x - line1Start.x;
CGFloat b = line1End.y - line1Start.y;
CGFloat c = line2End.x - line2Start.x;
CGFloat d = line2End.y - line2Start.y;
CGFloat atanA = atan2(a, b);
CGFloat atanB = atan2(c, d);
return atanA - atanB;
}
我喜欢它简洁明了。矢量版本会更简洁吗?
答案 1 :(得分:8)
@ duffymo的回答是正确的,但如果您不想实现跨产品,则可以使用atan2功能。这将返回-π和π之间的角度,您可以在每条线上使用它(或者更准确地说是表示线条的矢量)。
如果你得到第一个(固定线)的角度θ,你必须将第二条线的角度φ标准化,使其在θ-π和θ+π之间(加上±2π)。那么两条线之间的角度将是φ-θ。
答案 2 :(得分:7)
这是一个涉及2D矢量的简单问题。两个矢量之间的角度的正弦值与两个矢量之间的交叉积相关。并且“上方”或“下方”由交叉乘积产生的向量的符号确定:如果跨越两个向量A和B,并且产生的交叉积为正,则A“低于”B;如果它是否定的,则A“高于”B.有关详细信息,请参阅Mathworld。
以下是我在Java中编写代码的方法:
package cruft;
import java.text.DecimalFormat;
import java.text.NumberFormat;
/**
* VectorUtils
* User: Michael
* Date: Apr 18, 2010
* Time: 4:12:45 PM
*/
public class VectorUtils
{
private static final int DEFAULT_DIMENSIONS = 3;
private static final NumberFormat DEFAULT_FORMAT = new DecimalFormat("0.###");
public static void main(String[] args)
{
double [] a = { 1.0, 0.0, 0.0 };
double [] b = { 0.0, 1.0, 0.0 };
double [] c = VectorUtils.crossProduct(a, b);
System.out.println(VectorUtils.toString(c));
}
public static double [] crossProduct(double [] a, double [] b)
{
assert ((a != null) && (a.length >= DEFAULT_DIMENSIONS ) && (b != null) && (b.length >= DEFAULT_DIMENSIONS));
double [] c = new double[DEFAULT_DIMENSIONS];
c[0] = +a[1]*b[2] - a[2]*b[1];
c[1] = +a[2]*b[0] - a[0]*b[2];
c[2] = +a[0]*b[1] - a[1]*b[0];
return c;
}
public static String toString(double [] a)
{
StringBuilder builder = new StringBuilder(128);
builder.append("{ ");
for (double c : a)
{
builder.append(DEFAULT_FORMAT.format(c)).append(' ');
}
builder.append("}");
return builder.toString();
}
}
检查第3个组件的符号。如果是正数,A是“低于”B;如果它是负的,A是“高于”B - 只要两个矢量位于y轴右侧的两个象限中。显然,如果它们都位于y轴左侧的两个象限中,则反之亦然。
您需要考虑“上方”和“下方”的直观概念。如果A在第一象限(0 <=θ<= 90)并且B在第二象限(90 <=θ<= 180)中怎么办? “上方”和“下方”失去了意义。
然后线从它旋转 起始位置,我需要 从起始角度计算角度 位置到当前位置。例如,如果 它顺时针旋转,它是 正旋转;如果 逆时针,然后是负面的。 (要么 反之亦然。)
这正是跨产品的用途。第三个分量的符号对于逆时针是正的而对于顺时针是负的(当你向下看旋转的平面时)。
答案 3 :(得分:1)
您可以使用的一种“快速且脏”的方法是引入第三条参考线R.因此,给定两条线A和B,计算A和R之间的角度,然后计算B和R,并减去它们。
这大约是实际需要的计算量的两倍,但很容易解释和调试。
答案 4 :(得分:1)
// Considering two vectors CA and BA
// Computing angle from CA to BA
// Thanks to code shared by Jaanus, but atan2(y,x) is used wrongly.
float getAngleBetweenVectorsWithSignInDeg(Point2f C, Point2f A, Point2f B)
{
float a = A.x - C.x;
float b = A.y - C.y;
float c = B.x - C.x;
float d = B.y - C.y;
float angleA = atan2(b, a);
float angleB = atan2(d, c);
cout << "angleA: " << angleA << "rad, " << angleA * 180 / M_PI << " deg" << endl;
cout << "angleB: " << angleB << "rad, " << angleB * 180 / M_PI << " deg" << endl;
float rotationAngleRad = angleB - angleA;
float thetaDeg = rotationAngleRad * 180.0f / M_PI;
return thetaDeg;
}
答案 5 :(得分:0)
该功能在RADS中起作用
整圈(360度)有2pi RADS
因此,我相信您正在寻找的answear只是返回值 - 2pi
如果您要求让一个函数同时返回两个值,那么您要求中断语言,函数只能返回单个值。您可以传递两个指针,它可以用来设置值,以便在结束后您的程序可以继续工作后更改可以保留。但这并不是解决这个问题的明智方法。
修改强>
注意到该函数在返回值时实际将Rads转换为Degrees。但同样的原则也会奏效。