/
PropertyList.ts
161 lines (136 loc) · 4.51 KB
/
PropertyList.ts
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import { combineRGBComponents, SimpleEase, Color } from './ParticleUtils';
import { PropertyNode } from './PropertyNode';
function intValueSimple(this: PropertyList<number>, lerp: number): number
{
if (this.ease) lerp = this.ease(lerp);
return ((this.first.next.value - this.first.value) * lerp) + this.first.value;
}
function intColorSimple(this: PropertyList<Color>, lerp: number): number
{
if (this.ease) lerp = this.ease(lerp);
const curVal = this.first.value;
const nextVal = this.first.next.value;
const r = ((nextVal.r - curVal.r) * lerp) + curVal.r;
const g = ((nextVal.g - curVal.g) * lerp) + curVal.g;
const b = ((nextVal.b - curVal.b) * lerp) + curVal.b;
return combineRGBComponents(r, g, b);
}
function intValueComplex(this: PropertyList<number>, lerp: number): number
{
if (this.ease) lerp = this.ease(lerp);
// make sure we are on the right segment
let current = this.first;
let next = current.next;
while (lerp > next.time)
{
current = next;
next = next.next;
}
// convert the lerp value to the segment range
lerp = (lerp - current.time) / (next.time - current.time);
return ((next.value - current.value) * lerp) + current.value;
}
function intColorComplex(this: PropertyList<Color>, lerp: number): number
{
if (this.ease) lerp = this.ease(lerp);
// make sure we are on the right segment
let current = this.first;
let next = current.next;
while (lerp > next.time)
{
current = next;
next = next.next;
}
// convert the lerp value to the segment range
lerp = (lerp - current.time) / (next.time - current.time);
const curVal = current.value;
const nextVal = next.value;
const r = ((nextVal.r - curVal.r) * lerp) + curVal.r;
const g = ((nextVal.g - curVal.g) * lerp) + curVal.g;
const b = ((nextVal.b - curVal.b) * lerp) + curVal.b;
return combineRGBComponents(r, g, b);
}
function intValueStepped(this: PropertyList<number>, lerp: number): number
{
if (this.ease) lerp = this.ease(lerp);
// make sure we are on the right segment
let current = this.first;
while (current.next && lerp > current.next.time)
{
current = current.next;
}
return current.value;
}
function intColorStepped(this: PropertyList<Color>, lerp: number): number
{
if (this.ease) lerp = this.ease(lerp);
// make sure we are on the right segment
let current = this.first;
while (current.next && lerp > current.next.time)
{
current = current.next;
}
const curVal = current.value;
return combineRGBComponents(curVal.r, curVal.g, curVal.b);
}
/**
* Singly linked list container for keeping track of interpolated properties for particles.
* Each Particle will have one of these for each interpolated property.
*/
export class PropertyList<V>
{
/**
* The first property node in the linked list.
*/
public first: PropertyNode<V>;
/**
* Calculates the correct value for the current interpolation value. This method is set in
* the reset() method.
* @param lerp The interpolation value from 0-1.
* @return The interpolated value. Colors are converted to the hex value.
*/
public interpolate: (lerp: number) => number;
/**
* A custom easing method for this list.
* @param lerp The interpolation value from 0-1.
* @return The eased value, also from 0-1.
*/
public ease: SimpleEase;
/**
* If this list manages colors, which requires a different method for interpolation.
*/
private isColor: boolean;
/**
* @param isColor If this list handles color values
*/
constructor(isColor = false)
{
this.first = null;
this.isColor = !!isColor;
this.interpolate = null;
this.ease = null;
}
/**
* Resets the list for use.
* @param first The first node in the list.
* @param first.isStepped If the values should be stepped instead of interpolated linearly.
*/
public reset(first: PropertyNode<V>): void
{
this.first = first;
const isSimple = first.next && first.next.time >= 1;
if (isSimple)
{
this.interpolate = this.isColor ? intColorSimple : intValueSimple;
}
else if (first.isStepped)
{
this.interpolate = this.isColor ? intColorStepped : intValueStepped;
}
else
{
this.interpolate = this.isColor ? intColorComplex : intValueComplex;
}
this.ease = this.first.ease;
}
}