Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions Sources/OpenGestures/Util/AdditiveArithmetic.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// AdditiveArithmetic.swift
// OpenGestures
//
// Audited for 9126.1.5
// Status: Complete

public import OpenCoreGraphicsShims

// 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)
}
}
21 changes: 19 additions & 2 deletions Sources/OpenGestures/Util/LocationContaining.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,26 @@ extension Never: LocationContaining {
public var location: CGPoint { fatalError() }
}

// MARK: - IdentifiableLocation [WIP]
// MARK: - IdentifiableLocation

package struct IdentifiableLocation<ID> where ID: Hashable {
package struct IdentifiableLocation<ID>: 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
}
52 changes: 52 additions & 0 deletions Sources/OpenGestures/Util/ThresholdAdjustable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// ThresholdAdjustable.swift
// OpenGestures
//
// Audited for 9126.1.5
// Status: Complete

public import OpenCoreGraphicsShims

// 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
}
85 changes: 85 additions & 0 deletions Sources/OpenGestures/Util/VectorArithmetic.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// VectorArithmetic.swift
// OpenGestures
//
// Audited for 9126.1.5
// Status: Complete

public import OpenCoreGraphicsShims

// 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 }
}
}
63 changes: 63 additions & 0 deletions Tests/OpenGesturesTests/Util/AdditiveArithmeticTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// AdditiveArithmeticTests.swift
// OpenGesturesTests

import OpenCoreGraphicsShims
import OpenGestures
import Testing

@Suite
struct AdditiveArithmeticTests {
@Test
func doubleOperations() {
#expect(Double.zero == 0.0)
#expect(3.0 + 2.0 == 5.0)
#expect(3.0 - 2.0 == 1.0)

var value = 4.0
value += 1.5
#expect(value == 5.5)
value -= 2.5
#expect(value == 3.0)
}

@Test
func pointOperations() {
#expect(CGPoint.zero == CGPoint(x: 0, y: 0))
#expect(
CGPoint(x: 3, y: 4) + CGPoint(x: 1, y: 2)
== CGPoint(x: 4, y: 6)
)
#expect(
CGPoint(x: 3, y: 4) - CGPoint(x: 1, y: 2)
== CGPoint(x: 2, y: 2)
)

var point = CGPoint(x: 2, y: 3)
point += CGPoint(x: 4, y: 5)
#expect(point == CGPoint(x: 6, y: 8))
point -= CGPoint(x: 1, y: 1)
#expect(point == CGPoint(x: 5, y: 7))
}

@Test
func vectorAndSizeOperations() {
#expect(
CGVector(dx: 5, dy: 7) + CGVector(dx: 1, dy: 2)
== CGVector(dx: 6, dy: 9)
)
#expect(
CGVector(dx: 5, dy: 7) - CGVector(dx: 1, dy: 2)
== CGVector(dx: 4, dy: 5)
)

#expect(
CGSize(width: 8, height: 6) + CGSize(width: 2, height: 1)
== CGSize(width: 10, height: 7)
)
#expect(
CGSize(width: 8, height: 6) - CGSize(width: 2, height: 1)
== CGSize(width: 6, height: 5)
)
}
}
2 changes: 1 addition & 1 deletion Tests/OpenGesturesTests/Util/RingBufferTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// RingBufferTests.swift
// OpenGesturesTests

@_spi(Private) import OpenGestures
import OpenGestures
import Testing

// MARK: - RingBufferTests
Expand Down
19 changes: 19 additions & 0 deletions Tests/OpenGesturesTests/Util/ThresholdAdjustableTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ThresholdAdjustableTests.swift
// OpenGesturesTests

import OpenGestures
import Testing

@Suite
struct ThresholdAdjustableTests {
@Test
func consumeThresholdForDouble() {
var value = 10.0

let consumed = value.consume(3.0, from: 8.0)

#expect(consumed == 3.0)
#expect(value == 7.0)
}
}
31 changes: 31 additions & 0 deletions Tests/OpenGesturesTests/Util/VectorArithmeticTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// VectorArithmeticTests.swift
// OpenGesturesTests

import OpenCoreGraphicsShims
import OpenGestures
import Testing

@Suite
struct VectorArithmeticTests {
@Test
func doubleMagnitudeAndScaling() {
#expect(12.0.magnitude == 12.0)
#expect((-12.0).magnitude == 12.0)
#expect(12.0.scaled(by: 0.25) == 3.0)
}

@Test
func pointMagnitudeAndScaling() {
let point = CGPoint(x: 3, y: 4)
#expect(point.magnitude == 5.0)
#expect(point.scaled(by: 2) == CGPoint(x: 6, y: 8))
}

@Test
func vectorMagnitudeAndScaling() {
let vector = CGVector(dx: 5, dy: 12)
#expect(vector.magnitude == 13.0)
#expect(vector.scaled(by: 0.5) == CGVector(dx: 2.5, dy: 6.0))
}
}
Loading