如何在没有数学函数(arctan)的点和组织(0,0)之间计算快速航向角度(最接近八个提供值之一)? 我已经将xy坐标系分为8段(0,45,90,135,180,225,270,315),我需要找到点和组织之间的角度(不准确只是上面八个最接近的值)没有重数学函数。 我可以找到像
std::pair<float,float> point;
float angle =arctan(point.second/point.first);
int index =static_cast<int>( (angle+22.5)/45);
然后按索引从数组中读取。
(我增加了22.5度,因为[-22.5, 22.5)=>0, [22.5,67.5)=>45,[67.5,112.5)=>90...
)
是否有更快的方法,任何想法(执行时间非常重要)?
答案 0 :(得分:9)
给定(a,b)
点a<b
,a>=0
和b>=0
,是否接近45度或0度?
嗯,tan(theta) = opposite/adjacent
,在0度到45度的范围内,棕褐色是单调递增的。
tan(22.5 degrees) =~ 207107/500000
。因此,如果a/b > 207107/500000
,最近的角度是45度。如果a/b < 207107/500000
,则最近的角度为0
度。我们甚至可以通过说500000*a < 207107*b
而没有浮点数学来做到这一点。
对于任意点(a,b)
,我们可以通过a和b上的符号找出它所在的象限。我们可以将问题旋转(通过否定)到正 - 正象限,然后在结果角度上反转该旋转(这是一个非常简单的地图)。
对于正正象限中的任意(a,b)
,如果a>b
只是反转a和b,则按上述方式求解,“接近0度”对应于“接近90度”。
上面的一些内容过于冗余,但您应该能够将这些分支转换为整数运算并以数组访问结束。
现在,请注意,在某些系统上,trig函数内在函数可以非常快,比一堆无分支整数运算和数组查找快得多。您的第一步应该是看看是否可以用arctan
更快地替换arctan
。
bool neg_a = a<0;
bool neg_b = b<0;
a *= (1-2*neg_a);
b *= (1-2*neg_b);
bool near_0 = 500000*a<207107*b; // a/b < 207107/500000
bool near_90 = 207107*a>500000*b; // a/b > 500000/207107
bool near_45 = !near_0 & !near_90;
// 3 CW 2 1
// -+ | ++
// 2-4 | 0-2 CCW
//4 ----+---- 0
//CCW-- | +- CW
// 4-6 | 6-8
// 5 6 7
// 0 1 or 2
int index = near_45 + 2*near_90;
// negating a or b reverses angle
index *= (1-2*neg_a);
index *= (1-2*neg_b);
// base is 4 if a is negative:
index += 4*(neg_a);
// base is 8 if b is negative, and a is not negative:
index += 8*(neg_b&!neg_a);
index &= 7;
return index;
这非常荒谬,但是没有分支。也没有调试。
答案 1 :(得分:1)
您可以通过比较其光线的渐变与八分圆边界的渐变,在22.5,67.5度等来计算该点所处的八分圆。所以:
static const float borders[] = { tan(-3 * PI / 8), tan(-PI / 8), tan(PI / 8), tan(3 * PI / 8) };
static const int angles[] = { 270, 315, 0, 45, 90, 135, 180, 225 };
float gradient = y / x;
int i = std::distance(borders, std::lower_bound(std::begin(borders), std::end(borders), gradient)) + (x < 0 ? 4 : 0);
int angle = angles[i];
答案 2 :(得分:1)
该解决方案基于最大化cos(theta-theta_i),其中theta_i为0,45,90,...,315度。我使用角度差同一性cos(ab)= cos(a)cos(b)-sin(a)sin(b)并使用x = r * cos(a)和y = r * sin(a)来简化不同的表达式,因此不必使用实际的trig函数:
int getIndex_simple(float x, float y){
float rCosThetaMinusThetaI[8];
rCosThetaMinusThetaI[0]=x;
rCosThetaMinusThetaI[1]=(x+y)*M_SQRT1_2;
rCosThetaMinusThetaI[2]=y;
rCosThetaMinusThetaI[3]=(y-x)*M_SQRT1_2;
rCosThetaMinusThetaI[4]=-x;
rCosThetaMinusThetaI[5]=(-x-y)*M_SQRT1_2;
rCosThetaMinusThetaI[6]=-y;
rCosThetaMinusThetaI[7]=(x-y)*M_SQRT1_2;
int best_i=0;
float best_rCosThetaMinusThetaI=rCosThetaMinusThetaI[0];
for(int i=1; i<8; i++)
if( rCosThetaMinusThetaI[i]>best_rCosThetaMinusThetaI ){
best_i=i;
best_rCosThetaMinusThetaI=rCosThetaMinusThetaI[i];
}
return best_i;
}
您可以采取一些简单的措施来加快速度。例如,对于i&gt; = 4,rCosThetaMinusThetaI[i] == -rCosThetaMinusThetaI[i-4]
,因此您只需要四个变量。显然你不必使用数组,我这样做是为了让它易于阅读。此外,由于rCosThetaMinusThetaI[0]==x
和rCosThetaMinusThetaI[2]==y
,您只需要两个变量。因此,我们可以将上述内容重写为一系列八个if
语句:
int getIndex_optimized(float x, float y){
float cosTheta1 = (x+y)*M_SQRT1_2;
float cosTheta3 = (y-x)*M_SQRT1_2;
int best_i=0;
float best_cosTheta=x;
if( best_cosTheta < cosTheta1 ){ best_i=1; best_cosTheta=cosTheta1; }
if( best_cosTheta < y ){ best_i=2; best_cosTheta=y; }
if( best_cosTheta < cosTheta3 ){ best_i=3; best_cosTheta=cosTheta3; }
if( best_cosTheta < -x ){ best_i=4; best_cosTheta=-x; }
if( best_cosTheta < -cosTheta1){ best_i=5; best_cosTheta=-cosTheta1; }
if( best_cosTheta < -y ){ best_i=6; best_cosTheta=-y; }
if( best_cosTheta < -cosTheta3){ best_i=7; best_cosTheta=-cosTheta3; }
return best_i;
}
这两个功能是C99代码;你可以使用-std = c99编译它们。你需要包含math.h来获得常量M_SQRT1_2 = 0.7071 ...
答案 3 :(得分:0)
在您使用的8条光线中,当该点距离x轴比y轴更近(或相同距离)时,使用一组四条光线。否则使用其他四个。
在这4个中,当x为正(或零)时使用右两个光线。否则另外两个使用
在这些中,当y为正时使用上部光线。否则使用较低的一个
因此,使用这些简单的测试在查找表中形成索引。
float ClosestAngle(std::pair<float,float> point) {
float angle[8] = { 247.5 , 112.5f , 292.5 , 67.5f , 202.5 , 157.5 , 337.5 , 22.5f };
int closerToXAxisThanYAxis = (abs(point.first) <= abs(point.second)) ? 4 : 0;
int xIsPositive = (point.first >= 0) ? 2 : 0;
int yIsPositive = (point.second >= 0) ? 1 : 0;
return angle[closerToXAxisThanYAxis + xIsPositive + yIsPositive];
}