Flutter Impeller
path_builder.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_builder.h"
6 
7 #include <cmath>
8 
9 namespace impeller {
10 
11 PathBuilder::PathBuilder() = default;
12 
13 PathBuilder::~PathBuilder() = default;
14 
16  auto path = prototype_;
17  path.SetFillType(fill);
18  return path;
19 }
20 
22  auto path = prototype_;
23  path.SetFillType(fill);
24  path.SetConvexity(convexity_);
25  if (!did_compute_bounds_) {
26  path.ComputeBounds();
27  }
28  did_compute_bounds_ = false;
29  return path;
30 }
31 
32 PathBuilder& PathBuilder::MoveTo(Point point, bool relative) {
33  current_ = relative ? current_ + point : point;
34  subpath_start_ = current_;
35  prototype_.AddContourComponent(current_);
36  return *this;
37 }
38 
40  LineTo(subpath_start_);
41  prototype_.SetContourClosed(true);
42  prototype_.AddContourComponent(current_);
43  return *this;
44 }
45 
46 PathBuilder& PathBuilder::LineTo(Point point, bool relative) {
47  point = relative ? current_ + point : point;
48  prototype_.AddLinearComponent(current_, point);
49  current_ = point;
50  return *this;
51 }
52 
54  Point endpoint =
55  relative ? Point{current_.x + x, current_.y} : Point{x, current_.y};
56  prototype_.AddLinearComponent(current_, endpoint);
57  current_ = endpoint;
58  return *this;
59 }
60 
62  Point endpoint =
63  relative ? Point{current_.x, current_.y + y} : Point{current_.x, y};
64  prototype_.AddLinearComponent(current_, endpoint);
65  current_ = endpoint;
66  return *this;
67 }
68 
70  Point point,
71  bool relative) {
72  point = relative ? current_ + point : point;
73  controlPoint = relative ? current_ + controlPoint : controlPoint;
74  prototype_.AddQuadraticComponent(current_, controlPoint, point);
75  current_ = point;
76  return *this;
77 }
78 
79 Point PathBuilder::ReflectedQuadraticControlPoint1() const {
80  /*
81  * If there is no previous command or if the previous command was not a
82  * quadratic, assume the control point is coincident with the current point.
83  */
84  if (prototype_.GetComponentCount() == 0) {
85  return current_;
86  }
87 
88  QuadraticPathComponent quad;
89  if (!prototype_.GetQuadraticComponentAtIndex(
90  prototype_.GetComponentCount() - 1, quad)) {
91  return current_;
92  }
93 
94  /*
95  * The control point is assumed to be the reflection of the control point on
96  * the previous command relative to the current point.
97  */
98  return (current_ * 2.0) - quad.cp;
99 }
100 
102  point = relative ? current_ + point : point;
103  /*
104  * The reflected control point is absolute and we made the endpoint absolute
105  * too. So there the last argument is always false (i.e, not relative).
106  */
107  QuadraticCurveTo(point, ReflectedQuadraticControlPoint1(), false);
108  return *this;
109 }
110 
112  convexity_ = value;
113  return *this;
114 }
115 
117  Point controlPoint2,
118  Point point,
119  bool relative) {
120  controlPoint1 = relative ? current_ + controlPoint1 : controlPoint1;
121  controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2;
122  point = relative ? current_ + point : point;
123  prototype_.AddCubicComponent(current_, controlPoint1, controlPoint2, point);
124  current_ = point;
125  return *this;
126 }
127 
128 Point PathBuilder::ReflectedCubicControlPoint1() const {
129  /*
130  * If there is no previous command or if the previous command was not a
131  * cubic, assume the first control point is coincident with the current
132  * point.
133  */
134  if (prototype_.GetComponentCount() == 0) {
135  return current_;
136  }
137 
138  CubicPathComponent cubic;
139  if (!prototype_.GetCubicComponentAtIndex(prototype_.GetComponentCount() - 1,
140  cubic)) {
141  return current_;
142  }
143 
144  /*
145  * The first control point is assumed to be the reflection of the second
146  * control point on the previous command relative to the current point.
147  */
148  return (current_ * 2.0) - cubic.cp2;
149 }
150 
152  Point point,
153  bool relative) {
154  auto controlPoint1 = ReflectedCubicControlPoint1();
155  controlPoint2 = relative ? current_ + controlPoint2 : controlPoint2;
156  auto endpoint = relative ? current_ + point : point;
157 
158  CubicCurveTo(endpoint, // endpoint
159  controlPoint1, // control point 1
160  controlPoint2, // control point 2
161  false // relative since all points are already absolute
162  );
163  return *this;
164 }
165 
167  MoveTo(p1);
168  prototype_.AddQuadraticComponent(p1, cp, p2);
169  return *this;
170 }
171 
173  Point cp1,
174  Point cp2,
175  Point p2) {
176  MoveTo(p1);
177  prototype_.AddCubicComponent(p1, cp1, cp2, p2);
178  return *this;
179 }
180 
182  current_ = rect.origin;
183 
184  auto tl = rect.origin;
185  auto bl = rect.origin + Point{0.0, rect.size.height};
186  auto br = rect.origin + Point{rect.size.width, rect.size.height};
187  auto tr = rect.origin + Point{rect.size.width, 0.0};
188 
189  MoveTo(tl);
190  prototype_.AddLinearComponent(tl, tr)
191  .AddLinearComponent(tr, br)
192  .AddLinearComponent(br, bl);
193  Close();
194 
195  return *this;
196 }
197 
199  return AddOval(Rect{c.x - r, c.y - r, 2.0f * r, 2.0f * r});
200 }
201 
203  return radius <= 0.0 ? AddRect(rect)
204  : AddRoundedRect(rect, {radius, radius, radius, radius});
205 }
206 
208  if (radii.AreAllZero()) {
209  return AddRect(rect);
210  }
211 
212  current_ = rect.origin + Point{radii.top_left.x, 0.0};
213 
214  MoveTo({rect.origin.x + radii.top_left.x, rect.origin.y});
215 
216  //----------------------------------------------------------------------------
217  // Top line.
218  //
219  prototype_.AddLinearComponent(
220  {rect.origin.x + radii.top_left.x, rect.origin.y},
221  {rect.origin.x + rect.size.width - radii.top_right.x, rect.origin.y});
222 
223  //----------------------------------------------------------------------------
224  // Top right arc.
225  //
226  AddRoundedRectTopRight(rect, radii);
227 
228  //----------------------------------------------------------------------------
229  // Right line.
230  //
231  prototype_.AddLinearComponent(
232  {rect.origin.x + rect.size.width, rect.origin.y + radii.top_right.y},
233  {rect.origin.x + rect.size.width,
234  rect.origin.y + rect.size.height - radii.bottom_right.y});
235 
236  //----------------------------------------------------------------------------
237  // Bottom right arc.
238  //
239  AddRoundedRectBottomRight(rect, radii);
240 
241  //----------------------------------------------------------------------------
242  // Bottom line.
243  //
244  prototype_.AddLinearComponent(
245  {rect.origin.x + rect.size.width - radii.bottom_right.x,
246  rect.origin.y + rect.size.height},
247  {rect.origin.x + radii.bottom_left.x, rect.origin.y + rect.size.height});
248 
249  //----------------------------------------------------------------------------
250  // Bottom left arc.
251  //
252  AddRoundedRectBottomLeft(rect, radii);
253 
254  //----------------------------------------------------------------------------
255  // Left line.
256  //
257  prototype_.AddLinearComponent(
258  {rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y},
259  {rect.origin.x, rect.origin.y + radii.top_left.y});
260 
261  //----------------------------------------------------------------------------
262  // Top left arc.
263  //
264  AddRoundedRectTopLeft(rect, radii);
265 
266  Close();
267 
268  return *this;
269 }
270 
271 PathBuilder& PathBuilder::AddRoundedRectTopLeft(Rect rect,
272  RoundingRadii radii) {
273  const auto magic_top_left = radii.top_left * kArcApproximationMagic;
274  prototype_.AddCubicComponent(
275  {rect.origin.x, rect.origin.y + radii.top_left.y},
276  {rect.origin.x, rect.origin.y + radii.top_left.y - magic_top_left.y},
277  {rect.origin.x + radii.top_left.x - magic_top_left.x, rect.origin.y},
278  {rect.origin.x + radii.top_left.x, rect.origin.y});
279  return *this;
280 }
281 
282 PathBuilder& PathBuilder::AddRoundedRectTopRight(Rect rect,
283  RoundingRadii radii) {
284  const auto magic_top_right = radii.top_right * kArcApproximationMagic;
285  prototype_.AddCubicComponent(
286  {rect.origin.x + rect.size.width - radii.top_right.x, rect.origin.y},
287  {rect.origin.x + rect.size.width - radii.top_right.x + magic_top_right.x,
288  rect.origin.y},
289  {rect.origin.x + rect.size.width,
290  rect.origin.y + radii.top_right.y - magic_top_right.y},
291  {rect.origin.x + rect.size.width, rect.origin.y + radii.top_right.y});
292  return *this;
293 }
294 
295 PathBuilder& PathBuilder::AddRoundedRectBottomRight(Rect rect,
296  RoundingRadii radii) {
297  const auto magic_bottom_right = radii.bottom_right * kArcApproximationMagic;
298  prototype_.AddCubicComponent(
299  {rect.origin.x + rect.size.width,
300  rect.origin.y + rect.size.height - radii.bottom_right.y},
301  {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height -
302  radii.bottom_right.y +
303  magic_bottom_right.y},
304  {rect.origin.x + rect.size.width - radii.bottom_right.x +
305  magic_bottom_right.x,
306  rect.origin.y + rect.size.height},
307  {rect.origin.x + rect.size.width - radii.bottom_right.x,
308  rect.origin.y + rect.size.height});
309  return *this;
310 }
311 
312 PathBuilder& PathBuilder::AddRoundedRectBottomLeft(Rect rect,
313  RoundingRadii radii) {
314  const auto magic_bottom_left = radii.bottom_left * kArcApproximationMagic;
315  prototype_.AddCubicComponent(
316  {rect.origin.x + radii.bottom_left.x, rect.origin.y + rect.size.height},
317  {rect.origin.x + radii.bottom_left.x - magic_bottom_left.x,
318  rect.origin.y + rect.size.height},
319  {rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y +
320  magic_bottom_left.y},
321  {rect.origin.x, rect.origin.y + rect.size.height - radii.bottom_left.y});
322  return *this;
323 }
324 
326  Radians start,
327  Radians sweep,
328  bool use_center) {
329  if (sweep.radians < 0) {
330  start.radians += sweep.radians;
331  sweep.radians *= -1;
332  }
333  sweep.radians = std::min(k2Pi, sweep.radians);
334  start.radians = std::fmod(start.radians, k2Pi);
335 
336  const Point radius = {oval_bounds.size.width * 0.5f,
337  oval_bounds.size.height * 0.5f};
338  const Point center = {oval_bounds.origin.x + radius.x,
339  oval_bounds.origin.y + radius.y};
340 
341  Vector2 p1_unit(std::cos(start.radians), std::sin(start.radians));
342 
343  if (use_center) {
344  MoveTo(center);
345  LineTo(center + p1_unit * radius);
346  } else {
347  MoveTo(center + p1_unit * radius);
348  }
349 
350  while (sweep.radians > 0) {
351  Vector2 p2_unit;
352  Scalar quadrant_angle;
353  if (sweep.radians < kPiOver2) {
354  quadrant_angle = sweep.radians;
355  p2_unit = Vector2(std::cos(start.radians + quadrant_angle),
356  std::sin(start.radians + quadrant_angle));
357  } else {
358  quadrant_angle = kPiOver2;
359  p2_unit = Vector2(-p1_unit.y, p1_unit.x);
360  }
361 
362  Vector2 arc_cp_lengths =
363  (quadrant_angle / kPiOver2) * kArcApproximationMagic * radius;
364 
365  Point p1 = center + p1_unit * radius;
366  Point p2 = center + p2_unit * radius;
367  Point cp1 = p1 + Vector2(-p1_unit.y, p1_unit.x) * arc_cp_lengths;
368  Point cp2 = p2 + Vector2(p2_unit.y, -p2_unit.x) * arc_cp_lengths;
369 
370  prototype_.AddCubicComponent(p1, cp1, cp2, p2);
371  current_ = p2;
372 
373  start.radians += quadrant_angle;
374  sweep.radians -= quadrant_angle;
375  p1_unit = p2_unit;
376  }
377 
378  if (use_center) {
379  Close();
380  }
381 
382  return *this;
383 }
384 
386  const Point r = {container.size.width * 0.5f, container.size.height * 0.5f};
387  const Point c = {container.origin.x + r.x, container.origin.y + r.y};
389 
390  MoveTo({c.x, c.y - r.y});
391 
392  //----------------------------------------------------------------------------
393  // Top right arc.
394  //
395  prototype_.AddCubicComponent({c.x, c.y - r.y}, // p1
396  {c.x + m.x, c.y - r.y}, // cp1
397  {c.x + r.x, c.y - m.y}, // cp2
398  {c.x + r.x, c.y} // p2
399  );
400 
401  //----------------------------------------------------------------------------
402  // Bottom right arc.
403  //
404  prototype_.AddCubicComponent({c.x + r.x, c.y}, // p1
405  {c.x + r.x, c.y + m.y}, // cp1
406  {c.x + m.x, c.y + r.y}, // cp2
407  {c.x, c.y + r.y} // p2
408  );
409 
410  //----------------------------------------------------------------------------
411  // Bottom left arc.
412  //
413  prototype_.AddCubicComponent({c.x, c.y + r.y}, // p1
414  {c.x - m.x, c.y + r.y}, // cp1
415  {c.x - r.x, c.y + m.y}, // cp2
416  {c.x - r.x, c.y} // p2
417  );
418 
419  //----------------------------------------------------------------------------
420  // Top left arc.
421  //
422  prototype_.AddCubicComponent({c.x - r.x, c.y}, // p1
423  {c.x - r.x, c.y - m.y}, // cp1
424  {c.x - m.x, c.y - r.y}, // cp2
425  {c.x, c.y - r.y} // p2
426  );
427 
428  Close();
429 
430  return *this;
431 }
432 
433 PathBuilder& PathBuilder::AddLine(const Point& p1, const Point& p2) {
434  MoveTo(p1);
435  prototype_.AddLinearComponent(p1, p2);
436  return *this;
437 }
438 
440  return prototype_;
441 }
442 
444  auto linear = [&](size_t index, const LinearPathComponent& l) {
445  prototype_.AddLinearComponent(l.p1, l.p2);
446  };
447  auto quadratic = [&](size_t index, const QuadraticPathComponent& q) {
448  prototype_.AddQuadraticComponent(q.p1, q.cp, q.p2);
449  };
450  auto cubic = [&](size_t index, const CubicPathComponent& c) {
451  prototype_.AddCubicComponent(c.p1, c.cp1, c.cp2, c.p2);
452  };
453  auto move = [&](size_t index, const ContourComponent& m) {
454  prototype_.AddContourComponent(m.destination);
455  };
456  path.EnumerateComponents(linear, quadratic, cubic, move);
457  return *this;
458 }
459 
461  prototype_.Shift(offset);
462  return *this;
463 }
464 
466  prototype_.SetBounds(bounds);
467  did_compute_bounds_ = true;
468  return *this;
469 }
470 
471 } // namespace impeller
impeller::PathBuilder::AddQuadraticCurve
PathBuilder & AddQuadraticCurve(Point p1, Point cp, Point p2)
Move to point p1, then insert a quadradic curve from p1 to p2 with the control point cp.
Definition: path_builder.cc:166
impeller::TRect::size
TSize< Type > size
Definition: rect.h:24
impeller::LinearPathComponent
Definition: path_component.h:27
impeller::Path::GetQuadraticComponentAtIndex
bool GetQuadraticComponentAtIndex(size_t index, QuadraticPathComponent &quadratic) const
Definition: path.cc:172
impeller::TPoint::y
Type y
Definition: point.h:24
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::PathBuilder::SetBounds
PathBuilder & SetBounds(Rect bounds)
Set the bounding box that will be used by Path.GetBoundingBox in place of performing the computation.
Definition: path_builder.cc:465
impeller::PathBuilder::CubicCurveTo
PathBuilder & CubicCurveTo(Point controlPoint1, Point controlPoint2, Point point, bool relative=false)
Insert a cubic curve from the curren position to point using the control points controlPoint1 and con...
Definition: path_builder.cc:116
impeller::PathBuilder::AddPath
PathBuilder & AddPath(const Path &path)
Definition: path_builder.cc:443
impeller::PathBuilder
Definition: path_builder.h:13
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::Convexity
Convexity
Definition: path.h:37
impeller::PathBuilder::RoundingRadii::AreAllZero
bool AreAllZero() const
Definition: path_builder.h:125
impeller::PathBuilder::AddRoundedRect
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
Definition: path_builder.cc:207
impeller::PathBuilder::HorizontalLineTo
PathBuilder & HorizontalLineTo(Scalar x, bool relative=false)
Definition: path_builder.cc:53
impeller::Radians::radians
Scalar radians
Definition: scalar.h:36
impeller::PathBuilder::~PathBuilder
~PathBuilder()
impeller::PathBuilder::SetConvexity
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:111
impeller::PathBuilder::kArcApproximationMagic
constexpr static const Scalar kArcApproximationMagic
Definition: path_builder.h:22
impeller::PathBuilder::SmoothQuadraticCurveTo
PathBuilder & SmoothQuadraticCurveTo(Point point, bool relative=false)
Definition: path_builder.cc:101
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:181
impeller::PathBuilder::RoundingRadii::bottom_right
Point bottom_right
Definition: path_builder.h:112
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:31
path_builder.h
impeller::PathBuilder::RoundingRadii
Definition: path_builder.h:108
impeller::PathBuilder::SmoothCubicCurveTo
PathBuilder & SmoothCubicCurveTo(Point controlPoint2, Point point, bool relative=false)
Definition: path_builder.cc:151
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:28
impeller::Path::GetCubicComponentAtIndex
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent &cubic) const
Definition: path.cc:187
impeller::PathBuilder::Shift
PathBuilder & Shift(Point offset)
Transform the existing path segments and contours by the given offset.
Definition: path_builder.cc:460
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::PathBuilder::QuadraticCurveTo
PathBuilder & QuadraticCurveTo(Point controlPoint, Point point, bool relative=false)
Insert a quadradic curve from the current position to point using the control point controlPoint.
Definition: path_builder.cc:69
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:54
impeller::PathBuilder::LineTo
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:46
impeller::PathBuilder::AddCubicCurve
PathBuilder & AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
Definition: path_builder.cc:172
impeller::PathBuilder::RoundingRadii::top_left
Point top_left
Definition: path_builder.h:109
impeller::Radians
Definition: scalar.h:35
impeller::FillType
FillType
Definition: path.h:29
impeller::TRect::origin
TPoint< Type > origin
Definition: rect.h:23
impeller::PathBuilder::AddLine
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
Definition: path_builder.cc:433
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:21
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
impeller::TSize::width
Type width
Definition: size.h:21
impeller::TPoint::x
Type x
Definition: point.h:23
impeller::PathBuilder::PathBuilder
PathBuilder()
impeller::CubicPathComponent
Definition: path_component.h:90
impeller::PathBuilder::GetCurrentPath
const Path & GetCurrentPath() const
Definition: path_builder.cc:439
impeller::PathBuilder::Close
PathBuilder & Close()
Definition: path_builder.cc:39
impeller::PathBuilder::CopyPath
Path CopyPath(FillType fill=FillType::kNonZero) const
Definition: path_builder.cc:15
impeller::PathBuilder::RoundingRadii::top_right
Point top_right
Definition: path_builder.h:111
impeller::TPoint< Scalar >
impeller::Path::GetComponentCount
size_t GetComponentCount(std::optional< ComponentType > type={}) const
Definition: path.cc:32
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:32
impeller::PathBuilder::RoundingRadii::bottom_left
Point bottom_left
Definition: path_builder.h:110
impeller::PathBuilder::VerticalLineTo
PathBuilder & VerticalLineTo(Scalar y, bool relative=false)
Definition: path_builder.cc:61
impeller::PathBuilder::AddCircle
PathBuilder & AddCircle(const Point &center, Scalar radius)
Definition: path_builder.cc:198
impeller::TSize::height
Type height
Definition: size.h:22
impeller::PathBuilder::AddOval
PathBuilder & AddOval(const Rect &rect)
Definition: path_builder.cc:385
impeller::ContourComponent
Definition: path_component.h:137
impeller
Definition: aiks_context.cc:10
impeller::QuadraticPathComponent
Definition: path_component.h:50
impeller::TRect< Scalar >
impeller::PathBuilder::AddArc
PathBuilder & AddArc(const Rect &oval_bounds, Radians start, Radians sweep, bool use_center=false)
Definition: path_builder.cc:325