| Flutter Impeller |

path.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 "impeller/geometry/path.h"
6
7 #include <optional>
8 #include <variant>
9
10 #include "impeller/geometry/path_component.h"
11
12 namespace impeller {
13
14 Path::Path() {
15 AddContourComponent({});
16 };
17
18 Path::~Path() = default;
19
20 std::tuple<size_t, size_t> Path::Polyline::GetContourPointBounds(
21 size_t contour_index) const {
22 if (contour_index >= contours.size()) {
23 return {points.size(), points.size()};
24 }
25 const size_t start_index = contours.at(contour_index).start_index;
26 const size_t end_index = (contour_index >= contours.size() - 1)
27 ? points.size()
28 : contours.at(contour_index + 1).start_index;
29 return std::make_tuple(start_index, end_index);
30 }
31
32 size_t Path::GetComponentCount(std::optional<ComponentType> type) const {
33 if (type.has_value()) {
34 switch (type.value()) {
35 case ComponentType::kLinear:
36 return linears_.size();
37 case ComponentType::kQuadratic:
38 return quads_.size();
39 case ComponentType::kCubic:
40 return cubics_.size();
41 case ComponentType::kContour:
42 return contours_.size();
43 }
44 }
45 return components_.size();
46 }
47
48 void Path::SetFillType(FillType fill) {
49 fill_ = fill;
50 }
51
52 FillType Path::GetFillType() const {
53 return fill_;
54 }
55
56 bool Path::IsConvex() const {
57 return convexity_ == Convexity::kConvex;
58 }
59
60 void Path::SetConvexity(Convexity value) {
61 convexity_ = value;
62 }
63
64 void Path::Shift(Point shift) {
65 size_t currentIndex = 0;
66 for (const auto& component : components_) {
67 switch (component.type) {
68 case ComponentType::kLinear:
69 linears_[component.index].p1 += shift;
70 linears_[component.index].p2 += shift;
71 break;
72 case ComponentType::kQuadratic:
73 quads_[component.index].cp += shift;
74 quads_[component.index].p1 += shift;
75 quads_[component.index].p2 += shift;
76 break;
77 case ComponentType::kCubic:
78 cubics_[component.index].cp1 += shift;
79 cubics_[component.index].cp2 += shift;
80 cubics_[component.index].p1 += shift;
81 cubics_[component.index].p2 += shift;
82 break;
83 case ComponentType::kContour:
84 contours_[component.index].destination += shift;
85 break;
86 }
87 currentIndex++;
88 }
89 }
90
91 Path& Path::AddLinearComponent(Point p1, Point p2) {
92 linears_.emplace_back(p1, p2);
93 components_.emplace_back(ComponentType::kLinear, linears_.size() - 1);
94 return *this;
95 }
96
97 Path& Path::AddQuadraticComponent(Point p1, Point cp, Point p2) {
98 quads_.emplace_back(p1, cp, p2);
99 components_.emplace_back(ComponentType::kQuadratic, quads_.size() - 1);
100 return *this;
101 }
102
103 Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) {
104 cubics_.emplace_back(p1, cp1, cp2, p2);
105 components_.emplace_back(ComponentType::kCubic, cubics_.size() - 1);
106 return *this;
107 }
108
109 Path& Path::AddContourComponent(Point destination, bool is_closed) {
110 if (components_.size() > 0 &&
111 components_.back().type == ComponentType::kContour) {
112 // Never insert contiguous contours.
113 contours_.back() = ContourComponent(destination, is_closed);
114 } else {
115 contours_.emplace_back(ContourComponent(destination, is_closed));
116 components_.emplace_back(ComponentType::kContour, contours_.size() - 1);
117 }
118 return *this;
119 }
120
121 void Path::SetContourClosed(bool is_closed) {
122 contours_.back().is_closed = is_closed;
123 }
124
125 void Path::EnumerateComponents(
126 const Applier<LinearPathComponent>& linear_applier,
127 const Applier<QuadraticPathComponent>& quad_applier,
128 const Applier<CubicPathComponent>& cubic_applier,
129 const Applier<ContourComponent>& contour_applier) const {
130 size_t currentIndex = 0;
131 for (const auto& component : components_) {
132 switch (component.type) {
133 case ComponentType::kLinear:
134 if (linear_applier) {
135 linear_applier(currentIndex, linears_[component.index]);
136 }
137 break;
138 case ComponentType::kQuadratic:
139 if (quad_applier) {
140 quad_applier(currentIndex, quads_[component.index]);
141 }
142 break;
143 case ComponentType::kCubic:
144 if (cubic_applier) {
145 cubic_applier(currentIndex, cubics_[component.index]);
146 }
147 break;
148 case ComponentType::kContour:
149 if (contour_applier) {
150 contour_applier(currentIndex, contours_[component.index]);
151 }
152 break;
153 }
154 currentIndex++;
155 }
156 }
157
158 bool Path::GetLinearComponentAtIndex(size_t index,
159 LinearPathComponent& linear) const {
160 if (index >= components_.size()) {
161 return false;
162 }
163
164 if (components_[index].type != ComponentType::kLinear) {
165 return false;
166 }
167
168 linear = linears_[components_[index].index];
169 return true;
170 }
171
172 bool Path::GetQuadraticComponentAtIndex(
173 size_t index,
174 QuadraticPathComponent& quadratic) const {
175 if (index >= components_.size()) {
176 return false;
177 }
178
179 if (components_[index].type != ComponentType::kQuadratic) {
180 return false;
181 }
182
183 quadratic = quads_[components_[index].index];
184 return true;
185 }
186
187 bool Path::GetCubicComponentAtIndex(size_t index,
188 CubicPathComponent& cubic) const {
189 if (index >= components_.size()) {
190 return false;
191 }
192
193 if (components_[index].type != ComponentType::kCubic) {
194 return false;
195 }
196
197 cubic = cubics_[components_[index].index];
198 return true;
199 }
200
201 bool Path::GetContourComponentAtIndex(size_t index,
202 ContourComponent& move) const {
203 if (index >= components_.size()) {
204 return false;
205 }
206
207 if (components_[index].type != ComponentType::kContour) {
208 return false;
209 }
210
211 move = contours_[components_[index].index];
212 return true;
213 }
214
215 bool Path::UpdateLinearComponentAtIndex(size_t index,
216 const LinearPathComponent& linear) {
217 if (index >= components_.size()) {
218 return false;
219 }
220
221 if (components_[index].type != ComponentType::kLinear) {
222 return false;
223 }
224
225 linears_[components_[index].index] = linear;
226 return true;
227 }
228
229 bool Path::UpdateQuadraticComponentAtIndex(
230 size_t index,
231 const QuadraticPathComponent& quadratic) {
232 if (index >= components_.size()) {
233 return false;
234 }
235
236 if (components_[index].type != ComponentType::kQuadratic) {
237 return false;
238 }
239
240 quads_[components_[index].index] = quadratic;
241 return true;
242 }
243
244 bool Path::UpdateCubicComponentAtIndex(size_t index,
245 CubicPathComponent& cubic) {
246 if (index >= components_.size()) {
247 return false;
248 }
249
250 if (components_[index].type != ComponentType::kCubic) {
251 return false;
252 }
253
254 cubics_[components_[index].index] = cubic;
255 return true;
256 }
257
258 bool Path::UpdateContourComponentAtIndex(size_t index,
259 const ContourComponent& move) {
260 if (index >= components_.size()) {
261 return false;
262 }
263
264 if (components_[index].type != ComponentType::kContour) {
265 return false;
266 }
267
268 contours_[components_[index].index] = move;
269 return true;
270 }
271
272 Path::Polyline Path::CreatePolyline(Scalar scale) const {
273 Polyline polyline;
274
275 std::optional<Point> previous_contour_point;
276 auto collect_points = [&polyline, &previous_contour_point](
277 const std::vector<Point>& collection) {
278 if (collection.empty()) {
279 return;
280 }
281
282 for (const auto& point : collection) {
283 if (previous_contour_point.has_value() &&
284 previous_contour_point.value() == point) {
285 // Skip over duplicate points in the same contour.
286 continue;
287 }
288 previous_contour_point = point;
289 polyline.points.push_back(point);
290 }
291 };
292
293 auto get_path_component = [this](size_t component_i) -> PathComponentVariant {
294 if (component_i >= components_.size()) {
295 return std::monostate{};
296 }
297 const auto& component = components_[component_i];
298 switch (component.type) {
299 case ComponentType::kLinear:
300 return &linears_[component.index];
301 case ComponentType::kQuadratic:
302 return &quads_[component.index];
303 case ComponentType::kCubic:
304 return &cubics_[component.index];
305 case ComponentType::kContour:
306 return std::monostate{};
307 }
308 };
309
310 auto compute_contour_start_direction =
311 [&get_path_component](size_t current_path_component_index) {
312 size_t next_component_index = current_path_component_index + 1;
313 while (!std::holds_alternative<std::monostate>(
314 get_path_component(next_component_index))) {
315 auto next_component = get_path_component(next_component_index);
316 auto maybe_vector =
317 std::visit(PathComponentStartDirectionVisitor(), next_component);
318 if (maybe_vector.has_value()) {
319 return maybe_vector.value();
320 } else {
321 next_component_index++;
322 }
323 }
324 return Vector2(0, -1);
325 };
326
327 std::optional<size_t> previous_path_component_index;
328 auto end_contour = [&polyline, &previous_path_component_index,
329 &get_path_component]() {
330 // Whenever a contour has ended, extract the exact end direction from the
331 // last component.
332 if (polyline.contours.empty()) {
333 return;
334 }
335
336 if (!previous_path_component_index.has_value()) {
337 return;
338 }
339
340 auto& contour = polyline.contours.back();
341 contour.end_direction = Vector2(0, 1);
342
343 size_t previous_index = previous_path_component_index.value();
344 while (!std::holds_alternative<std::monostate>(
345 get_path_component(previous_index))) {
346 auto previous_component = get_path_component(previous_index);
347 auto maybe_vector =
348 std::visit(PathComponentEndDirectionVisitor(), previous_component);
349 if (maybe_vector.has_value()) {
350 contour.end_direction = maybe_vector.value();
351 break;
352 } else {
353 if (previous_index == 0) {
354 break;
355 }
356 previous_index--;
357 }
358 }
359 };
360
361 for (size_t component_i = 0; component_i < components_.size();
362 component_i++) {
363 const auto& component = components_[component_i];
364 switch (component.type) {
365 case ComponentType::kLinear:
366 collect_points(linears_[component.index].CreatePolyline());
367 previous_path_component_index = component_i;
368 break;
369 case ComponentType::kQuadratic:
370 collect_points(quads_[component.index].CreatePolyline(scale));
371 previous_path_component_index = component_i;
372 break;
373 case ComponentType::kCubic:
374 collect_points(cubics_[component.index].CreatePolyline(scale));
375 previous_path_component_index = component_i;
376 break;
377 case ComponentType::kContour:
378 if (component_i == components_.size() - 1) {
379 // If the last component is a contour, that means it's an empty
380 // contour, so skip it.
381 continue;
382 }
383 end_contour();
384
385 Vector2 start_direction = compute_contour_start_direction(component_i);
386 const auto& contour = contours_[component.index];
387 polyline.contours.push_back({.start_index = polyline.points.size(),
388 .is_closed = contour.is_closed,
389 .start_direction = start_direction});
390 previous_contour_point = std::nullopt;
391 collect_points({contour.destination});
392 break;
393 }
394 end_contour();
395 }
396 return polyline;
397 }
398
399 std::optional<Rect> Path::GetBoundingBox() const {
400 return computed_bounds_;
401 }
402
403 void Path::ComputeBounds() {
404 auto min_max = GetMinMaxCoveragePoints();
405 if (!min_max.has_value()) {
406 computed_bounds_ = std::nullopt;
407 return;
408 }
409 auto min = min_max->first;
410 auto max = min_max->second;
411 const auto difference = max - min;
412 computed_bounds_ = Rect{min.x, min.y, difference.x, difference.y};
413 }
414
415 std::optional<Rect> Path::GetTransformedBoundingBox(
416 const Matrix& transform) const {
417 auto bounds = GetBoundingBox();
418 if (!bounds.has_value()) {
419 return std::nullopt;
420 }
421 return bounds->TransformBounds(transform);
422 }
423
424 std::optional<std::pair<Point, Point>> Path::GetMinMaxCoveragePoints() const {
425 if (linears_.empty() && quads_.empty() && cubics_.empty()) {
426 return std::nullopt;
427 }
428
429 std::optional<Point> min, max;
430
431 auto clamp = [&min, &max](const Point& point) {
432 if (min.has_value()) {
433 min = min->Min(point);
434 } else {
435 min = point;
436 }
437
438 if (max.has_value()) {
439 max = max->Max(point);
440 } else {
441 max = point;
442 }
443 };
444
445 for (const auto& linear : linears_) {
446 clamp(linear.p1);
447 clamp(linear.p2);
448 }
449
450 for (const auto& quad : quads_) {
451 for (const Point& point : quad.Extrema()) {
452 clamp(point);
453 }
454 }
455
456 for (const auto& cubic : cubics_) {
457 for (const Point& point : cubic.Extrema()) {
458 clamp(point);
459 }
460 }
461
462 if (!min.has_value() || !max.has_value()) {
463 return std::nullopt;
464 }
465
466 return std::make_pair(min.value(), max.value());
467 }
468
469 void Path::SetBounds(Rect rect) {
470 computed_bounds_ = rect;
471 }
472
473 } // namespace impeller
path.h
impeller::LinearPathComponent
Definition: path_component.h:27
impeller::Path::GetQuadraticComponentAtIndex
bool GetQuadraticComponentAtIndex(size_t index, QuadraticPathComponent &quadratic) const
Definition: path.cc:172
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:29
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::PathComponentEndDirectionVisitor
Definition: path_component.h:165
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:399
impeller::Convexity
Convexity
Definition: path.h:37
impeller::Path::GetMinMaxCoveragePoints
std::optional< std::pair< Point, Point > > GetMinMaxCoveragePoints() const
Definition: path.cc:424
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Path::~Path
~Path()
impeller::Path::Polyline
Definition: path.h:78
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
impeller::Path::GetCubicComponentAtIndex
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent &cubic) const
Definition: path.cc:187
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:28
impeller::Path::EnumerateComponents
void EnumerateComponents(const Applier< LinearPathComponent > &linear_applier, const Applier< QuadraticPathComponent > &quad_applier, const Applier< CubicPathComponent > &cubic_applier, const Applier< ContourComponent > &contour_applier) const
Definition: path.cc:125
impeller::Path::GetTransformedBoundingBox
std::optional< Rect > GetTransformedBoundingBox(const Matrix &transform) const
Definition: path.cc:415
impeller::Path::Polyline::points
std::vector< Point > points
Definition: path.h:81
impeller::Path::Polyline::GetContourPointBounds
std::tuple< size_t, size_t > GetContourPointBounds(size_t contour_index) const
Definition: path.cc:20
impeller::FillType
FillType
Definition: path.h:29
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
impeller::PathComponentStartDirectionVisitor
Definition: path_component.h:156
impeller::Path::Path
Path()
Definition: path.cc:14
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:201
impeller::CubicPathComponent
Definition: path_component.h:90
impeller::Path::Applier
std::function< void(size_t index, const T &component)> Applier
Definition: path.h:103
impeller::TPoint< Scalar >
impeller::Path::GetComponentCount
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:32
impeller::Path::Polyline::contours
std::vector< PolylineContour > contours
Definition: path.h:82
impeller::Path::IsConvex
bool IsConvex() const
Definition: path.cc:56
impeller::PathComponentVariant
std::variant< std::monostate, const LinearPathComponent *, const QuadraticPathComponent *, const CubicPathComponent * > PathComponentVariant
Definition: path_component.h:154
path_component.h
impeller::ContourComponent
Definition: path_component.h:137
impeller::Convexity::kConvex
@ kConvex
impeller
Definition: aiks_context.cc:10
impeller::QuadraticPathComponent
Definition: path_component.h:50
impeller::Path::GetLinearComponentAtIndex
bool GetLinearComponentAtIndex(size_t index, LinearPathComponent &linear) const
Definition: path.cc:158
impeller::Path::ComponentType::kContour
@ kContour
impeller::TRect
Definition: rect.h:20
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:36
impeller::Path::GetFillType
FillType GetFillType() const
Definition: path.cc:52
impeller::Path::CreatePolyline
Polyline CreatePolyline(Scalar scale) const
Definition: path.cc:272