确定角度是否接近零+/- 2?



在我的应用程序中,我试图提供一个指示,用户的标题是在一个合理的范围内从零。如果超过360度,角度将重置为零。这就是我要做的:

let angle1 = Angle(degree: 359)
let angle2 = Angle(degree: 2)
angle1.degrees > 358 || angle1.degrees < 2
angle2.degrees > 358 || angle2.degrees < 2

是否有一个内置的方法或更好的方法来测试这个?此外,是否存在CoreLocation标题大于360或负值的情况?

您正在做的工作,但是有一种方法可以在代码中做到这一点,我认为这是逻辑的更直接/更准确的代码表示。这将检查值的范围是否包含您的值。在本例中,我们将检查0到2或358到360的范围是否包含相关的当前角度。

作为旁注,我喜欢将这些类型的计算作为计算属性包装到我的模型中。它可能看起来像…

struct Angle {
var degrees: Double
var isCloseToZero: Bool {
let upperRange = 358.0...360.0
let lowerRange = 0.0...2.0

return lowerRange.contains(degrees) || upperRange.contains(degrees)
}
}

可以这样访问

let angle = Angle(degrees: 359)
let isClsoeToZero = angle.isCloseToZero

您可以尝试下面的代码来实现您所要求的。

var angle = Angle(degrees: 359.0) // Angle(degrees: 2.0)
// between 358 and 360 inclusive or between 0 and +- 2 inclusive
if angle.degrees >= 358 && angle.degrees <= 360 ||
angle.degrees >= 0 && angle.degrees <= 2 ||
angle.degrees >= -2 && angle.degrees <= 0 {
print("Yes, user is heading within a reasonable range from zero")
}

关于CoreLocationcourse(标题),根据文件,https://developer.apple.com/documentation/corelocation/cllocation/1423832-course

"…负值表示课程信息无效…">

是否存在CoreLocation标题较大的情况

我不确定,但是很多与角度相关的数学可能会使你的值超出0..<360范围。要解决这个问题,您可以添加make:

extension Angle {
// Return a normalized copy of this angle that's guaranteed to be within +0 ..< +360
var normalized: Angle {
let potentiallyNegativeAngle = degrees.truncatingRemainder(dividingBy: 360.0)
let positiveInRangeAngle = (potentiallyNegativeAngle + 360).truncatingRemainder(dividingBy: 360.0)
return Angle(degrees: positiveInRangeAngle) }
}

是否有一个内置的方法或更好的方法来测试这个?

不,但是你自己写会很有趣。我是这样做的:

// It astounds me that these basic operators aren't already built-in
extension Angle {
static func + (minuend: Angle, subtrahend: Angle) -> Angle {
Angle(radians: minuend.radians + subtrahend.radians)
}

static func - (minuend: Angle, subtrahend: Angle) -> Angle {
Angle(radians: minuend.radians - subtrahend.radians)
}
}
extension Angle {
// There's probably some clever way to do this without branching,
// and purely with modular arithmetic, but I couldn't figure it out
func isWithin(_ delta: Angle, of target: Angle) -> Bool {
return self.normalized > (target - delta).normalized ||
self.normalized < (target + delta).normalized
}

func isCloseToZero(delta: Angle = Angle(degrees: 2.0)) -> Bool {
isWithin(delta, of: Angle(degrees: 0))
}
}

下面是一些测试用例:

print(" -340.0: ", Angle(degrees: -340.0).isCloseToZero()) // False
print(" -358.1: ", Angle(degrees: -358.1).isCloseToZero())
print("   -5.0: ", Angle(degrees:   -5.0).isCloseToZero()) // False
print("   -1.9: ", Angle(degrees:   -1.9).isCloseToZero())
print("    0.0: ", Angle(degrees:    0.0).isCloseToZero())
print("   +1.9: ", Angle(degrees:   +1.9).isCloseToZero())
print("   +5.0: ", Angle(degrees:   +5.0).isCloseToZero()) // False
print(" +358.1: ", Angle(degrees: +358.1).isCloseToZero())
print(" +360.0: ", Angle(degrees: +360.0).isCloseToZero())
print(" +365.0: ", Angle(degrees: +365.0).isCloseToZero()) // False

这里有一个更花哨的变体,它使用一些聪明的模运算来实现完全无分支:

extension Angle {
// Returns the distance between `self` and `target`, in the range `-180..<180` degrees
func distance(to target: Angle) -> Angle {
let rawDistance = (self - target).radians
let normalizedDistance = .pi - abs(abs(rawDistance) - .pi)
return Angle(radians: normalizedDistance)
}

func isWithin(_ delta: Angle, of target: Angle) -> Bool {
let normalizedDelta = delta.normalized
precondition(normalizedDelta.radians <= .pi,
"""
`isWithin(_:of:)` always find the shortest distance between the two angles,
so the delta has to be congruent to an angle between 0 and 180 degrees!
It was (delta), which normalized to: (normalizedDelta)
"""
)
return abs(self.distance(to: target).radians) <= normalizedDelta.radians
}

func isCloseToZero(delta: Angle = Angle(degrees: 2.0)) -> Bool {
isWithin(delta, of: Angle(degrees: 0))
}
}

最新更新