diff --git a/GF/DeviceSwiftShims/Util/AdditiveArithmetic.swift b/GF/DeviceSwiftShims/Util/AdditiveArithmetic.swift new file mode 100644 index 0000000..b288ee3 --- /dev/null +++ b/GF/DeviceSwiftShims/Util/AdditiveArithmetic.swift @@ -0,0 +1,69 @@ +// +// AdditiveArithmetic.swift +// Gestures +// +// Audited for 9126.1.5 +// Status: Complete + +import CoreGraphics + +// MARK: - _AdditiveArithmetic + +package protocol _AdditiveArithmetic: Equatable { + static var zero: Self { get } + + static func + (lhs: Self, rhs: Self) -> Self + static func += (lhs: inout Self, rhs: Self) + static func - (lhs: Self, rhs: Self) -> Self + static func -= (lhs: inout Self, rhs: Self) +} + +extension _AdditiveArithmetic { + package static func += (lhs: inout Self, rhs: Self) { + lhs = lhs + rhs + } + + package static func -= (lhs: inout Self, rhs: Self) { + lhs = lhs - rhs + } +} + +// MARK: - Double + _AdditiveArithmetic + +extension Double: _AdditiveArithmetic {} + +// MARK: - CGPoint + _AdditiveArithmetic + +extension CGPoint: _AdditiveArithmetic { + package static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint { + CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) + } + + package static func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint { + CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) + } +} + +// MARK: - CGVector + _AdditiveArithmetic + +extension CGVector: _AdditiveArithmetic { + package static func + (lhs: CGVector, rhs: CGVector) -> CGVector { + CGVector(dx: lhs.dx + rhs.dx, dy: lhs.dy + rhs.dy) + } + + package static func - (lhs: CGVector, rhs: CGVector) -> CGVector { + CGVector(dx: lhs.dx - rhs.dx, dy: lhs.dy - rhs.dy) + } +} + +// MARK: - CGSize + _AdditiveArithmetic + +extension CGSize: _AdditiveArithmetic { + package static func + (lhs: CGSize, rhs: CGSize) -> CGSize { + CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height) + } + + package static func - (lhs: CGSize, rhs: CGSize) -> CGSize { + CGSize(width: lhs.width - rhs.width, height: lhs.height - rhs.height) + } +} diff --git a/GF/DeviceSwiftShims/Util/LocationContaining.swift b/GF/DeviceSwiftShims/Util/LocationContaining.swift index 0ab4746..2b53ab8 100644 --- a/GF/DeviceSwiftShims/Util/LocationContaining.swift +++ b/GF/DeviceSwiftShims/Util/LocationContaining.swift @@ -29,9 +29,26 @@ extension Never: LocationContaining { } } -// MARK: - IdentifiableLocation [WIP] +// MARK: - IdentifiableLocation -package struct IdentifiableLocation where ID: Hashable { +package struct IdentifiableLocation: Identifiable where ID: Hashable { package var id: ID package var location: CGPoint } + +extension IdentifiableLocation: LocationContaining {} + +extension IdentifiableLocation: NestedCustomStringConvertible {} + +extension IdentifiableLocation: VectorContaining { + package typealias VectorType = CGPoint + + package var vector: CGPoint { + get { location } + set { location = newValue } + } +} + +extension IdentifiableLocation: ThresholdAdjustable { + package typealias Threshold = Double +} diff --git a/GF/DeviceSwiftShims/Util/ThresholdAdjustable.swift b/GF/DeviceSwiftShims/Util/ThresholdAdjustable.swift new file mode 100644 index 0000000..e6f4100 --- /dev/null +++ b/GF/DeviceSwiftShims/Util/ThresholdAdjustable.swift @@ -0,0 +1,52 @@ +// +// ThresholdAdjustable.swift +// Gestures +// +// Audited for 9126.1.5 +// Status: Complete + +import CoreGraphics + +// MARK: - ThresholdAdjustable + +/// A value whose backing vector can consume a threshold-sized movement. +package protocol ThresholdAdjustable: VectorContaining { + /// The scalar threshold type used to gate movement. + associatedtype Threshold + + /// Consumes up to `threshold` units from `movement`. + /// + /// When `movement` reaches the threshold, this subtracts the threshold-sized + /// portion of `movement` from `vector` and returns that consumed movement. + /// Returns `nil` without mutation when `threshold` is not positive or + /// `movement` is below threshold. + mutating func consume(_ threshold: Threshold, from movement: VectorType) -> VectorType? +} + +extension ThresholdAdjustable where Threshold == Double { + package mutating func consume(_ threshold: Double, from movement: VectorType) -> VectorType? { + guard threshold > 0 else { return nil } + + let movementMagnitude = movement.magnitude + guard movementMagnitude >= threshold else { return nil } + + let scale = threshold / movementMagnitude + let consumedMovement = movement.scaled(by: scale) + vector -= consumedMovement + return consumedMovement + } +} + +// MARK: - ThresholdAdjustable Conformance + +extension Double: ThresholdAdjustable { + package typealias Threshold = Double +} + +extension CGPoint: ThresholdAdjustable { + package typealias Threshold = Double +} + +extension CGVector: ThresholdAdjustable { + package typealias Threshold = Double +} diff --git a/GF/DeviceSwiftShims/Util/VectorArithmetic.swift b/GF/DeviceSwiftShims/Util/VectorArithmetic.swift new file mode 100644 index 0000000..5225af0 --- /dev/null +++ b/GF/DeviceSwiftShims/Util/VectorArithmetic.swift @@ -0,0 +1,85 @@ +// +// VectorArithmetic.swift +// Gestures +// +// Audited for 9126.1.5 +// Status: Complete + +import CoreGraphics + +// MARK: - VectorArithmetic + +package protocol VectorArithmetic: _AdditiveArithmetic { + var magnitude: Double { get } + + func scaled(by rhs: Double) -> Self +} + +// MARK: - VectorArithmetic Conformance + +extension Double: VectorArithmetic { + package var magnitude: Double { + abs(self) + } + + package func scaled(by rhs: Double) -> Double { + self * rhs + } +} + +extension CGPoint: VectorArithmetic { + package var magnitude: Double { + hypot(abs(x), abs(y)) + } + + package func scaled(by rhs: Double) -> CGPoint { + CGPoint(x: x * rhs, y: y * rhs) + } +} + +extension CGVector: VectorArithmetic { + package var magnitude: Double { + hypot(abs(dx), abs(dy)) + } + + package func scaled(by rhs: Double) -> CGVector { + CGVector(dx: dx * rhs, dy: dy * rhs) + } +} + +// MARK: - VectorContaining + +package protocol VectorContaining { + associatedtype VectorType: VectorArithmetic + + var vector: VectorType { get set } +} + +// MARK: - VectorContaining Conformance + +extension Double: VectorContaining { + package typealias VectorType = Double + + package var vector: Double { + get { self } + set { self = newValue } + } +} + +extension CGPoint: VectorContaining { + package typealias VectorType = CGPoint + + package var vector: CGPoint { + get { self } + set { self = newValue } + } +} + +extension CGVector: VectorContaining { + package typealias VectorType = CGVector + + package var vector: CGVector { + get { self } + set { self = newValue } + } +}