lottie/vector: added angleAt() api to bezier class.
[platform/core/uifw/lottie-player.git] / src / vector / vbezier.cpp
1 #include "vbezier.h"
2 #include "vline.h"
3 #include <cmath>
4
5 V_BEGIN_NAMESPACE
6
7 VBezier VBezier::fromPoints(const VPointF &p1, const VPointF &p2,
8                             const VPointF &p3, const VPointF &p4)
9 {
10     VBezier b;
11     b.x1 = p1.x();
12     b.y1 = p1.y();
13     b.x2 = p2.x();
14     b.y2 = p2.y();
15     b.x3 = p3.x();
16     b.y3 = p3.y();
17     b.x4 = p4.x();
18     b.y4 = p4.y();
19     return b;
20 }
21
22 float VBezier::length() const
23 {
24     VBezier left, right; /* bez poly splits */
25     float   len = 0.0;   /* arc length */
26     float   chord;       /* chord length */
27     float   length;
28
29     len = len + VLine::length(x1, y1, x2, y2);
30     len = len + VLine::length(x2, y2, x3, y3);
31     len = len + VLine::length(x3, y3, x4, y4);
32
33     chord = VLine::length(x1, y1, x4, y4);
34
35     if ((len - chord) > 0.01) {
36         split(&left, &right);    /* split in two */
37         length = left.length() + /* try left side */
38                  right.length(); /* try right side */
39
40         return length;
41     }
42
43     return len;
44 }
45
46 VBezier VBezier::onInterval(float t0, float t1) const
47 {
48     if (t0 == 0 && t1 == 1) return *this;
49
50     VBezier bezier = *this;
51
52     VBezier result;
53     bezier.parameterSplitLeft(t0, &result);
54     float trueT = (t1 - t0) / (1 - t0);
55     bezier.parameterSplitLeft(trueT, &result);
56
57     return result;
58 }
59
60 float VBezier::tAtLength(float l) const
61 {
62     float       len = length();
63     float       t = 1.0;
64     const float error = 0.01;
65     if (l > len || vCompare(l, len)) return t;
66
67     t *= 0.5;
68
69     float lastBigger = 1.0;
70     while (1) {
71         VBezier right = *this;
72         VBezier left;
73         right.parameterSplitLeft(t, &left);
74         float lLen = left.length();
75         if (fabs(lLen - l) < error) break;
76
77         if (lLen < l) {
78             t += (lastBigger - t) * 0.5;
79         } else {
80             lastBigger = t;
81             t -= t * 0.5;
82         }
83     }
84     return t;
85 }
86
87 void VBezier::splitAtLength(float len, VBezier *left, VBezier *right)
88 {
89     float t;
90
91     *right = *this;
92     t = right->tAtLength(len);
93     right->parameterSplitLeft(t, left);
94 }
95
96 VPointF VBezier::derivative(float t) const
97 {
98     // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * t^2) * p2 + t^2 * p3)
99
100     float m_t = 1. - t;
101
102     float d = t * t;
103     float a = -m_t * m_t;
104     float b = 1 - 4 * t + 3 * d;
105     float c = 2 * t - 3 * d;
106
107     return 3 * VPointF(a * x1 + b * x2 + c * x3 + d * x4,
108                        a * y1 + b * y2 + c * y3 + d * y4);
109 }
110
111
112 float VBezier::angleAt(float t) const
113 {
114     if (t < 0 || t > 1) {
115         return 0;
116     }
117     return VLine({}, derivative(t)).angle();
118 }
119
120
121 V_END_NAMESPACE