将角度归一化到 0 到 360 度的范围?

Normalizing an Angle to the range 0 to 360 degrees?

我们在 SwiftUI 中有 Angle 数据类型。这可以存储从 -infinity 到 +infinity 的度数或弧度值。

我想将角度转换为 0 到 360 之间的值。例如通过使用 wrap around mod:

等方法
func normalize(_ angle: Angle) -> Angle {
  wrapAroundMod(angle.degrees, 360.0)
}

但是,wrapAroundMod 不是内置函数,不支持浮点数。我正在努力想出一个没有一堆分支逻辑的优雅解决方案。

是否有一种优雅的方法来归一化角度?

浮点数的 % 等价于 truncatingRemainder,它可以很好地处理负值!

您可以使用此函数对角度进行归一化:

extension Angle {
  /// Returns an Angle in the range `0° ..< 360°`
  func normalized() -> Angle {
    var degrees = self.degrees.truncatingRemainder(dividingBy: 360)
    if degrees < 0 {
      degrees = degrees + 360
    }
    return Angle(degrees: degrees)
  }
}

如果您想在 -180° ..< 180° 范围内对其进行归一化,也可以考虑添加此扩展名:

extension Angle {
  /// Returns an Angle in the range `-180° ..< 180°` by mapping `180° ..< 360°` to `-180° ..< 0°`
  func normalizedDelta() -> Angle {
    var normalized = normalized()
    if normalized >= Angle(degrees: 180) {
      normalized = normalized - Angle(degrees: 360)
    }
    return normalized
  }
}

这些函数具有以下属性:

Angle(degrees: -1081).normalized().degrees == 359
Angle(degrees: -721).normalized().degrees == 359
Angle(degrees: -361).normalized().degrees == 359
Angle(degrees: -1).normalized().degrees == 359
Angle(degrees: 0).normalized().degrees == 0
Angle(degrees: 359.9).normalized().degrees == 359.9
Angle(degrees: 360).normalized().degrees == 0
Angle(degrees: 361).normalized().degrees == 1
Angle(degrees: 720).normalized().degrees == 0
Angle(degrees: 721).normalized().degrees == 1
Angle(degrees: 1081).normalized().degrees == 1

Angle(degrees: -1081).normalizedDelta().degrees // == -1
Angle(degrees: -721).normalizedDelta().degrees // == -1
Angle(degrees: -361).normalizedDelta().degrees // == -1
Angle(degrees: -1).normalizedDelta().degrees // == -1
Angle(degrees: 0).normalizedDelta().degrees == 0
Angle(degrees: 359.9).normalizedDelta().degrees // == -0.1
Angle(degrees: 360).normalizedDelta().degrees == 0
Angle(degrees: 361).normalizedDelta().degrees == 1
Angle(degrees: 720).normalizedDelta().degrees == 0
Angle(degrees: 721).normalizedDelta().degrees == 1
Angle(degrees: 1081).normalizedDelta().degrees == 1

优雅是见仁见智...

您可以通过使用 truncatingRemainder(dividingBy:):

的两个应用程序在不使用分支的情况下完成此操作
extension Angle {
    /// Returns an Angle in the range `0° ..< 360°`
    func normalized() -> Angle {
        let degrees = (self.degrees.truncatingRemainder(dividingBy: 360) + 360)
                      .truncatingRemainder(dividingBy: 360)

        return Angle(degrees: degrees)
    }
}