1 module plot2d.anim; 2 3 import std.algorithm : min, max; 4 5 import plot2d.types; 6 import plot2d.interp; 7 8 interface Transition 9 { 10 /++ 11 Params: 12 dt = time from animation start 13 dur = animation duration 14 Returns: 15 value from 0 to 1 16 +/ 17 float opCall(float dt, float dur) 18 out (v) 19 { 20 assert (v >= 0, "less that zero"); 21 assert (v <= 1, "more that one"); 22 }; 23 } 24 25 float lineTransition(float dt, float dur) 26 { return max(0, min(1, dt / dur)); } 27 28 class LineTransition : Transition 29 { 30 override float opCall(float dt, float dur) 31 { return lineTransition(dt, dur); } 32 } 33 34 unittest 35 { 36 import std.math; 37 auto lt = new LineTransition; 38 enum eps = 1e-6; 39 void test(float v) { assert(fabs(v) < eps); } 40 test(lt(0, 1)); 41 test(lt(-1, 1)); 42 test(lt(0.5, 1) - 0.5); 43 test(lt(0.5, 2) - 0.25); 44 test(lt(2, 1) - 1); 45 } 46 47 class BezierTransition : Transition 48 { 49 protected: 50 Point zero = Point(0.0f, 0.0f); 51 Point one = Point(1.0f, 1.0f); 52 Point p1, p2; 53 54 public: 55 this() { this(0.5, 0, 0.5, 1); } 56 57 this(float offset) { this(offset, 0, 1-offset, 1); } 58 59 this(float p1x, float p1y, float p2x, float p2y) 60 { 61 p1 = Point(p1x, p1y); 62 p2 = Point(p2x, p2y); 63 } 64 65 override float opCall(float dt, float dur) 66 { return bezierInterp([zero, p1, p2, one], lineTransition(dt, dur)).y; } 67 } 68 69 class TrueBezierTransition : BezierTransition 70 { 71 protected: 72 Point[] tbl; 73 74 void buildTable(size_t cnt) 75 { 76 import std.exception : enforce; 77 enforce(cnt >= 2, "bad points count (<2)"); 78 tbl.length = cnt+1; 79 foreach (i; 0 .. cnt+1) 80 { 81 auto tmp = bezierInterp([zero, p1, p2, one], (i * 1.0f) / cnt); 82 if (i > 0) enforce(tmp.x > tbl[i-1].x, "bad transition points"); 83 tbl[i] = tmp; 84 } 85 } 86 87 public: 88 this(size_t cnt=20) { this(0.5, 0, 0.5, 1, cnt); } 89 90 this(float offset, size_t cnt=20) { this(offset, 0, 1-offset, 1, cnt); } 91 92 this(float p1x, float p1y, float p2x, float p2y, size_t cnt=20) 93 { 94 super(p1x, p1y, p2x, p2y); 95 buildTable(cnt); 96 } 97 98 override float opCall(float dt, float dur) 99 { 100 import std.math; 101 auto t = lineTransition(dt, dur); 102 103 if (fabs(t) <= float.epsilon) return 0; 104 if (fabs(t-1) <= float.epsilon) return 1; 105 106 size_t s = 0, e = tbl.length-1; 107 while (e - s > 1) 108 { 109 auto c = (s+e)/2; 110 if (t > tbl[c].x) s = c; 111 else if (t < tbl[c].x) e = c; 112 else if (t == tbl[c].x) return tbl[c].y; 113 } 114 return min(1, max(0, (tbl[s] + (tbl[s+1] - tbl[s]) * t).y)); 115 } 116 }