是否存在数值稳定的角平分算法?
问题如下:
实际上我是按照以下方式计算的:
我的方法的问题是,当角度几乎为180°(AB几乎平行于BC)时,平分线非常不准确(当然因为中点几乎与B重合)。当前算法是如此不准确,以至于有时得到的平分线几乎与其他2个段之一平行。
是的,没有“强制转换”问题,所有计算都是在单精度浮点中完成的。
答案 0 :(得分:3)
如果将BA旋转+ 90°并且BC旋转-90°,则可以使用角平分线保持不变。
如果情况稳定,即使BA和BC的点积为正,则使用原始公式。
如果为负数,请为BA (x,y) -> (-y,x)
和BC
(x,y) -> (y,-x)
应用轮换,这也会使点积为正。像以前一样继续使用新的载体。
如果你试试这个,你会注意到,在矢量之间的角度-90°现在会发生平分线方向的跳跃。不可能避免这种跳跃,因为连续的平分线在两圈之后才会相同(固定BA和移动C)。
答案 1 :(得分:1)
你可以很简单地找到二等分矢量:
∥BC∥ * BA + ∥BA∥ * BC
但是,对于ABC共线或近似,这也不会在数值上稳定。更好的方法是通过点积找到AB和BC之间的角度。
cos θ = (BA · BC) / (∥BC∥ * ∥BA∥)
即使在共线情况下也会产生正确的角度。
答案 2 :(得分:1)
这不是小事。假设两个边缘向量是a和b:
float2 a = A - B;
float2 b = C - B;
计算点积float dp = dot( a, b )
归一化两个向量:
float2 a_norm = normalize( a );
float2 b_norm = normalize( b );
检查点积的符号位。当dp
为非负数时,
return normalize( a_norm + b_norm );
,您就完成了。
当点积为负时,输入向量之间的角度为钝角。 在这种情况下,应用朴素的公式会增加数值精度。 需要另一种方式。
float2 c = normalize( a_norm - b_norm );
float dir = dot( a, rotate90( b ) );
return ( dir < 0 ) ? rotate90( c ) : rotate270( c );
请注意,-
而不是+
,这才是精度的保证。当a
和b
之间的角度大于90°时,a
和-b
之间的角度小于90°,长度a_norm - b_norm
足够大,可以给出准确的方向。之后,我们只需要按正确的方向旋转90°即可。
P.S。将2D向量旋转90°的倍数是无损操作。 这是rotate90和rotate270函数的伪代码:
float2 rotate90( float2 vec )
{
return float2( vec.y, -vec.x );
}
float2 rotate270( float2 vec )
{
return float2( -vec.y, vec.x );
}
答案 3 :(得分:0)
定义:如果A和B是点,则向量(A,B)是从A点到B点的向量。
让我们说O点是我们坐标系的原点。
A点的坐标是半径 - 矢量(O,A)的相同。
设点M是平分线的中间点,所以你需要:
- 标准化向量(B,A)
- 标准化矢量(B,C)
-vector(B,M)=向量(B,A)+向量(B,C)//从B到中间点的向量
- (可选)您可以将矢量(B,M)与标量相乘以获得更长的矢量/增加B和M之间的距离
-vector(O,M)=向量(O,B)+向量(B,M)//半径向量从O到M
现在中间点M与radius-vector(O,M)具有相同的坐标。
答案 4 :(得分:0)
执行此操作的足够简单的方法有两种格式(但其内容在其他方面相同):
// Move A and C to the origin for easier rotation calculations
Aprime=A-B;
Cprime=C-B;
// The counter-clockwise angle between the positive X axis to A'
angle_a = arctan(Aprime.y, Aprimet.x);
// ditto for C'
angle_c = arctan(Cprime.y, Cprime.x);
// The counter-clockwise angle from A' to C'
angle_ac = angle_c - angle_a;
// The counter-clockwise angle from the positive X axis to M'
angle_m = angle_ac/2 + angle_a;
// Construct M' which, like A' and C', is relative to the origin.
Mprime=(cos(angle_m), sin(angle_m));
// Construct M which is relative to B rather than relative to the origin.
M=Mprime+B
A'=A-B
B'=B
C'=C-B
A'
的角度为angle_a = arctan(A_y, A_x)
。C'
的角度为angle_c = arctan(C_y, C_x)
。A'
到C'
的逆时针角度设为angle_ac = angle_c - angle_a
。M'
的角度为angle_m = angle_ac/2 + angle_a
。M'
构造为M' = (cos(angle_m), sin(angle_m))
。M
构造为M = M' + B
。向量BM
将角度ABC
二等分。
由于存在任意划分,因此此方法没有困难。这是一个图形计算器,以鼓励直观的解决方案:https://www.desmos.com/calculator/xwbno717da