forked from cii0/ShikishiOSS
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAnimation.swift
124 lines (121 loc) · 4.38 KB
/
Animation.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2021 Cii
//
// This file is part of Shikishi.
//
// Shikishi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Shikishi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Shikishi. If not, see <http://www.gnu.org/licenses/>.
struct Animation<Value: Interpolatable> {
var keyframes = [Keyframe<Value>]()
}
extension Animation: Codable where Value: Codable {}
extension Animation {
mutating func insert(_ keyframe: Keyframe<Value>) {
for (i, aKeyframe) in keyframes.enumerated() {
if keyframe.time < aKeyframe.time {
keyframes.insert(keyframe, at: i)
return
}
}
keyframes.append(keyframe)
}
func value(withTime t: Rational) -> Value? {
if let result = timeResult(withTime: t) {
return value(with: result)
} else {
return nil
}
}
func value(with timeResult: TimeResult) -> Value? {
guard !keyframes.isEmpty else {
return nil
}
let i1 = timeResult.index, it = timeResult.internalTime
let k1 = keyframes[i1]
if k1.type == .step {
return k1.value
}
guard it > 0 && i1 + 1 < keyframes.count,
let st = timeResult.sectionTime else { return k1.value }
let k2 = keyframes[i1 + 1]
if keyframes.count <= 2 || k1.type == .linear {
let t = Double(it / st)
return Value.linear(k1.value, k2.value, t: t)
} else {
let t = Double(it / st)
let isUseFirstIndex = i1 - 1 >= 0
let isUseLastIndex = i1 + 2 < keyframes.count
if isUseFirstIndex {
if isUseLastIndex {
let k0 = keyframes[i1 - 1], k3 = keyframes[i1 + 2]
return Value.spline(k0.value, k1.value,
k2.value, k3.value, t: t)
} else {
let k0 = keyframes[i1 - 1]
return Value.lastSpline(k0.value, k1.value, k2.value, t: t)
}
} else if isUseLastIndex {
let k3 = keyframes[i1 + 2]
return Value.firstSpline(k1.value, k2.value, k3.value, t: t)
} else {
let t = Double(it / st)
return Value.linear(k1.value, k2.value, t: t)
}
}
}
struct TimeResult {
var index: Int, internalTime: Rational
var sectionTime: Rational?, time: Rational
}
func timeResult(withTime t: Rational) -> TimeResult? {
guard !keyframes.isEmpty else {
return nil
}
var oldT: Rational?
for i in (0..<keyframes.count).reversed() {
let ki = keyframes[i]
let kt = ki.time
if t >= kt {
if let oldT = oldT {
return TimeResult(index: i,
internalTime: t - kt,
sectionTime: oldT - kt, time: t)
} else {
return TimeResult(index: i,
internalTime: t - kt,
sectionTime: nil, time: t)
}
}
oldT = kt
}
if let oldT = oldT {
return TimeResult(index: 0,
internalTime: t - keyframes.first!.time,
sectionTime: oldT - keyframes.first!.time,
time: t)
} else {
return TimeResult(index: 0,
internalTime: 0,
sectionTime: nil,
time: t)
}
}
}
struct Keyframe<Value: Interpolatable> {
enum KeyframeType: Int8, Codable {
case step, linear, spline
}
var value: Value
var type = KeyframeType.spline
var time = Rational(0)
}
extension Keyframe: Codable where Value: Codable {}