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
68 changes: 68 additions & 0 deletions Sources/OpenSwiftUICore/Animation/AnimatableArray.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// AnimatableArray.swift
// OpenSwiftUICore
//
// Audited for iOS 18.0
// Status: Complete

package struct AnimatableArray<Element>: VectorArithmetic where Element: VectorArithmetic {
package var elements: [Element]

package init(_ elements: [Element]) {
self.elements = elements
}

package static var zero: AnimatableArray<Element> { .init([]) }

package static func += (lhs: inout AnimatableArray<Element>, rhs: AnimatableArray<Element>) {
let count = Swift.min(lhs.elements.count, rhs.elements.count)
for i in 0..<count {
lhs.elements[i] += rhs.elements[i]
}
}

package static func -= (lhs: inout AnimatableArray<Element>, rhs: AnimatableArray<Element>) {
let count = Swift.min(lhs.elements.count, rhs.elements.count)
for i in 0..<count {
lhs.elements[i] -= rhs.elements[i]
}
}

@_transparent
package static func + (lhs: AnimatableArray<Element>, rhs: AnimatableArray<Element>) -> AnimatableArray<Element> {
var result = lhs
result += rhs
return result
}

@_transparent
package static func - (lhs: AnimatableArray<Element>, rhs: AnimatableArray<Element>) -> AnimatableArray<Element> {
var result = lhs
result -= rhs
return result
}

package mutating func scale(by rhs: Double) {
for i in elements.indices {
elements[i].scale(by: rhs)
}
}

package var magnitudeSquared: Double {
elements.reduce(0) { partialResult, element in
partialResult + element.magnitudeSquared
}
}
}

extension Array where Element: Animatable {
package var animatableData: AnimatableArray<Element.AnimatableData> {
get { AnimatableArray(map(\.animatableData)) }
set {
let count = Swift.min(count, newValue.elements.count)
for i in 0..<count {
self[i].animatableData = newValue.elements[i]
}
}
}
}
25 changes: 13 additions & 12 deletions Sources/OpenSwiftUICore/Animation/AnimatablePair.swift
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
//
// AnimatablePair.swift
// OpenSwiftUI
// OpenSwiftUICore
//
// Audited for iOS 15.5
// Audited for iOS 18.0
// Status: Complete

/// A pair of animatable values, which is itself animatable.
@frozen
public struct AnimatablePair<First, Second>: VectorArithmetic where First: VectorArithmetic, Second: VectorArithmetic {
/// The first value.
public var first: First

/// The second value.
public var second: Second


/// Creates an animated pair with the provided values.
@inlinable
public init(_ first: First, _ second: Second) {
self.first = first
self.second = second
}

@inlinable
subscript() -> (First, Second) {
package subscript() -> (First, Second) {
get { (first, second) }
set { (first, second) = newValue }
}

@_transparent
public static var zero: AnimatablePair<First, Second> {
@_transparent
get { .init(First.zero, Second.zero) }
.init(First.zero, Second.zero)
}

@_transparent
Expand Down Expand Up @@ -57,13 +60,11 @@ public struct AnimatablePair<First, Second>: VectorArithmetic where First: Vecto
second.scale(by: rhs)
}

/// The dot-product of this animated pair with itself.
@_transparent
public var magnitudeSquared: Double {
@_transparent
get { first.magnitudeSquared + second.magnitudeSquared }
}

public static func == (a: AnimatablePair<First, Second>, b: AnimatablePair<First, Second>) -> Bool {
a.first == b.first && a.second == b.second
first.magnitudeSquared + second.magnitudeSquared
}
}

extension AnimatablePair: Sendable where First: Sendable, Second: Sendable {}
142 changes: 142 additions & 0 deletions Sources/OpenSwiftUICore/Animation/AnyAnimatableData.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//
// AnyAnimatableData.swift
// OpenSwiftUICore
//
// Audited for iOS 18.0
// Status: Complete
// ID: 7ABB4C511D8E2C0F1768F58E8C14509E (SwiftUICore)

@frozen
public struct _AnyAnimatableData: VectorArithmetic {
package var vtable: _AnyAnimatableDataVTable.Type
package var value: Any

@inline(__always)
init(vtable: _AnyAnimatableDataVTable.Type, value: Any) {
self.vtable = vtable
self.value = value
}

package init<T>(_ container: T) where T: Animatable {
vtable = VTable<T>.self
value = container.animatableData
}

package func update<T>(_ container: inout T) where T: Animatable {
guard vtable == VTable<T>.self else { return }
container.animatableData = value as! T.AnimatableData
}

public static var zero: _AnyAnimatableData {
_AnyAnimatableData(vtable: ZeroVTable.self, value: ZeroVTable.zero)
}

public static func == (lhs: _AnyAnimatableData, rhs: _AnyAnimatableData) -> Bool {
if lhs.vtable == rhs.vtable {
lhs.vtable.isEqual(lhs.value, rhs.value)
} else {
false
}
}

public static func += (lhs: inout _AnyAnimatableData, rhs: _AnyAnimatableData) {
if lhs.vtable == rhs.vtable {
lhs.vtable.add(&lhs.value, rhs.value)
} else if lhs.vtable == ZeroVTable.self {
lhs = rhs
}
}

public static func -= (lhs: inout _AnyAnimatableData, rhs: _AnyAnimatableData) {
if lhs.vtable == rhs.vtable {
lhs.vtable.subtract(&lhs.value, rhs.value)
} else if lhs.vtable == ZeroVTable.self {
lhs = rhs
lhs.vtable.negate(&lhs.value)
}
}

@_transparent
public static func + (lhs: _AnyAnimatableData, rhs: _AnyAnimatableData) -> _AnyAnimatableData {
var ret = lhs
ret += rhs
return ret
}

@_transparent
public static func - (lhs: _AnyAnimatableData, rhs: _AnyAnimatableData) -> _AnyAnimatableData {
var ret = lhs
ret -= rhs
return ret
}

public mutating func scale(by rhs: Double) {
vtable.scale(&value, by: rhs)
}

public var magnitudeSquared: Double {
vtable.magnitudeSquared(value)
}
}

@available(*, unavailable)
extension _AnyAnimatableData: Sendable {}

@usableFromInline
package class _AnyAnimatableDataVTable {
package class var zero: Any {
preconditionFailure("")
}

package class func isEqual(_ lhs: Any, _ rhs: Any) -> Bool { false }
package class func add(_ lhs: inout Any, _ rhs: Any) {}
package class func subtract(_ lhs: inout Any, _ rhs: Any) {}
package class func negate(_ lhs: inout Any) {}
package class func scale(_ lhs: inout Any, by rhs: Double) {}
package class func magnitudeSquared(_ lhs: Any) -> Double { .zero }
}

@available(*, unavailable)
extension _AnyAnimatableDataVTable: Sendable {}

private final class VTable<Value>: _AnyAnimatableDataVTable where Value: Animatable {
override class var zero: Any {
Value.AnimatableData.zero
}

override class func isEqual(_ lhs: Any, _ rhs: Any) -> Bool {
lhs as! Value.AnimatableData == rhs as! Value.AnimatableData
}

override class func add(_ lhs: inout Any, _ rhs: Any) {
var value = lhs as! Value.AnimatableData
value += rhs as! Value.AnimatableData
lhs = value
}

override class func subtract(_ lhs: inout Any, _ rhs: Any) {
var value = lhs as! Value.AnimatableData
value -= rhs as! Value.AnimatableData
lhs = value
}

override class func negate(_ lhs: inout Any) {
var value = lhs as! Value.AnimatableData
value = .zero - value
lhs = value
}

override class func scale(_ lhs: inout Any, by rhs: Double) {
var value = lhs as! Value.AnimatableData
value.scale(by: rhs)
lhs = value
}

override class func magnitudeSquared(_ lhs: Any) -> Double {
(lhs as! Value.AnimatableData).magnitudeSquared
}
}

private final class ZeroVTable: _AnyAnimatableDataVTable {
override class var zero: Any { () }
}
18 changes: 16 additions & 2 deletions Sources/OpenSwiftUICore/Animation/EmptyAnimatableData.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
//
// EmptyAnimatableData.swift
// OpenSwiftUI
// OpenSwiftUICore
//
// Audited for iOS 15.5
// Audited for iOS 18.0
// Status: Complete

/// An empty type for animatable data.
///
/// This type is suitable for use as the `animatableData` property of
/// types that do not have any animatable properties.
@frozen
public struct EmptyAnimatableData: VectorArithmetic {
@inlinable
Expand Down Expand Up @@ -33,3 +37,13 @@ public struct EmptyAnimatableData: VectorArithmetic {

public static func == (_: EmptyAnimatableData, _: EmptyAnimatableData) -> Bool { true }
}

public import Foundation

extension Double: Animatable {
public typealias AnimatableData = Swift.Double
}

extension CGFloat: Animatable {
public typealias AnimatableData = CGFloat
}
Loading
Loading