-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfloatlist.py
344 lines (268 loc) · 7.72 KB
/
floatlist.py
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#module: floatlist.py
from math import floor, ceil
def _lerp(a, b, t):
"""
Linearly interpolates between A and B fractional distance t.
Parameters:
a (Any): The first of the two values to interpolate between.
b (Any): The second of the two values to interpolate between.
t (float): The fractional distance between the two to
evaluate for.
Returns:
I'll be straight with you: a + (b-a)*t.
Preconditions:
The instances passed as a and b must define the "+", "-", and
"*" operators.
t must be within the range [0..1].
Postconditions:
None.
"""
return a + (b-a)*t
def _frange(start, stop, step=1):
"""
Returns a list containing all the terms at <step> intervals
between start and stop. This expands upon the built-in range()
as one can now use fractional values.
Parameters:
start (Numerical): The value to start the sequence at.
stop (Numerical): The value to stop at or before.
step (Numerical): The value to step by.
Returns:
A list defined as follows:
{ [start, start+step*1, ... start+step*n] | start+step*n <= stop}
Preconditions:
None.
Postconditions:
Note that the values contained in the returned list may not
contain stop as a value.
"""
if start == None:
start = 0
if step == None:
step = 1
lst = []
while(start <= stop):
lst.append(start)
start += step
return lst
class FloatList(list):
"""
Decorates Python's standard list with the ability to comprehend floating
point indices and optional relevant linear interpolation for transitive
values.
slots:
lerp (Boolean): Whether or not to linearly interpolate between the
nearest two indices when sampling or setting.
"""
__slots__= ("lerp")
def __init__(self, interpolate=True):
"""
Initializes the FloatList.
Parameters:
-interpolate (Boolean): Whether or not to interpolate on access
and update. (Default: True)
Returns:
None.
Preconditions:
None.
Postconditions:
None.
"""
super().__init__(self)
self.lerp = interpolate
def __init__(self, list, interpolate=True):
"""
Optional constructor which accepts a pre-existing list type as initial
data.
Parameters:
-list (List): An initial set of data.
-interpolate (Boolean): Whether or not to interpolate on access
and update. (Default: True)
Returns:
None.
Preconditions:
Every item in list must arithmetically define '+', '-', and '*'
operations for interpolation purposes.
Postconditions:
None.
"""
super(FloatList, self).__init__(self)
self.lerp = interpolate
for i in list:
self.append(i)
def __len__(self):
"""
Returns the length of this FloatList instance, by simply calling
the __len__() function of the decorated list.
This defines the behavior of len() when passed an instance of
FloatList.
Parameters:
None.
Returns:
The length of this FloatList.
Preconditions:
None.
Postconditions:
None.
"""
return super(FloatList, self).__len__()
def __getitem__(self, key):
"""
Overrides the behavior of bracketed list access, allowing for
floating point values to be used, and interpolated values to
be returned.
Parameters:
key (Float, Slice): The floating point index of the value to
be returned.
Returns:
The item, or a NotFoundException, as defined by list.__getitem__().
Preconditions:
If the key given is a slice, it is allowed to contain floating point
values, just as floating point values are allowed as indices.
Postconditions:
None.
"""
try:
if isinstance(key, slice):
return self.__get_slice(key)
else:
return self.__get_single(key)
except TypeError:
print("Could not retrieve key="+str(key)+". Key not integer, "+\
"float, or slice.")
def __get_slice(self, s):
"""
Returns a slice of this FloatList.
Parameters:
s (slice): The slice instance created to access the list.
Returns:
A list of values at the indices specified by the slice modulo the
length of the list.
Preconditions:
The given slice may contain floating point values.
Postconditions:
A list is created, populated with the requested values, and finally
returned.
"""
items = []
for i in _frange(s.start, s.stop, s.step):
items.append(self.__get_single(i))
return items
def __get_single(self, k):
"""
Returns a single element from the float list.
Parameters:
k (Float): The floating point index to sample.
Returns:
The value at that index.
Preconditions:
None.
Postconditions:
if interpolation is specified against, the index is simply floored.
Otherwise the value returned is the interpolation of the elements
on either integer side of the specified index.
"""
k %= super(FloatList, self).__len__()
if self.lerp:
return _lerp( super(FloatList, self).__getitem__(floor(k)), \
super(FloatList, self).__getitem__(ceil(k)% \
super(FloatList, self).__len__()), \
k-floor(k) )
return super(FloatList, self).__getitem__(floor(k))
def __setitem__(self, key, value):
"""
Assigns a value to pre-existing items.
The nearest two values to the given index are updated proportionately
to their distance to the given index.
Parameters:
key (Float, slice): The index (indices) to edit.
value (Float): The value to store.
Returns:
None.
Preconditions:
If a slice is passed as key, it may contain floating point values.
Postconditions:
The values around each given index are updated with the value given.
"""
try:
if isinstance(key, slice):
self.__set_slice(key, value)
else:
return self.__set_single(key, value)
except TypeError:
print("Key not integer, float, or slice type.")
def __set_slice(self, s, v):
"""
Sets a slice of this FloatList with the given value.
Parameters:
s (slice): The indices to edit.
v (Float): The value to store.
Returns:
None.
Preconditions:
The slice may contain floating point values.
Postconditions:
The values around each given index are updated with the value given.
"""
for i in _frange(s.start, s.stop, s.step):
self.__set_single(i, v.__get_single(i))
def __set_single(self, k, v):
"""
Sets the nearest two indices to the given key.of the FloatList with the
given value.
Parameters:
k (slice): The index to edit.
v (Float): The value to store.
Returns:
None.
Preconditions:
None.
Postconditions:
The values around the given index are updated with the value given.
"""
k %= super(FloatList, self).__len__()
if self.lerp:
super(FloatList, self).__setitem__(floor(k), _lerp(v,
super(FloatList, self).__getitem__(floor(k)),
k-floor(k)) )
super(FloatList, self).__setitem__(ceil(k), _lerp( super(FloatList, self).__getitem__(ceil(k)),
v,
k-floor(k)) )
else:
super(FloatList, self).__setitem__(floor(k), v)
def __contains__(self, item):
"""
Returns whether or not this FloatList contains the given item.
Parameters:
item (Any): The item to search for.
Returns:
Whether or not this FloatList contains the given item.
Preconditions:
None.
Postconditions:
None.
"""
if self.lerp:
for i in range(super(FloatList, self).__len__()-1):
if super(FloatList, self).__getitem__(i) <= item and \
super(FloatList, self).__getitem__(i+1) >= item:
return True
else:
return super(FloatList, self).__contains__(item)
return False
def aslist(self):
"""
Returns the contents of this FloatList as a list.
Parameters:
None.
Returns:
The contents of this FloatList as a list.
Preconditions:
None.
Postconditions:
None.
"""
l = []
for i in self:
l.append[i]
return l