AAngle

Angle 是一个 Swift 包,它提供了一种灵活且可扩展的方式来处理不同类型的角度,包括弧度百分度周/圈角分角秒

特性

角度类型: 支持各种角度类型:Radians(弧度)、Degrees(度)、Gradians(百分度)、Revolutions(周数)、ArcSeconds(角秒)和 ArcMinutes(角分)。

归一化: 内置归一化功能,可将角度保持在特定范围内(例如,Degrees 的 0-360 度)。

算术运算: 支持加法、减法、乘法和除法,并具有一致的归一化行为。

角度转换: 轻松在不同角度类型之间进行转换。

测量支持: 将角度值转换为 Swift 的 Measurement<UnitAngle> 类型,以便与 Foundation framework 的 units(单位)一起使用。

类型安全的单位: 使用 AngleType 枚举来表示单位,确保类型安全并避免基于字符串的错误。

可扩展协议: 采用 Anglable 协议设计,可以轻松添加自定义角度类型。

完整的运算符支持: 支持所有基本算术运算符、比较运算符,包括 ==<<=>>=,以及复合赋值运算符(如 +=-=),确保正确的归一化行为。

三角函数: 一组基本的三角函数:sine(正弦)、cosine(余弦)、tangent(正切)、cotangent(余切)、secant(正割)、cosecant(余割)。三角形计算:oppositeLeg(hypotenuse:)(已知斜边求对边)、adjacentLeg(hypotenuse:)(已知斜边求邻边)、hypotenuse(fromOppositeLeg:)(已知对边求斜边)、hypotenuse(fromAdjacentLeg:)(已知邻边求斜边)、oppositeLeg(fromAdjacentLeg:)(已知邻边求对边)、adjacentLeg(fromOppositeLeg:)(已知对边求邻边)。

安装

您可以使用 Swift Package Manager 安装 AAngle 包。

使用 Xcode

  1. 转到 File(文件) > Add Packages...(添加包...)
  2. 输入存储库 URL:https://github.com/gmcusaro/AAngle.git
  3. 选择 “Up to Next Major Version”(直到下一个主版本)并指定 1.0.0(或您的初始版本)作为起始版本。
  4. 点击 “Add Package”(添加包)。

使用 Packages(包): 将以下依赖项添加到您的 Package.swift 文件中

dependencies: [
    .package(url: "https://github.com/gmcusaro/AAngle.git", from: "1.0.0")
]

然后将该产品添加到任何需要访问该库的目标中

.product(name: "AAngle", package: "AAngle"),

基本用法

import AAngle

// Create angles
let degrees = Degrees(90)
let radians = Radians(Double.pi / 2)
let revolutions = AngleType.revolutions.initAngle(degress)
let grads = Gradians() // Init 0.0 grad
let arcMinutes = ArcMinutes.zero // Init 0.0 arc minutes
let arcSeconds = AngleType.arcSeconds.initAngle(324000.00000000) // Init 324000.00000000 arc seconds

// Angle conversions
let degreesFromRadians = Degrees(radians) // Convert radians to degrees
let degreesFromRevolutions: Degrees = revolutions.convertTo(.degrees) as? Degrees // Convert to any Anglable type

// Using Measurement
let measurement = degrees.toMeasurement()
print(measurement) // Output: 90.0 deg

// Accessing to rawValue, description and debugDescription
print(degrees.rawValue) // 90.0
print(degrees.description) // "90.0"
print(degrees.debugDescription) // "Angle(Degrees): rawValue = 90.0, normalized = 90.0"

运算符

AAngle 类型的核心设计原则是:尽可能表示归一化的角度。归一化通过将角度保持在预定义的范围内来确保一致性并防止歧义(例如,Degrees 的 0-360 度,Radians 的 0-2π 弧度)。但是,某些运算(如与标量相乘和相除)可能会产生超出此标准范围的数学上有效的结果,并且保留这些值可能很重要。因此,Angle 包中的运算符具有特定的归一化行为

+, - 加法和减法: 这些运算符始终产生归一化的结果。结果角度被归一化为左侧操作数类型的标准范围。这确保了加减角度总是得到预期范围内的值。结果的类型与左侧操作数的类型相同。

let degrees = Degrees(350)
let radians = Radians(Double.pi / 2) // Equivalent to 90 degrees
let sum = degrees + radians  // Result: Degrees(80)  (350 + 90 = 440, normalized to 80)
let difference = radians - degrees // Result: Radians(-4.537856055185257)

+=, -= 加法和减法赋值: 这些运算符原地修改角度,并且始终归一化结果。它们等效于执行相应的算术运算,然后将归一化值分配回原始变量。

var myAngle = Degrees(350)
myAngle += 90  // myAngle is now 80 (normalized)
myAngle -= Revolutions(0.25) // myAngle is now 340 (normalized)

* 乘法: 与标量(Double、Int 等)的乘法进行归一化。这是因为将角度乘以一个值可能会合法地产生超出标准范围的角度(例如,2 * 180 度 = 360 度,3 * 180 度 = 540 度)。保留这些值通常在数学上是必要的。

let degrees = Degrees(180)
let doubled = degrees * 2  // Result: Degrees(360) (not normalized)
let tripled = degrees * 3  // Result: Degrees(540) (not normalized)

*= 乘法赋值: 乘法赋值运算符归一化的。重复乘法(通常这样做)将导致值保持在标准范围内。

var degrees = Degrees(180)
degrees *= 2  // Result: Degrees(0) (normalized)

/ 除法: 与标量相除进行归一化,原因与乘法相同。除一个角度可以产生一个更小或更大的角度,该角度可能落入也可能不落入标准范围内,并且通常需要保留未归一化的值。

let radians = Radians(Double.pi)
let halved = radians / 2  // Result: Radians(1.570796326794966) (not normalized, equal to pi/2)

/= 除法赋值: 除法赋值运算符归一化的。重复除法(通常这样做)将导致值保持在标准范围内。

var degrees = Degrees(10)
degrees /= 2  // Result: Degrees(90) (normalized)

==, <, <=, >, >= 比较运算符: 比较运算符在不同类型的 Anglable 实例之间正确工作。在比较之前,右侧操作数被转换为左侧操作数的类型。这确保了无论原始单位如何,都能进行一致且准确的比较。比较使用容差值来考虑潜在的浮点精度误差。

let degrees = Degrees(90)
let radians = Radians(Double.pi / 2)
print(degrees == 180)     // true
print(degrees == radians) // true (comparison after converting radians to degrees)
print(degrees < Int(89))  // false
print(degrees < radians)  // false

归一化

Anglable 协议提供了用于归一化角度值的方法,确保它们落在定义的范围内。归一化对于一致性以及防止角度表示中的歧义至关重要。每个符合的类型(例如,DegreesRadians)都定义了自己的 normalizationValue,它是 Anglable 协议的 static 属性。

normalize()原地将角度归一化为符合类型 normalizationValue 定义的标准范围。 例如,对于 Degrees,这将是范围 0 到 360(不包括 360)。 normalize() 方法会修改现有角度。

var myAngle = Degrees(450)
myAngle.normalize() // myAngle is now 90

normalized() -> Self:返回一个包含归一化值的 Anglable 实例。 原始实例被修改。 normalized() 方法会创建一个新的、归一化的角度实例。

let myAngle = Degrees(450)
let normalizedAngle = myAngle.normalized() // normalizedAngle is 90, myAngle is still 450

normalize(by value: Double)*: 使用自定义归一化值原地归一化角度。 如果您需要默认值以外的范围,这将非常有用。 normalize() 方法会修改现有角度。

var myAngle = Radians(3 * Double.pi) // 3π
myAngle.normalize(by: Double.pi) // myAngle is now π (normalized to the range 0 to π)

normalized(by value: Double) -> Self:返回一个Anglable 实例,该实例使用提供的自定义归一化值进行归一化。 原始实例不会被修改。 normalized() 方法会创建一个新的、归一化的角度实例。

let myAngle = Radians(3 * Double.pi) // 3π
let normalizedAngle = myAngle.normalized(by: Double.pi) // normalizedAngle is π, myAngle is still 3π

关键设计原理

默认归一化(在适当的情况下): 加法、减法和赋值运算默认进行归一化,以保持在标准范围内表示角度的核心原则。 这避免了常见错误并确保了一致性。

保留量级(必要时): 乘法和除法(不带赋值)保留结果的量级,即使它落在归一化范围之外也是如此。 这对于许多数学运算至关重要,在这些运算中,“未包装”的角度很重要。

类型一致性: 二元运算 (+, -) 返回与左侧操作数相同类型的值。 这使得行为可预测并有助于防止意外的类型更改。

跨类型比较: 比较运算符通过执行内部转换来正确处理不同的 Anglable 类型。

容差: 使用容差执行比较运算,以防止因浮点精度限制而导致的错误。

三角函数

AAngleRadians 类型提供了一组三角函数。

基本三角函数

import AAngle

let radians = Radians(Degrees(45))

// Basic trigonometric functions
print(radians.sine)      // 0.7071067811865475
print(radians.cosine)    // 0.7071067811865476
print(radians.tangent)   // 0.9999999999999999
print(radians.cotangent) // Optional(1.0000000000000002)
print(radians.secant)    // Optional(1.414213562373095)
print(radians.cosecant)  // Optional(1.414213562373095)

三角形计算

import AAngle

let radians = Radians(Double.pi / 4) // 45 degrees

// Triangle calculations
let hypotenuse = 10.0
print(radians.oppositeLeg(hypotenuse: hypotenuse)) // 7.0710678118654755
print(radians.adjacentLeg(hypotenuse: hypotenuse)) // 7.071067811865476
print(radians.hypotenuse(fromOppositeLeg: 5.0))    // 7.0710678118654755
print(radians.hypotenuse(fromAdjacentLeg: 5.0))    // 7.071067811865476
print(radians.oppositeLeg(fromAdjacentLeg: 5.0))   // 5.0
print(radians.adjacentLeg(fromOppositeLeg: 5.0))   // 5.0

许可

此软件包根据 Apache License 获得许可。 有关更多信息,请参阅 LICENSE