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 }