AcceleratedCGPoints is a Swift Package Manager library for iOS/tvOS (13.0 and above), watchOS (6.0 and above), and macOS (10.15 and above), under Swift 5.2 and above, providing
- a conformance of
CGPoint
toAdditiveArithmetic
, to support direct arithmetic operations on points:
// Addition and subtraction of points.
extension CGPoint: AdditiveArithmetic {
public static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint
public static func += (lhs: inout CGPoint, rhs: CGPoint)
public static func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint
public static func -= (lhs: inout CGPoint, rhs: CGPoint)
}
- a conformance of
CGPoint
toVectorArithmetic
, to support multiplication of points by scalars:
extension CGPoint: VectorArithmetic {
// Multiplication of a scalar and a point.
public mutating func scale <T: BinaryFloatingPoint> (by s: T)
public var magnitudeSquared: Double
}
- an extension of
CGPoint
, adding some convenience functions to go along with the conformance toVectorArithmetic
:
extension CGPoint {
// Multiplication of a scalar and a point.
// Equivalent to `scale(by:)` above.
public static func * <T: BinaryFloatingPoint> (lhs: T, rhs: CGPoint) -> CGPoint
public static func *= <T: BinaryFloatingPoint> (lhs: inout CGPoint, s: T)
}
- another extension of
CGPoint
, adding support for creating uniformly-distributed pseudo-random points:
extension CGPoint {
// Same range for both components.
public static func random <T> (in range: ClosedRange<T>) -> CGPoint
where T: BinaryFloatingPoint, T.RawSignificand: FixedWidthInteger
// A range for each component.
public static func random <T> (xRange: ClosedRange<T>, yRange: ClosedRange<T>) -> CGPoint
where T: BinaryFloatingPoint, T.RawSignificand: FixedWidthInteger
}
- extensions to
Array
, to support accelerated arithmetic on largeCGPoint
arrays, using Apple'sAccelerate
framework:
// Addition and subtraction of arrays of points.
extension Array: AdditiveArithmetic where Element == CGPoint {
// NOTE: `.zero` is an array of length 1.
public static let zero: [CGPoint]
// Returns an empty array if the arguments are empty arrays.
// NOTE: crashes if the arguments are of different lengths.
public static func + (lhs: [CGPoint], rhs: [CGPoint]) -> [CGPoint]
public static func - (lhs: [CGPoint], rhs: [CGPoint]) -> [CGPoint]
// Does nothing if the arguments are empty arrays.
// NOTE: crashes if the arguments are of different lengths.
public static func += (lhs: inout [CGPoint], rhs: [CGPoint])
public static func -= (lhs: inout [CGPoint], rhs: [CGPoint])
}
extension Array: VectorArithmetic where Element == CGPoint {
// Multiplication of a single scalar and an array of points.
// Does nothing if `self` is an empty array.
public mutating func scale <T: BinaryFloatingPoint> (by s: T)
// Returns 0 if `self` is an empty array.
public var magnitudeSquared: Double
}
extension Array where Element == CGPoint {
// Multiplication of a single scalar and an array of points.
// Equivalent to `scale(by:)` above.
// Does nothing if `rhs` is an empty array.
public static func * <T: BinaryFloatingPoint> (lhs: T, rhs: [CGPoint]) -> [CGPoint]
// Multiplication of a single scalar and an array of points.
// Equivalent to `scale(by:)` above.
// Does nothing if `lhs` is an empty array.
public static func *= <T: BinaryFloatingPoint> (lhs: inout [CGPoint], s: T)
// Performs the operations
//
// p[i].x -> sx * p[i].x + q.x
// p[i].y -> sy * p[i].y + q.y
//
// on each point `p[i]` in the array, given two scalar values `sx` and `sy` (which
// are interpreted as components of a point `s`) and a point `q`, returning the
// result in a new array of points.
//
// Returns an empty array if `points` is an empty array.
//
public static func scale(_ points: [CGPoint], by s: CGPoint, thenAdd q: CGPoint) -> [CGPoint]
// Performs the operations
//
// p[i].x -> sx * p[i].x - q.x
// p[i].y -> sy * p[i].y - q.y
//
// on each point `p[i]` in the array, given two scalar values `sx` and `sy` (which
// are interpreted as components of a point `s`) and a point `q`, returning the
// result in a new array of points.
//
// Returns an empty array if `points` is an empty array.
//
public static func scale(_ points: [CGPoint], by s: CGPoint, thenSubtract q: CGPoint) -> [CGPoint]
// Performs (in place) the operations
//
// p[i].x -> sx * p[i].x + q.x
// p[i].y -> sy * p[i].y + q.y
//
// on each point `p[i]` in the array, given two scalar values `sx` and `sy` (which
// are interpreted as components of a point `s`) and a point `q`.
//
// Does nothing if `self` is an empty array.
//
public mutating func scale(by s: CGPoint, thenAdd q: CGPoint)
// Performs (in place) the operations
//
// p[i].x -> sx * p[i].x - q.x
// p[i].y -> sy * p[i].y - q.y
//
// on each point `p[i]` in the array, given two scalar values `sx` and `sy` (which
// are interpreted as components of a point `s`) and a point `q`.
//
// Does nothing if `self` is an empty array.
//
public mutating func scale(by s: CGPoint, thenSubtract q: CGPoint)
}
- an extension to
Array
to support creating arrays of uniformly-distributed pseudo-random points:
extension Array where Element == CGPoint {
// Returns an array of `count` random points whose coordinates are uniformly-distributed
// pseudo-random numbers in the given range.
public static func random <T> (count: Int, in range: ClosedRange<T>) -> [CGPoint]
where T: BinaryFloatingPoint, T.RawSignificand: FixedWidthInteger
// Returns an array of `count` random points whose `x` and `y` coordinates are uniformly-distributed
// pseudo-random numbers in the ranges `xRange` and `yRange`, respectively.
public static func random <T> (count: Int, xRange: ClosedRange<T>, yRange: ClosedRange<T>) -> [CGPoint]
where T: BinaryFloatingPoint, T.RawSignificand: FixedWidthInteger
}
Typical results when comparing the speeds of these operations (on an old 2012 MacBook Pro) against typical forEach
or map
implementations, on arrays of 1_000_000 points, appear below:
- time per point (standard approach): 1.6e-06 seconds
- time per point (accelerated approach): 1.2e-08 seconds
- accelerated 129 times faster than standard
- time per point (standard approach): 1.2e-06 seconds
- time per point (accelerated approach): 1.6e-08 seconds
- accelerated 74 times faster than standard
- time per point (standard approach): 1.5e-06 seconds
- time per point (accelerated approach): 8.6e-09 seconds
- accelerated 174 times faster than standard
- time per point (standard approach): 2.0e-06 seconds
- time per point (accelerated approach): 2.3e-08 seconds
- accelerated 88 times faster than standard
- time per point (standard approach): 3.3e-07 seconds
- time per point (accelerated approach): 4.4e-09 seconds
- accelerated 75 times faster than standard
- time per point (standard approach): 6.3e-07 seconds
- time per point (accelerated approach): 1.2e-08 seconds
- accelerated 54 times faster than standard
- time per point (standard approach): 3.2e-07 seconds
- time per point (accelerated approach): 2.8e-08 seconds
- accelerated 12 times faster than standard
- time per point (standard approach): 3.6e-07 seconds
- time per point (accelerated approach): 2.8e-08 seconds
- accelerated 13 times faster than standard
AcceleratedCGPoints is provided only as a Swift Package Manager package, because I'm moving away from CocoaPods and Carthage, and can be easily installed directly from Xcode.
AcceleratedCGPoints is available under the MIT license. See the LICENSE file for more info.