Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
path_component.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "path_component.h"
6
7#include <cmath>
8
10
11namespace impeller {
12
13/*
14 * Based on: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Specific_cases
15 */
16
17static inline Scalar LinearSolve(Scalar t, Scalar p0, Scalar p1) {
18 return p0 + t * (p1 - p0);
19}
20
21static inline Scalar QuadraticSolve(Scalar t, Scalar p0, Scalar p1, Scalar p2) {
22 return (1 - t) * (1 - t) * p0 + //
23 2 * (1 - t) * t * p1 + //
24 t * t * p2;
25}
26
28 Scalar p0,
29 Scalar p1,
30 Scalar p2) {
31 return 2 * (1 - t) * (p1 - p0) + //
32 2 * t * (p2 - p1);
33}
34
35static inline Scalar CubicSolve(Scalar t,
36 Scalar p0,
37 Scalar p1,
38 Scalar p2,
39 Scalar p3) {
40 return (1 - t) * (1 - t) * (1 - t) * p0 + //
41 3 * (1 - t) * (1 - t) * t * p1 + //
42 3 * (1 - t) * t * t * p2 + //
43 t * t * t * p3;
44}
45
47 Scalar p0,
48 Scalar p1,
49 Scalar p2,
50 Scalar p3) {
51 return -3 * p0 * (1 - t) * (1 - t) + //
52 p1 * (3 * (1 - t) * (1 - t) - 6 * (1 - t) * t) +
53 p2 * (6 * (1 - t) * t - 3 * t * t) + //
54 3 * p3 * t * t;
55}
56
58 return {
59 LinearSolve(time, p1.x, p2.x), // x
60 LinearSolve(time, p1.y, p2.y), // y
61 };
62}
63
65 std::vector<Point>& points) const {
66 if (points.size() == 0 || points.back() != p2) {
67 points.push_back(p2);
68 }
69}
70
71std::vector<Point> LinearPathComponent::Extrema() const {
72 return {p1, p2};
73}
74
75std::optional<Vector2> LinearPathComponent::GetStartDirection() const {
76 if (p1 == p2) {
77 return std::nullopt;
78 }
79 return (p1 - p2).Normalize();
80}
81
82std::optional<Vector2> LinearPathComponent::GetEndDirection() const {
83 if (p1 == p2) {
84 return std::nullopt;
85 }
86 return (p2 - p1).Normalize();
87}
88
90 return {
91 QuadraticSolve(time, p1.x, cp.x, p2.x), // x
92 QuadraticSolve(time, p1.y, cp.y, p2.y), // y
93 };
94}
95
97 return {
98 QuadraticSolveDerivative(time, p1.x, cp.x, p2.x), // x
99 QuadraticSolveDerivative(time, p1.y, cp.y, p2.y), // y
100 };
101}
102
104 Scalar scale_factor,
105 std::vector<Point>& points) const {
106 ToLinearPathComponents(scale_factor, [&points](const Point& point) {
107 points.emplace_back(point);
108 });
109}
110
112 Scalar scale_factor,
113 const PointProc& proc) const {
114 Scalar line_count =
115 std::ceilf(ComputeQuadradicSubdivisions(scale_factor, *this));
116 for (size_t i = 1; i < line_count; i += 1) {
117 proc(Solve(i / line_count));
118 }
119 proc(p2);
120}
121
122std::vector<Point> QuadraticPathComponent::Extrema() const {
123 CubicPathComponent elevated(*this);
124 return elevated.Extrema();
125}
126
127std::optional<Vector2> QuadraticPathComponent::GetStartDirection() const {
128 if (p1 != cp) {
129 return (p1 - cp).Normalize();
130 }
131 if (p1 != p2) {
132 return (p1 - p2).Normalize();
133 }
134 return std::nullopt;
135}
136
137std::optional<Vector2> QuadraticPathComponent::GetEndDirection() const {
138 if (p2 != cp) {
139 return (p2 - cp).Normalize();
140 }
141 if (p2 != p1) {
142 return (p2 - p1).Normalize();
143 }
144 return std::nullopt;
145}
146
148 return {
149 CubicSolve(time, p1.x, cp1.x, cp2.x, p2.x), // x
150 CubicSolve(time, p1.y, cp1.y, cp2.y, p2.y), // y
151 };
152}
153
155 return {
156 CubicSolveDerivative(time, p1.x, cp1.x, cp2.x, p2.x), // x
157 CubicSolveDerivative(time, p1.y, cp1.y, cp2.y, p2.y), // y
158 };
159}
160
163 std::vector<Point>& points) const {
165 scale, [&points](const Point& point) { points.emplace_back(point); });
166}
167
168inline QuadraticPathComponent CubicPathComponent::Lower() const {
169 return QuadraticPathComponent(3.0 * (cp1 - p1), 3.0 * (cp2 - cp1),
170 3.0 * (p2 - cp2));
171}
172
174 auto p0 = Solve(t0);
175 auto p3 = Solve(t1);
176 auto d = Lower();
177 auto scale = (t1 - t0) * (1.0 / 3.0);
178 auto p1 = p0 + scale * d.Solve(t0);
179 auto p2 = p3 - scale * d.Solve(t1);
180 return CubicPathComponent(p0, p1, p2, p3);
181}
182
184 const PointProc& proc) const {
185 Scalar line_count = std::ceilf(ComputeCubicSubdivisions(scale, *this));
186 for (size_t i = 1; i < line_count; i++) {
187 proc(Solve(i / line_count));
188 }
189 proc(p2);
190}
191
192static inline bool NearEqual(Scalar a, Scalar b, Scalar epsilon) {
193 return (a > (b - epsilon)) && (a < (b + epsilon));
194}
195
196static inline bool NearZero(Scalar a) {
197 return NearEqual(a, 0.0, 1e-12);
198}
199
200static void CubicPathBoundingPopulateValues(std::vector<Scalar>& values,
201 Scalar p1,
202 Scalar p2,
203 Scalar p3,
204 Scalar p4) {
205 const Scalar a = 3.0 * (-p1 + 3.0 * p2 - 3.0 * p3 + p4);
206 const Scalar b = 6.0 * (p1 - 2.0 * p2 + p3);
207 const Scalar c = 3.0 * (p2 - p1);
208
209 /*
210 * Boundary conditions.
211 */
212 if (NearZero(a)) {
213 if (NearZero(b)) {
214 return;
215 }
216
217 Scalar t = -c / b;
218 if (t >= 0.0 && t <= 1.0) {
219 values.emplace_back(t);
220 }
221 return;
222 }
223
224 Scalar b2Minus4AC = (b * b) - (4.0 * a * c);
225
226 if (b2Minus4AC < 0.0) {
227 return;
228 }
229
230 Scalar rootB2Minus4AC = ::sqrt(b2Minus4AC);
231
232 /* From Numerical Recipes in C.
233 *
234 * q = -1/2 (b + sign(b) sqrt[b^2 - 4ac])
235 * x1 = q / a
236 * x2 = c / q
237 */
238 Scalar q = (b < 0) ? -(b - rootB2Minus4AC) / 2 : -(b + rootB2Minus4AC) / 2;
239
240 {
241 Scalar t = q / a;
242 if (t >= 0.0 && t <= 1.0) {
243 values.emplace_back(t);
244 }
245 }
246
247 {
248 Scalar t = c / q;
249 if (t >= 0.0 && t <= 1.0) {
250 values.emplace_back(t);
251 }
252 }
253}
254
255std::vector<Point> CubicPathComponent::Extrema() const {
256 /*
257 * As described in: https://pomax.github.io/bezierinfo/#extremities
258 */
259 std::vector<Scalar> values;
260
263
264 std::vector<Point> points = {p1, p2};
265
266 for (const auto& value : values) {
267 points.emplace_back(Solve(value));
268 }
269
270 return points;
271}
272
273std::optional<Vector2> CubicPathComponent::GetStartDirection() const {
274 if (p1 != cp1) {
275 return (p1 - cp1).Normalize();
276 }
277 if (p1 != cp2) {
278 return (p1 - cp2).Normalize();
279 }
280 if (p1 != p2) {
281 return (p1 - p2).Normalize();
282 }
283 return std::nullopt;
284}
285
286std::optional<Vector2> CubicPathComponent::GetEndDirection() const {
287 if (p2 != cp2) {
288 return (p2 - cp2).Normalize();
289 }
290 if (p2 != cp1) {
291 return (p2 - cp1).Normalize();
292 }
293 if (p2 != p1) {
294 return (p2 - p1).Normalize();
295 }
296 return std::nullopt;
297}
298
300 const LinearPathComponent* component) {
301 if (!component) {
302 return std::nullopt;
303 }
304 return component->GetStartDirection();
305}
306
308 const QuadraticPathComponent* component) {
309 if (!component) {
310 return std::nullopt;
311 }
312 return component->GetStartDirection();
313}
314
316 const CubicPathComponent* component) {
317 if (!component) {
318 return std::nullopt;
319 }
320 return component->GetStartDirection();
321}
322
324 const LinearPathComponent* component) {
325 if (!component) {
326 return std::nullopt;
327 }
328 return component->GetEndDirection();
329}
330
332 const QuadraticPathComponent* component) {
333 if (!component) {
334 return std::nullopt;
335 }
336 return component->GetEndDirection();
337}
338
340 const CubicPathComponent* component) {
341 if (!component) {
342 return std::nullopt;
343 }
344 return component->GetEndDirection();
345}
346
347} // namespace impeller
static const int points[]
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
static bool b
struct MyStruct a[10]
uint8_t value
static void CubicPathBoundingPopulateValues(std::vector< Scalar > &values, Scalar p1, Scalar p2, Scalar p3, Scalar p4)
float Scalar
Definition scalar.h:18
static bool NearZero(Scalar a)
static Scalar LinearSolve(Scalar t, Scalar p0, Scalar p1)
static Scalar CubicSolve(Scalar t, Scalar p0, Scalar p1, Scalar p2, Scalar p3)
static Scalar CubicSolveDerivative(Scalar t, Scalar p0, Scalar p1, Scalar p2, Scalar p3)
static Scalar QuadraticSolve(Scalar t, Scalar p0, Scalar p1, Scalar p2)
static Scalar QuadraticSolveDerivative(Scalar t, Scalar p0, Scalar p1, Scalar p2)
Scalar ComputeQuadradicSubdivisions(Scalar scale_factor, Point p0, Point p1, Point p2)
Scalar ComputeCubicSubdivisions(Scalar scale_factor, Point p0, Point p1, Point p2, Point p3)
static bool NearEqual(Scalar a, Scalar b, Scalar epsilon)
const Scalar scale
void ToLinearPathComponents(Scalar scale, const PointProc &proc) const
void AppendPolylinePoints(Scalar scale, std::vector< Point > &points) const
std::function< void(const Point &point)> PointProc
CubicPathComponent Subsegment(Scalar t0, Scalar t1) const
Point Solve(Scalar time) const
std::optional< Vector2 > GetStartDirection() const
std::vector< Point > Extrema() const
std::optional< Vector2 > GetEndDirection() const
Point SolveDerivative(Scalar time) const
std::optional< Vector2 > GetEndDirection() const
std::optional< Vector2 > GetStartDirection() const
std::vector< Point > Extrema() const
Point Solve(Scalar time) const
void AppendPolylinePoints(std::vector< Point > &points) const
std::optional< Vector2 > operator()(const LinearPathComponent *component)
std::optional< Vector2 > operator()(const LinearPathComponent *component)
std::optional< Vector2 > GetEndDirection() const
void AppendPolylinePoints(Scalar scale_factor, std::vector< Point > &points) const
std::function< void(const Point &point)> PointProc
std::vector< Point > Extrema() const
Point SolveDerivative(Scalar time) const
std::optional< Vector2 > GetStartDirection() const
void ToLinearPathComponents(Scalar scale_factor, const PointProc &proc) const
Point Solve(Scalar time) const
constexpr TPoint Normalize() const
Definition point.h:208