1 module mutils.timeline.utils;
2 
3 import std.traits;
4 
5 struct TimeIndexGetter {
6 	uint lastIndex = 0;
7 	float lastTime = float.min_exp;
8 
9 	void reset() {
10 		this = this.init;
11 	}
12 
13 	void set(T)(in T[] slice, float time) {
14 		if (time <= slice[0].time || lastTime > time) {
15 			lastIndex = 0;
16 		}
17 		lastTime = time;
18 		foreach (uint i; lastIndex + 1 .. cast(uint) slice.length) {
19 			if (time <= slice[i].time) {
20 				lastIndex = i - 1;
21 				return;
22 			}
23 
24 		}
25 		lastIndex = cast(uint) slice.length - 1;
26 	}
27 
28 	/// Returns [currIndex, nextIndex]
29 	uint[2] index(T)(in T[] slice, float time) {
30 		static assert(hasMember!(T, "time"));
31 		assert(slice.length < lastIndex.max);
32 		assert(slice.length > 0);
33 		if (lastIndex >= slice.length || lastTime > time) {
34 			lastIndex = 0;
35 		}
36 
37 		lastTime = time;
38 
39 		uint[2] ti;
40 		if (time <= slice[0].time) {
41 			lastIndex = 0;
42 			return ti;
43 		}
44 		foreach (uint i; lastIndex + 1 .. cast(uint) slice.length) {
45 			if (time <= slice[i].time) {
46 				ti[0] = i - 1;
47 				ti[1] = i;
48 				lastIndex = i - 1;
49 				return ti;
50 			}
51 
52 		}
53 		lastIndex = cast(uint) slice.length - 2;
54 		uint last = cast(uint)(slice.length - 1);
55 		ti[0] = last;
56 		ti[1] = last;
57 		return ti;
58 	}
59 
60 	T[] passedFromLast(T)(T[] slice, float time) {
61 		static assert(hasMember!(T, "time"));
62 		assert(slice.length < lastIndex.max);
63 		assert(slice.length > 0);
64 
65 		scope (exit)
66 			lastTime = time;
67 
68 		if (lastIndex > slice.length || lastTime > time || time <= slice[0].time) {
69 			lastIndex = 0;
70 			return null;
71 		}
72 
73 		foreach (uint i; lastIndex + 1 .. cast(uint) slice.length) {
74 			if (time <= slice[i].time) {
75 				bool after = slice[lastIndex].time < lastTime;
76 				T[] ret = slice[lastIndex + after .. i];
77 				lastIndex = i - 1;
78 				return ret;
79 			}
80 
81 		}
82 		bool after = slice[lastIndex].time < lastTime;
83 		T[] ret = slice[lastIndex + after .. slice.length];
84 		lastIndex = cast(uint) slice.length - 1;
85 		return ret;
86 	}
87 }
88 
89 // Helper to avoid GC
90 private T[n] s(T, size_t n)(auto ref T[n] array) pure nothrow @nogc @safe {
91 	return array;
92 }
93 
94 unittest {
95 	import std.algorithm : equal;
96 
97 	struct Data {
98 		float time;
99 	}
100 
101 	TimeIndexGetter getter;
102 	Data[5] data = [Data(0), Data(1), Data(2), Data(3), Data(4)];
103 	//Check index
104 	assert(getter.index(data, 0) == [0, 0].s);
105 	assert(getter.index(data, 5) == [4, 4].s);
106 	assert(getter.index(data, 1) == [0, 1].s);
107 	assert(getter.index(data, 4) == [3, 4].s);
108 	assert(getter.index(data, -5) == [0, 0].s);
109 	//Check passedFromLast
110 	assert(getter.passedFromLast(data, 0) == null);
111 	assert(getter.passedFromLast(data, 0.5) == data[0 .. 1]);
112 	assert(getter.passedFromLast(data, 0.5) == null);
113 	assert(getter.passedFromLast(data, 1.1) == data[1 .. 2]);
114 	assert(getter.passedFromLast(data, 1.1) == null);
115 	assert(getter.passedFromLast(data, 4.1) == data[2 .. 5]);
116 	assert(getter.passedFromLast(data, 5) == null);
117 	assert(getter.passedFromLast(data, 10) == null);
118 	assert(getter.passedFromLast(data, -10) == null);
119 	assert(getter.passedFromLast(data, 10) == data);
120 	//Check index after passedFromLast
121 	assert(getter.index(data, 15) == [4, 4].s);
122 	//Check set
123 	getter.set(data, 1.5);
124 	assert(getter.index(data, 1.7) == [1, 2].s);
125 	assert(getter.lastIndex == 1);
126 	assert(getter.passedFromLast(data, 2.5) == data[2 .. 3]);
127 	//Reset
128 	getter.reset();
129 	assert(getter == TimeIndexGetter.init);
130 
131 }