计算两个方向之间的差异

Calculate difference between 2 directions

我如何计算两个角度之间的度差?

这对大多数人来说很简单,因为我只是使用:

var difference = Math.Abs(direction1 - direction2);

然而,当方向 2 为北(0 度)且方向 1 为西北(例如 318 度)时,显然这是出路。

计算北 (0) 边界后差异的正确方法是什么?

编辑:此答案假定方向是 向量 而不是角度。有关角度,请参阅

一般情况下,你取点积,用反余弦得到角度,然后乘以180d / Math.PI转换为度数。这假设方向是标准化的。

var angleInDegrees = Math.ACos(direction1.Dot(direction2)) * 180d / Math.PI;

这总是会产生 [0, 180] 范围内的正角。这适用于 2D 和 3D 矢量。

如果您使用 2D 向量并且想要一个 [0, 360] 中的角度,您可以通过将其中一个向量旋转 90 度来检查最近的旋转是顺时针还是逆时针,并检查它是否与另一个小于或大于 90 度:

var isClockwise = direction1.Dot(new Vector(direction2.Y, -direction2.X)) > 0;

如果是顺时针,则将结果加上 180 度。反之亦然,具体取决于您的坐标系。

这很简单,你需要做的就是添加一个检查,看看方向(航向)是否大于180,如果是,从360中减去差值。像这样:

static int GetHeadingDifference(int heading1, int heading2)
{   
    var difference = Math.Abs(heading1 - heading2);
    if (difference > 180)
        return 360 - difference;
    
    return difference;
}

这将 return 正确的标题。 Here 演示。


但您可能希望将其包装在自定义 object 中以便为您处理所有事情。您可以这样做:

struct Heading
{
    private const float MaxDegrees = 360;
    
    // Use a float for more accurate heading
    public float Degree {get; set;}
    
    public Heading(float heading)
    {
        Degree = heading;
    }
    
    // Override addition to wrap around
    public static Heading operator +(Heading a, Heading b) 
    {
        var val = (a.Degree + b.Degree) % MaxDegrees;
        return new Heading(val);
    }
    
    // Override subtraction to always result in a number <= 180
    public static Heading operator -(Heading a, Heading b)
    {
        var difference = Math.Abs(a.Degree - b.Degree);
        if (difference > 180)
            return new Heading(MaxDegrees - difference);
        
        return new Heading(difference);
    }
    
    // Override equality to check the actual degree value
    public static bool operator ==(Heading a, Heading b)
    {
        return a.Degree == b.Degree;
    }
    
    // Overriding equality requires overriding inequality
    public static bool operator !=(Heading a, Heading b)
    {
        return !(a == b);
    }
    
    // Override Equals to catch explicit a.Equals(b) calls
    public override bool Equals(object other)
    {
        if (other is not Heading)
            return false;
        
        return ((Heading) other).Degree == Degree;
    }
    
    // When overriding equality you must implement GetHashCode
    public override int GetHashCode()
    {
        // Oerflow is ok here, just wrap
        unchecked
        {
            var hash = 17;
            hash = hash * 23 + Degree.GetHashCode();
            return hash;
        }
    }
}

它的演示 here


P.S 如果您只想拥有一个数字,请使用此选项,如果您确实有矢量,请使用 JonasH's answer