Skip to content

Commit

Permalink
Use generic methods with different arities
Browse files Browse the repository at this point in the history
  • Loading branch information
calda committed Aug 3, 2022
1 parent ba14b04 commit 96980ab
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,9 @@ extension Ellipse {

/// Creates a single array of animatable keyframes from the separate arrays of keyframes in this Ellipse
func combinedKeyframes(context: LayerAnimationContext) throws-> KeyframeGroup<Ellipse.Keyframe> {
let combinedKeyframes = Keyframes.combinedIfPossible(size, position) { untypedValues in
Keyframe(
size: untypedValues[0] as! Vector3D,
position: untypedValues[1] as! Vector3D)
}
let combinedKeyframes = Keyframes.combinedIfPossible(
size, position,
makeCombinedResult: Ellipse.Keyframe.init)

if let combinedKeyframes = combinedKeyframes {
return combinedKeyframes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,9 @@ extension Rectangle {

/// Creates a single array of animatable keyframes from the separate arrays of keyframes in this Rectangle
func combinedKeyframes(context: LayerAnimationContext) throws-> KeyframeGroup<Rectangle.Keyframe> {
let combinedKeyframes = Keyframes.combinedIfPossible(size, position, cornerRadius) { untypedValues in
Keyframe(
size: untypedValues[0] as! Vector3D,
position: untypedValues[1] as! Vector3D,
cornerRadius: untypedValues[2] as! Vector1D)
}
let combinedKeyframes = Keyframes.combinedIfPossible(
size, position, cornerRadius,
makeCombinedResult: Rectangle.Keyframe.init)

if let combinedKeyframes = combinedKeyframes {
return combinedKeyframes
Expand Down
13 changes: 2 additions & 11 deletions Sources/Private/CoreAnimation/Animations/StarAnimation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,8 @@ extension Star {
outerRoundness,
innerRoundness ?? KeyframeGroup(Vector1D(0)),
points,
rotation)
{ untypedValues in
Keyframe(
position: untypedValues[0] as! Vector3D,
outerRadius: untypedValues[1] as! Vector1D,
innerRadius: untypedValues[2] as! Vector1D,
outerRoundness: untypedValues[3] as! Vector1D,
innerRoundness: untypedValues[4] as! Vector1D,
points: untypedValues[5] as! Vector1D,
rotation: untypedValues[6] as! Vector1D)
}
rotation,
makeCombinedResult: Star.Keyframe.init)

if let combinedKeyframes = combinedKeyframes {
return combinedKeyframes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,122 @@
// MARK: - Keyframes

enum Keyframes {
/// Combines the given keyframe groups of `Keyframe<T>`s into a single `KeyframeGroup`
/// of `Keyframe<CombinedResult>`s if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<CombinedResult>(
_ allGroups: AnyKeyframeGroup...,
makeCombinedResult: ([Any]) -> CombinedResult)

// MARK: Internal

/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of
/// of `Keyframe<[T]>`s if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T>(_ allGroups: [KeyframeGroup<T>]) -> KeyframeGroup<[T]>? {
combinedIfPossible(allGroups, makeCombinedResult: { index in
allGroups.map { $0.valueForCombinedKeyframes(at: index) }
})
}

/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of
/// of `Keyframe<[T]>`s if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T1, T2, CombinedResult>(
_ k1: KeyframeGroup<T1>,
_ k2: KeyframeGroup<T2>,
makeCombinedResult: (T1, T2) -> CombinedResult)
-> KeyframeGroup<CombinedResult>?
{
combinedIfPossible(
allGroups.map { $0.untyped },
makeCombinedResult: makeCombinedResult)
[k1, k2],
makeCombinedResult: { index in
makeCombinedResult(
k1.valueForCombinedKeyframes(at: index),
k2.valueForCombinedKeyframes(at: index))
})
}

/// Combines the given `[KeyframeGroup]` of `Keyframe<T>`s into a single `KeyframeGroup`
/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of
/// of `Keyframe<[T]>`s if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T>(_ allGroups: [KeyframeGroup<T>]) -> KeyframeGroup<[T]>? {
combinedIfPossible(allGroups, makeCombinedResult: { $0 })
static func combinedIfPossible<T1, T2, T3, CombinedResult>(
_ k1: KeyframeGroup<T1>,
_ k2: KeyframeGroup<T2>,
_ k3: KeyframeGroup<T3>,
makeCombinedResult: (T1, T2, T3) -> CombinedResult)
-> KeyframeGroup<CombinedResult>?
{
combinedIfPossible(
[k1, k2, k3],
makeCombinedResult: { index in
makeCombinedResult(
k1.valueForCombinedKeyframes(at: index),
k2.valueForCombinedKeyframes(at: index),
k3.valueForCombinedKeyframes(at: index))
})
}

/// Combines the given keyframe groups of `Keyframe<T>`s into a single keyframe group of
/// of `Keyframe<[T]>`s if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T1, T2, T3, T4, T5, T6, T7, CombinedResult>(
_ k1: KeyframeGroup<T1>,
_ k2: KeyframeGroup<T2>,
_ k3: KeyframeGroup<T3>,
_ k4: KeyframeGroup<T4>,
_ k5: KeyframeGroup<T5>,
_ k6: KeyframeGroup<T6>,
_ k7: KeyframeGroup<T7>,
makeCombinedResult: (T1, T2, T3, T4, T5, T6, T7) -> CombinedResult)
-> KeyframeGroup<CombinedResult>?
{
combinedIfPossible(
[k1, k2, k3, k4, k5, k6, k7],
makeCombinedResult: { index in
makeCombinedResult(
k1.valueForCombinedKeyframes(at: index),
k2.valueForCombinedKeyframes(at: index),
k3.valueForCombinedKeyframes(at: index),
k4.valueForCombinedKeyframes(at: index),
k5.valueForCombinedKeyframes(at: index),
k6.valueForCombinedKeyframes(at: index),
k7.valueForCombinedKeyframes(at: index))
})
}

/// Combines the given `[KeyframeGroup?]` of `Keyframe<T>`s
/// into a single `KeyframeGroup` of `Keyframe<[T]>`s
/// if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T>(_ groups: [KeyframeGroup<T>?]) -> KeyframeGroup<[T]>? {
let nonOptionalGroups = groups.compactMap { $0 }
guard nonOptionalGroups.count == groups.count else { return nil }
return combinedIfPossible(nonOptionalGroups)
}

// MARK: Private

/// Combines the given `[KeyframeGroup]` of `Keyframe<T>`s into a single `KeyframeGroup`
/// of `Keyframe<CombinedResult>`s if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T, CombinedResult>(
_ allGroups: [KeyframeGroup<T>],
makeCombinedResult: ([T]) -> CombinedResult)
private static func combinedIfPossible<CombinedResult>(
_ allGroups: [AnyKeyframeGroup],
makeCombinedResult: (_ index: Int) -> CombinedResult)
-> KeyframeGroup<CombinedResult>?
{
let untypedGroups = allGroups.map { $0.untyped }

// Animations with no timing information (e.g. with just a single keyframe)
// can be trivially combined with any other set of keyframes, so we don't need
// to check those.
let animatingKeyframes = allGroups.filter { $0.keyframes.count > 1 }
let animatingKeyframes = untypedGroups.filter { $0.keyframes.count > 1 }

guard
!allGroups.isEmpty,
animatingKeyframes.allSatisfy({ $0.hasSameTimingParameters(as: animatingKeyframes[0]) })
else { return nil }

var combinedKeyframes = ContiguousArray<Keyframe<CombinedResult>>()
let baseKeyframes = (animatingKeyframes.first ?? allGroups[0]).keyframes
let baseKeyframes = (animatingKeyframes.first ?? untypedGroups[0]).keyframes

for index in baseKeyframes.indices {
let baseKeyframe = baseKeyframes[index]
let combinedValues = allGroups.map { otherKeyframes -> T in
if otherKeyframes.keyframes.count == 1 {
return otherKeyframes.keyframes[0].value
} else {
return otherKeyframes.keyframes[index].value
}
}
combinedKeyframes.append(baseKeyframe.withValue(makeCombinedResult(combinedValues)))
let combinedValue = makeCombinedResult(index)
combinedKeyframes.append(baseKeyframe.withValue(combinedValue))
}

return KeyframeGroup(keyframes: combinedKeyframes)
}

/// Combines the given `[KeyframeGroup?]` of `Keyframe<T>`s
/// into a single `KeyframeGroup` of `Keyframe<[T]>`s
/// if all of the `KeyframeGroup`s have the exact same animation timing
static func combinedIfPossible<T>(_ groups: [KeyframeGroup<T>?]) -> KeyframeGroup<[T]>? {
let nonOptionalGroups = groups.compactMap { $0 }
guard nonOptionalGroups.count == groups.count else { return nil }
return combinedIfPossible(nonOptionalGroups)
}
}

extension KeyframeGroup {
Expand Down Expand Up @@ -95,3 +150,14 @@ extension Keyframe {
// combined in this way.
}
}

extension KeyframeGroup {
/// The value to use for a combined set of keyframes, for the given index
fileprivate func valueForCombinedKeyframes(at index: Int) -> T {
if keyframes.count == 1 {
return keyframes[0].value
} else {
return keyframes[index].value
}
}
}

0 comments on commit 96980ab

Please sign in to comment.