Skip to content

Commit

Permalink
Merge pull request #177 from OpenSwiftUIProject/feature/animatable_data
Browse files Browse the repository at this point in the history
Update Animatable Data
  • Loading branch information
Kyle-Ye authored Dec 19, 2024
2 parents b706905 + 9f4daf1 commit 03b8a6a
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 49 deletions.
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

0 comments on commit 03b8a6a

Please sign in to comment.