Flutter Impeller
impeller::Path Class Reference

Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments. These segments may be broken up by move commands, which are effectively linear commands that pick up the pen rather than continuing to draw. More...

#include <path.h>

Classes

struct  Polyline
 
struct  PolylineContour
 

Public Types

enum  ComponentType {
  ComponentType::kLinear,
  ComponentType::kQuadratic,
  ComponentType::kCubic,
  ComponentType::kContour
}
 
template<class T >
using Applier = std::function< void(size_t index, const T &component)>
 

Public Member Functions

 Path ()
 
 ~Path ()
 
size_t GetComponentCount (std::optional< ComponentType > type={}) const
 
FillType GetFillType () const
 
bool IsConvex () const
 
void EnumerateComponents (const Applier< LinearPathComponent > &linear_applier, const Applier< QuadraticPathComponent > &quad_applier, const Applier< CubicPathComponent > &cubic_applier, const Applier< ContourComponent > &contour_applier) const
 
bool GetLinearComponentAtIndex (size_t index, LinearPathComponent &linear) const
 
bool GetQuadraticComponentAtIndex (size_t index, QuadraticPathComponent &quadratic) const
 
bool GetCubicComponentAtIndex (size_t index, CubicPathComponent &cubic) const
 
bool GetContourComponentAtIndex (size_t index, ContourComponent &contour) const
 
Polyline CreatePolyline (Scalar scale) const
 
std::optional< RectGetBoundingBox () const
 
std::optional< RectGetTransformedBoundingBox (const Matrix &transform) const
 
std::optional< std::pair< Point, Point > > GetMinMaxCoveragePoints () const
 

Friends

class PathBuilder
 

Detailed Description

Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments. These segments may be broken up by move commands, which are effectively linear commands that pick up the pen rather than continuing to draw.

All shapes supported by Impeller are paths either directly or via approximation (in the case of circles).

Paths are externally immutable once created, Creating paths must be done using a path builder.

Definition at line 54 of file path.h.

Member Typedef Documentation

◆ Applier

template<class T >
using impeller::Path::Applier = std::function<void(size_t index, const T& component)>

Definition at line 103 of file path.h.

Member Enumeration Documentation

◆ ComponentType

Enumerator
kLinear 
kQuadratic 
kCubic 
kContour 

Definition at line 56 of file path.h.

56  {
57  kLinear,
58  kQuadratic,
59  kCubic,
60  kContour,
61  };

Constructor & Destructor Documentation

◆ Path()

impeller::Path::Path ( )

Definition at line 14 of file path.cc.

14  {
15  AddContourComponent({});
16 };

◆ ~Path()

impeller::Path::~Path ( )
default

Member Function Documentation

◆ CreatePolyline()

Path::Polyline impeller::Path::CreatePolyline ( Scalar  scale) const

Callers must provide the scale factor for how this path will be transformed.

It is suitable to use the max basis length of the matrix used to transform the path. If the provided scale is 0, curves will revert to lines.

Definition at line 272 of file path.cc.

272  {
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) {
300  return &linears_[component.index];
302  return &quads_[component.index];
304  return &cubics_[component.index];
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) {
366  collect_points(linears_[component.index].CreatePolyline());
367  previous_path_component_index = component_i;
368  break;
370  collect_points(quads_[component.index].CreatePolyline(scale));
371  previous_path_component_index = component_i;
372  break;
374  collect_points(cubics_[component.index].CreatePolyline(scale));
375  previous_path_component_index = component_i;
376  break;
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 }

References impeller::Path::Polyline::contours, kContour, kCubic, kLinear, kQuadratic, and impeller::Path::Polyline::points.

Referenced by impeller::Tessellate(), and impeller::testing::TEST().

◆ EnumerateComponents()

void impeller::Path::EnumerateComponents ( const Applier< LinearPathComponent > &  linear_applier,
const Applier< QuadraticPathComponent > &  quad_applier,
const Applier< CubicPathComponent > &  cubic_applier,
const Applier< ContourComponent > &  contour_applier 
) const

Definition at line 125 of file path.cc.

129  {
130  size_t currentIndex = 0;
131  for (const auto& component : components_) {
132  switch (component.type) {
134  if (linear_applier) {
135  linear_applier(currentIndex, linears_[component.index]);
136  }
137  break;
139  if (quad_applier) {
140  quad_applier(currentIndex, quads_[component.index]);
141  }
142  break;
144  if (cubic_applier) {
145  cubic_applier(currentIndex, cubics_[component.index]);
146  }
147  break;
149  if (contour_applier) {
150  contour_applier(currentIndex, contours_[component.index]);
151  }
152  break;
153  }
154  currentIndex++;
155  }
156 }

References kContour, kCubic, kLinear, and kQuadratic.

Referenced by impeller::PathBuilder::AddPath(), and impeller::ComputeTessellator::Tessellate().

◆ GetBoundingBox()

std::optional< Rect > impeller::Path::GetBoundingBox ( ) const

Definition at line 399 of file path.cc.

399  {
400  return computed_bounds_;
401 }

Referenced by impeller::Canvas::ClipPath(), GetTransformedBoundingBox(), and impeller::testing::TEST().

◆ GetComponentCount()

size_t impeller::Path::GetComponentCount ( std::optional< ComponentType type = {}) const

Definition at line 32 of file path.cc.

32  {
33  if (type.has_value()) {
34  switch (type.value()) {
36  return linears_.size();
38  return quads_.size();
40  return cubics_.size();
42  return contours_.size();
43  }
44  }
45  return components_.size();
46 }

References kContour, kCubic, kLinear, and kQuadratic.

Referenced by impeller::ComputeTessellator::Tessellate().

◆ GetContourComponentAtIndex()

bool impeller::Path::GetContourComponentAtIndex ( size_t  index,
ContourComponent contour 
) const

Definition at line 201 of file path.cc.

202  {
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 }

References kContour.

Referenced by impeller::testing::TEST().

◆ GetCubicComponentAtIndex()

bool impeller::Path::GetCubicComponentAtIndex ( size_t  index,
CubicPathComponent cubic 
) const

Definition at line 187 of file path.cc.

188  {
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 }

References kCubic.

◆ GetFillType()

FillType impeller::Path::GetFillType ( ) const

Definition at line 52 of file path.cc.

52  {
53  return fill_;
54 }

◆ GetLinearComponentAtIndex()

bool impeller::Path::GetLinearComponentAtIndex ( size_t  index,
LinearPathComponent linear 
) const

Definition at line 158 of file path.cc.

159  {
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 }

References kLinear.

◆ GetMinMaxCoveragePoints()

std::optional< std::pair< Point, Point > > impeller::Path::GetMinMaxCoveragePoints ( ) const

Definition at line 424 of file path.cc.

424  {
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 }

References impeller::LinearPathComponent::p1, and impeller::LinearPathComponent::p2.

◆ GetQuadraticComponentAtIndex()

bool impeller::Path::GetQuadraticComponentAtIndex ( size_t  index,
QuadraticPathComponent quadratic 
) const

Definition at line 172 of file path.cc.

174  {
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 }

References kQuadratic.

◆ GetTransformedBoundingBox()

std::optional< Rect > impeller::Path::GetTransformedBoundingBox ( const Matrix transform) const

Definition at line 415 of file path.cc.

416  {
417  auto bounds = GetBoundingBox();
418  if (!bounds.has_value()) {
419  return std::nullopt;
420  }
421  return bounds->TransformBounds(transform);
422 }

References GetBoundingBox().

◆ IsConvex()

bool impeller::Path::IsConvex ( ) const

Definition at line 56 of file path.cc.

56  {
57  return convexity_ == Convexity::kConvex;
58 }

References impeller::kConvex.

Friends And Related Function Documentation

◆ PathBuilder

friend class PathBuilder
friend

Definition at line 135 of file path.h.


The documentation for this class was generated from the following files:
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:399
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
impeller::MinMagFilter::kLinear
@ kLinear
impeller::PathComponentVariant
std::variant< std::monostate, const LinearPathComponent *, const QuadraticPathComponent *, const CubicPathComponent * > PathComponentVariant
Definition: path_component.h:154
impeller::Convexity::kConvex
@ kConvex
impeller::Path::ComponentType::kContour
@ kContour