Flutter Engine
The Flutter Engine
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
14 std::vector<uint16_t>& indices)
15 : points_(points), indices_(indices) {}
16
18 if (points_.size() == 0u || contour_start_ == points_.size() - 1) {
19 // Empty or first contour.
20 return;
21 }
22
23 auto start = contour_start_;
24 auto end = points_.size() - 1;
25 // All filled paths are drawn as if they are closed, but if
26 // there is an explicit close then a lineTo to the origin
27 // is inserted. This point isn't strictly necesary to
28 // correctly render the shape and can be dropped.
29 if (points_[end] == points_[start]) {
30 end--;
31 }
32
33 // Triangle strip break for subsequent contours
34 if (contour_start_ != 0) {
35 auto back = indices_.back();
36 indices_.push_back(back);
37 indices_.push_back(start);
38 indices_.push_back(start);
39
40 // If the contour has an odd number of points, insert an extra point when
41 // bridging to the next contour to preserve the correct triangle winding
42 // order.
43 if (previous_contour_odd_points_) {
44 indices_.push_back(start);
45 }
46 } else {
47 indices_.push_back(start);
48 }
49
50 size_t a = start + 1;
51 size_t b = end;
52 while (a < b) {
53 indices_.push_back(a);
54 indices_.push_back(b);
55 a++;
56 b--;
57 }
58 if (a == b) {
59 indices_.push_back(a);
60 previous_contour_odd_points_ = false;
61 } else {
62 previous_contour_odd_points_ = true;
63 }
64 contour_start_ = points_.size();
65}
66
68 points_.push_back(point);
69}
70
71/*
72 * Based on: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Specific_cases
73 */
74
75static inline Scalar LinearSolve(Scalar t, Scalar p0, Scalar p1) {
76 return p0 + t * (p1 - p0);
77}
78
79static inline Scalar QuadraticSolve(Scalar t, Scalar p0, Scalar p1, Scalar p2) {
80 return (1 - t) * (1 - t) * p0 + //
81 2 * (1 - t) * t * p1 + //
82 t * t * p2;
83}
84
86 Scalar p0,
87 Scalar p1,
88 Scalar p2) {
89 return 2 * (1 - t) * (p1 - p0) + //
90 2 * t * (p2 - p1);
91}
92
93static inline Scalar CubicSolve(Scalar t,
94 Scalar p0,
95 Scalar p1,
96 Scalar p2,
97 Scalar p3) {
98 return (1 - t) * (1 - t) * (1 - t) * p0 + //
99 3 * (1 - t) * (1 - t) * t * p1 + //
100 3 * (1 - t) * t * t * p2 + //
101 t * t * t * p3;
102}
103
105 Scalar p0,
106 Scalar p1,
107 Scalar p2,
108 Scalar p3) {
109 return -3 * p0 * (1 - t) * (1 - t) + //
110 p1 * (3 * (1 - t) * (1 - t) - 6 * (1 - t) * t) +
111 p2 * (6 * (1 - t) * t - 3 * t * t) + //
112 3 * p3 * t * t;
113}
114
116 return {
117 LinearSolve(time, p1.x, p2.x), // x
118 LinearSolve(time, p1.y, p2.y), // y
119 };
120}
121
123 std::vector<Point>& points) const {
124 if (points.size() == 0 || points.back() != p2) {
125 points.push_back(p2);
126 }
127}
128
129std::vector<Point> LinearPathComponent::Extrema() const {
130 return {p1, p2};
131}
132
133std::optional<Vector2> LinearPathComponent::GetStartDirection() const {
134 if (p1 == p2) {
135 return std::nullopt;
136 }
137 return (p1 - p2).Normalize();
138}
139
140std::optional<Vector2> LinearPathComponent::GetEndDirection() const {
141 if (p1 == p2) {
142 return std::nullopt;
143 }
144 return (p2 - p1).Normalize();
145}
146
148 return {
149 QuadraticSolve(time, p1.x, cp.x, p2.x), // x
150 QuadraticSolve(time, p1.y, cp.y, p2.y), // y
151 };
152}
153
155 return {
158 };
159}
160
163 VertexWriter& writer) const {
164 Scalar line_count = std::ceilf(ComputeQuadradicSubdivisions(scale, *this));
165 for (size_t i = 1; i < line_count; i += 1) {
166 writer.Write(Solve(i / line_count));
167 }
168 writer.Write(p2);
169}
170
172 Scalar scale_factor,
173 std::vector<Point>& points) const {
174 ToLinearPathComponents(scale_factor, [&points](const Point& point) {
175 points.emplace_back(point);
176 });
177}
178
180 Scalar scale_factor,
181 const PointProc& proc) const {
182 Scalar line_count =
183 std::ceilf(ComputeQuadradicSubdivisions(scale_factor, *this));
184 for (size_t i = 1; i < line_count; i += 1) {
185 proc(Solve(i / line_count));
186 }
187 proc(p2);
188}
189
190std::vector<Point> QuadraticPathComponent::Extrema() const {
191 CubicPathComponent elevated(*this);
192 return elevated.Extrema();
193}
194
195std::optional<Vector2> QuadraticPathComponent::GetStartDirection() const {
196 if (p1 != cp) {
197 return (p1 - cp).Normalize();
198 }
199 if (p1 != p2) {
200 return (p1 - p2).Normalize();
201 }
202 return std::nullopt;
203}
204
205std::optional<Vector2> QuadraticPathComponent::GetEndDirection() const {
206 if (p2 != cp) {
207 return (p2 - cp).Normalize();
208 }
209 if (p2 != p1) {
210 return (p2 - p1).Normalize();
211 }
212 return std::nullopt;
213}
214
216 return {
217 CubicSolve(time, p1.x, cp1.x, cp2.x, p2.x), // x
218 CubicSolve(time, p1.y, cp1.y, cp2.y, p2.y), // y
219 };
220}
221
223 return {
226 };
227}
228
231 std::vector<Point>& points) const {
233 scale, [&points](const Point& point) { points.emplace_back(point); });
234}
235
237 VertexWriter& writer) const {
238 Scalar line_count = std::ceilf(ComputeCubicSubdivisions(scale, *this));
239 for (size_t i = 1; i < line_count; i++) {
240 writer.Write(Solve(i / line_count));
241 }
242 writer.Write(p2);
243}
244
245inline QuadraticPathComponent CubicPathComponent::Lower() const {
246 return QuadraticPathComponent(3.0 * (cp1 - p1), 3.0 * (cp2 - cp1),
247 3.0 * (p2 - cp2));
248}
249
251 auto p0 = Solve(t0);
252 auto p3 = Solve(t1);
253 auto d = Lower();
254 auto scale = (t1 - t0) * (1.0 / 3.0);
255 auto p1 = p0 + scale * d.Solve(t0);
256 auto p2 = p3 - scale * d.Solve(t1);
257 return CubicPathComponent(p0, p1, p2, p3);
258}
259
261 const PointProc& proc) const {
262 Scalar line_count = std::ceilf(ComputeCubicSubdivisions(scale, *this));
263 for (size_t i = 1; i < line_count; i++) {
264 proc(Solve(i / line_count));
265 }
266 proc(p2);
267}
268
269static inline bool NearEqual(Scalar a, Scalar b, Scalar epsilon) {
270 return (a > (b - epsilon)) && (a < (b + epsilon));
271}
272
273static inline bool NearZero(Scalar a) {
274 return NearEqual(a, 0.0, 1e-12);
275}
276
277static void CubicPathBoundingPopulateValues(std::vector<Scalar>& values,
278 Scalar p1,
279 Scalar p2,
280 Scalar p3,
281 Scalar p4) {
282 const Scalar a = 3.0 * (-p1 + 3.0 * p2 - 3.0 * p3 + p4);
283 const Scalar b = 6.0 * (p1 - 2.0 * p2 + p3);
284 const Scalar c = 3.0 * (p2 - p1);
285
286 /*
287 * Boundary conditions.
288 */
289 if (NearZero(a)) {
290 if (NearZero(b)) {
291 return;
292 }
293
294 Scalar t = -c / b;
295 if (t >= 0.0 && t <= 1.0) {
296 values.emplace_back(t);
297 }
298 return;
299 }
300
301 Scalar b2Minus4AC = (b * b) - (4.0 * a * c);
302
303 if (b2Minus4AC < 0.0) {
304 return;
305 }
306
307 Scalar rootB2Minus4AC = ::sqrt(b2Minus4AC);
308
309 /* From Numerical Recipes in C.
310 *
311 * q = -1/2 (b + sign(b) sqrt[b^2 - 4ac])
312 * x1 = q / a
313 * x2 = c / q
314 */
315 Scalar q = (b < 0) ? -(b - rootB2Minus4AC) / 2 : -(b + rootB2Minus4AC) / 2;
316
317 {
318 Scalar t = q / a;
319 if (t >= 0.0 && t <= 1.0) {
320 values.emplace_back(t);
321 }
322 }
323
324 {
325 Scalar t = c / q;
326 if (t >= 0.0 && t <= 1.0) {
327 values.emplace_back(t);
328 }
329 }
330}
331
332std::vector<Point> CubicPathComponent::Extrema() const {
333 /*
334 * As described in: https://pomax.github.io/bezierinfo/#extremities
335 */
336 std::vector<Scalar> values;
337
340
341 std::vector<Point> points = {p1, p2};
342
343 for (const auto& value : values) {
344 points.emplace_back(Solve(value));
345 }
346
347 return points;
348}
349
350std::optional<Vector2> CubicPathComponent::GetStartDirection() const {
351 if (p1 != cp1) {
352 return (p1 - cp1).Normalize();
353 }
354 if (p1 != cp2) {
355 return (p1 - cp2).Normalize();
356 }
357 if (p1 != p2) {
358 return (p1 - p2).Normalize();
359 }
360 return std::nullopt;
361}
362
363std::optional<Vector2> CubicPathComponent::GetEndDirection() const {
364 if (p2 != cp2) {
365 return (p2 - cp2).Normalize();
366 }
367 if (p2 != cp1) {
368 return (p2 - cp1).Normalize();
369 }
370 if (p2 != p1) {
371 return (p2 - p1).Normalize();
372 }
373 return std::nullopt;
374}
375
377 const LinearPathComponent* component) {
378 if (!component) {
379 return std::nullopt;
380 }
381 return component->GetStartDirection();
382}
383
385 const QuadraticPathComponent* component) {
386 if (!component) {
387 return std::nullopt;
388 }
389 return component->GetStartDirection();
390}
391
393 const CubicPathComponent* component) {
394 if (!component) {
395 return std::nullopt;
396 }
397 return component->GetStartDirection();
398}
399
401 const LinearPathComponent* component) {
402 if (!component) {
403 return std::nullopt;
404 }
405 return component->GetEndDirection();
406}
407
409 const QuadraticPathComponent* component) {
410 if (!component) {
411 return std::nullopt;
412 }
413 return component->GetEndDirection();
414}
415
417 const CubicPathComponent* component) {
418 if (!component) {
419 return std::nullopt;
420 }
421 return component->GetEndDirection();
422}
423
424} // namespace impeller
static const int points[]
An interface for generating a multi contour polyline as a triangle strip.
VertexWriter(std::vector< Point > &points, std::vector< uint16_t > &indices)
void Write(Point point)
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
struct MyStruct a[10]
glong glong end
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)
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
static double time(int loops, Benchmark *bench, Target *target)
Definition: nanobench.cpp:394
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