| 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
15 Path PathBuilder::CopyPath(FillType fill) const {
16 auto path = prototype_;
17 path.SetFillType(fill);
18 return path;
19 }
20
21 Path PathBuilder::TakePath(FillType fill) {
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
39 PathBuilder& PathBuilder::Close() {
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
53 PathBuilder& PathBuilder::HorizontalLineTo(Scalar x, bool relative) {
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
61 PathBuilder& PathBuilder::VerticalLineTo(Scalar y, bool relative) {
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
69 PathBuilder& PathBuilder::QuadraticCurveTo(Point controlPoint,
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
101 PathBuilder& PathBuilder::SmoothQuadraticCurveTo(Point point, bool relative) {
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
111 PathBuilder& PathBuilder::SetConvexity(Convexity value) {
112 convexity_ = value;
113 return *this;
114 }
115
116 PathBuilder& PathBuilder::CubicCurveTo(Point controlPoint1,
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
151 PathBuilder& PathBuilder::SmoothCubicCurveTo(Point controlPoint2,
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
166 PathBuilder& PathBuilder::AddQuadraticCurve(Point p1, Point cp, Point p2) {
167 MoveTo(p1);
168 prototype_.AddQuadraticComponent(p1, cp, p2);
169 return *this;
170 }
171
172 PathBuilder& PathBuilder::AddCubicCurve(Point p1,
173 Point cp1,
174 Point cp2,
175 Point p2) {
176 MoveTo(p1);
177 prototype_.AddCubicComponent(p1, cp1, cp2, p2);
178 return *this;
179 }
180
181 PathBuilder& PathBuilder::AddRect(Rect rect) {
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
198 PathBuilder& PathBuilder::AddCircle(const Point& c, Scalar r) {
199 return AddOval(Rect{c.x - r, c.y - r, 2.0f * r, 2.0f * r});
200 }
201
202 PathBuilder& PathBuilder::AddRoundedRect(Rect rect, Scalar radius) {
203 return radius <= 0.0 ? AddRect(rect)
204 : AddRoundedRect(rect, {radius, radius, radius, radius});
205 }
206
207 PathBuilder& PathBuilder::AddRoundedRect(Rect rect, RoundingRadii radii) {
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
325 PathBuilder& PathBuilder::AddArc(const Rect& oval_bounds,
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
385 PathBuilder& PathBuilder::AddOval(const Rect& container) {
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};
388 const Point m = {kArcApproximationMagic * r.x, kArcApproximationMagic * 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
439 const Path& PathBuilder::GetCurrentPath() const {
440 return prototype_;
441 }
442
443 PathBuilder& PathBuilder::AddPath(const Path& path) {
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
460 PathBuilder& PathBuilder::Shift(Point offset) {
461 prototype_.Shift(offset);
462 return *this;
463 }
464
465 PathBuilder& PathBuilder::SetBounds(Rect bounds) {
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