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 }