| Flutter Impeller |

round_superellipse_param.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 "flutter/impeller/geometry/round_superellipse_param.h"
6
7 namespace impeller {
8
9 namespace {
10
11 // Return the value that splits the range from `left` to `right` into two
12 // portions whose ratio equals to `ratio_left` : `ratio_right`.
13 Scalar Split(Scalar left, Scalar right, Scalar ratio_left, Scalar ratio_right) {
14 if (ratio_left == 0 && ratio_right == 0) {
15 return (left + right) / 2;
16 }
17 return (left * ratio_right + right * ratio_left) / (ratio_left + ratio_right);
18 }
19
20 // Return the same Point, but each NaN coordinate is replaced by 1.
21 inline Point ReplanceNaNWithOne(Point in) {
22 return Point{std::isnan(in.x) ? 1 : in.x, std::isnan(in.y) ? 1 : in.y};
23 }
24
25 // Swap the x and y coordinate of a point.
26 //
27 // Effectively mirrors the point by the y=x line.
28 inline Point Flip(Point a) {
29 return Point{a.y, a.x};
30 }
31
32 // A look up table with precomputed variables.
33 //
34 // The columns represent the following variabls respectively:
35 //
36 // * n
37 // * k_xJ, which is defined as 1 / (1 - xJ / a)
38 //
39 // For definition of the variables, see ComputeOctant.
40 constexpr Scalar kPrecomputedVariables[][2] = {
41 /*ratio=2.00*/ {2.00000000, 1.13276676},
42 /*ratio=2.10*/ {2.18349805, 1.20311921},
43 /*ratio=2.20*/ {2.33888662, 1.28698796},
44 /*ratio=2.30*/ {2.48660575, 1.36351941},
45 /*ratio=2.40*/ {2.62226596, 1.44717976},
46 /*ratio=2.50*/ {2.75148990, 1.53385819},
47 /*ratio=3.00*/ {3.36298265, 1.98288283},
48 /*ratio=3.50*/ {4.08649929, 2.23811846},
49 /*ratio=4.00*/ {4.85481134, 2.47563463},
50 /*ratio=4.50*/ {5.62945551, 2.72948597},
51 /*ratio=5.00*/ {6.43023796, 2.98020421}};
52
53 constexpr Scalar kMinRatio = 2.00;
54
55 // The curve is split into 3 parts:
56 // * The first part uses a denser look up table.
57 // * The second part uses a sparser look up table.
58 // * The third part uses a straight line.
59 constexpr Scalar kFirstStepInverse = 10; // = 1 / 0.10
60 constexpr Scalar kFirstMaxRatio = 2.50;
61 constexpr Scalar kFirstNumRecords = 6;
62
63 constexpr Scalar kSecondStepInverse = 2; // = 1 / 0.50
64 constexpr Scalar kSecondMaxRatio = 5.00;
65
66 constexpr Scalar kThirdNSlope = 1.559599389;
67 constexpr Scalar kThirdKxjSlope = 0.522807185;
68
69 constexpr size_t kNumRecords =
70 sizeof(kPrecomputedVariables) / sizeof(kPrecomputedVariables[0]);
71
72 // Compute the `n` and `xJ / a` for the given ratio.
73 std::array<Scalar, 2> ComputeNAndXj(Scalar ratio) {
74 if (ratio > kSecondMaxRatio) {
75 Scalar n = kThirdNSlope * (ratio - kSecondMaxRatio) +
76 kPrecomputedVariables[kNumRecords - 1][0];
77 Scalar k_xJ = kThirdKxjSlope * (ratio - kSecondMaxRatio) +
78 kPrecomputedVariables[kNumRecords - 1][1];
79 return {n, 1 - 1 / k_xJ};
80 }
81 ratio = std::clamp(ratio, kMinRatio, kSecondMaxRatio);
82 Scalar steps;
83 if (ratio < kFirstMaxRatio) {
84 steps = (ratio - kMinRatio) * kFirstStepInverse;
85 } else {
86 steps =
87 (ratio - kFirstMaxRatio) * kSecondStepInverse + kFirstNumRecords - 1;
88 }
89
90 size_t left = std::clamp<size_t>(static_cast<size_t>(std::floor(steps)), 0,
91 kNumRecords - 2);
92 Scalar frac = steps - left;
93
94 Scalar n = (1 - frac) * kPrecomputedVariables[left][0] +
95 frac * kPrecomputedVariables[left + 1][0];
96 Scalar k_xJ = (1 - frac) * kPrecomputedVariables[left][1] +
97 frac * kPrecomputedVariables[left + 1][1];
98 return {n, 1 - 1 / k_xJ};
99 }
100
101 // Find the center of the circle that passes the given two points and have the
102 // given radius.
103 Point FindCircleCenter(Point a, Point b, Scalar r) {
104 /* Denote the middle point of A and B as M. The key is to find the center of
105 * the circle.
106 * A --__
107 * / ⟍ `、
108 * / M ⟍\
109 * / ⟋ B
110 * / ⟋ ↗
111 * / ⟋
112 * / ⟋ r
113 * C ᜱ ↙
114 */
115
116 Point a_to_b = b - a;
117 Point m = (a + b) / 2;
118 Point c_to_m = Point(-a_to_b.y, a_to_b.x);
119 Scalar distance_am = a_to_b.GetLength() / 2;
120 Scalar distance_cm = sqrt(r * r - distance_am * distance_am);
121 return m - distance_cm * c_to_m.Normalize();
122 }
123
124 // Compute parameters for a square-like rounded superellipse with a symmetrical
125 // radius.
126 RoundSuperellipseParam::Octant ComputeOctant(Point center,
127 Scalar a,
128 Scalar radius) {
129 /* The following figure shows the first quadrant of a square-like rounded
130 * superellipse.
131 *
132 * superelipse
133 * A ↓ circular arc
134 * ---------...._J ↙
135 * | / `⟍ M (where x=y)
136 * | / ⟋ ⟍
137 * | / ⟋ \
138 * | / ⟋ |
139 * | ᜱD |
140 * | ⟋ |
141 * | ⟋ |
142 * |⟋ |
143 * +--------------------| A'
144 * O
145 * ←-------- a ---------→
146 */
147
148 if (radius <= 0) {
149 return RoundSuperellipseParam::Octant{
150 .offset = center,
151
152 .se_a = a,
153 .se_n = 0,
154 };
155 }
156
157 Scalar ratio = a * 2 / radius;
158 Scalar g = RoundSuperellipseParam::kGapFactor * radius;
159
160 auto precomputed_vars = ComputeNAndXj(ratio);
161 Scalar n = precomputed_vars[0];
162 Scalar xJ = precomputed_vars[1] * a;
163 Scalar yJ = pow(1 - pow(precomputed_vars[1], n), 1 / n) * a;
164 Scalar max_theta = asinf(pow(precomputed_vars[1], n / 2));
165
166 Scalar tan_phiJ = pow(xJ / yJ, n - 1);
167 Scalar d = (xJ - tan_phiJ * yJ) / (1 - tan_phiJ);
168 Scalar R = (a - d - g) * sqrt(2);
169
170 Point pointM{a - g, a - g};
171 Point pointJ = Point{xJ, yJ};
172 Point circle_center =
173 radius == 0 ? pointM : FindCircleCenter(pointJ, pointM, R);
174 Radians circle_max_angle =
175 radius == 0 ? Radians(0)
176 : (pointM - circle_center).AngleTo(pointJ - circle_center);
177
178 return RoundSuperellipseParam::Octant{
179 .offset = center,
180
181 .se_a = a,
182 .se_n = n,
183 .se_max_theta = max_theta,
184
185 .circle_start = pointJ,
186 .circle_center = circle_center,
187 .circle_max_angle = circle_max_angle,
188 };
189 }
190
191 // Compute parameters for a quadrant of a rounded superellipse with asymmetrical
192 // radii.
193 //
194 // The `corner` is the coordinate of the corner point in the same coordinate
195 // space as `center`, which specifies both the half size of the bounding box and
196 // which quadrant the curve should be.
197 RoundSuperellipseParam::Quadrant ComputeQuadrant(Point center,
198 Point corner,
199 Size in_radii) {
200 Point corner_vector = corner - center;
201 Size radii = in_radii.Abs();
202
203 // The prefix "norm" is short for "normalized".
204 //
205 // Be extra careful to avoid NaNs in cases that some coordinates of `in_radii`
206 // or `corner_vector` are zero.
207 Scalar norm_radius = radii.MinDimension();
208 Size forward_scale = norm_radius == 0 ? Size{1, 1} : radii / norm_radius;
209 Point norm_half_size = corner_vector.Abs() / forward_scale;
210 Point signed_scale = ReplanceNaNWithOne(corner_vector / norm_half_size);
211
212 // Each quadrant curve is composed of two octant curves, each of which belongs
213 // to a square-like rounded rectangle. For the two octants to connect at the
214 // circular arc, the centers these two square-like rounded rectangle must be
215 // offset from the quadrant center by a same distance in different directions.
216 // The distance is denoted as `c`.
217 Scalar c = norm_half_size.x - norm_half_size.y;
218
219 return RoundSuperellipseParam::Quadrant{
220 .offset = center,
221 .signed_scale = signed_scale,
222 .top = ComputeOctant(Point{0, -c}, norm_half_size.x, norm_radius),
223 .right = ComputeOctant(Point{c, 0}, norm_half_size.y, norm_radius),
224 };
225 }
226
227 // Checks whether the given point is contained in the first octant of the given
228 // square-like rounded superellipse.
229 //
230 // The first octant refers to the region that spans from 0 to pi/4 starting from
231 // positive Y axis clockwise.
232 //
233 // If the point is not within this octant at all, then this function always
234 // returns true. Otherwise this function returns whether the point is contained
235 // within the rounded superellipse.
236 //
237 // The `param.offset` is ignored. The input point should have been transformed
238 // to the coordinate space where the rounded superellipse is centered at the
239 // origin.
240 bool OctantContains(const RoundSuperellipseParam::Octant& param,
241 const Point& p) {
242 // Check whether the point is within the octant.
243 if (p.x < 0 || p.y < 0 || p.y < p.x) {
244 return true;
245 }
246 // Check if the point is within the superellipsoid segment.
247 if (p.x <= param.circle_start.x) {
248 Point p_se = p / param.se_a;
249 return powf(p_se.x, param.se_n) + powf(p_se.y, param.se_n) <= 1;
250 }
251 Scalar circle_radius =
252 param.circle_start.GetDistanceSquared(param.circle_center);
253 Point p_circle = p - param.circle_center;
254 return p_circle.GetDistanceSquared(Point()) < circle_radius;
255 }
256
257 // Determine if p is inside the corner curve defined by the indicated corner
258 // param.
259 //
260 // The coordinates of p should be within the same coordinate space with
261 // `param.offset`.
262 //
263 // If `check_quadrant` is true, then this function first checks if the point is
264 // within the quadrant of given corner. If not, this function returns true,
265 // otherwise this method continues to check whether the point is contained in
266 // the rounded superellipse.
267 //
268 // If `check_quadrant` is false, then the first step above is skipped, and the
269 // function checks whether the absolute (relative to the center) coordinate of p
270 // is contained in the rounded superellipse.
271 bool CornerContains(const RoundSuperellipseParam::Quadrant& param,
272 const Point& p,
273 bool check_quadrant = true) {
274 Point norm_point = (p - param.offset) / param.signed_scale;
275 if (check_quadrant) {
276 if (norm_point.x < 0 || norm_point.y < 0) {
277 return true;
278 }
279 } else {
280 norm_point = norm_point.Abs();
281 }
282 if (param.top.se_n < 2 || param.right.se_n < 2) {
283 // A rectangular corner. The top and left sides contain the borders
284 // while the bottom and right sides don't (see `Rect.contains`).
285 Scalar x_delta = param.right.offset.x + param.right.se_a - norm_point.x;
286 Scalar y_delta = param.top.offset.y + param.top.se_a - norm_point.y;
287 bool x_within = x_delta > 0 || (x_delta == 0 && param.signed_scale.x < 0);
288 bool y_within = y_delta > 0 || (y_delta == 0 && param.signed_scale.y < 0);
289 return x_within && y_within;
290 }
291 return OctantContains(param.top, norm_point - param.top.offset) &&
292 OctantContains(param.right, Flip(norm_point - param.right.offset));
293 }
294
295 class RoundSuperellipseBuilder {
296 public:
297 explicit RoundSuperellipseBuilder(PathBuilder& builder) : builder_(builder) {}
298
299 // Draws an arc representing 1/4 of a rounded superellipse.
300 //
301 // If `reverse` is false, the resulting arc spans from 0 to pi/2, moving
302 // clockwise starting from the positive Y-axis. Otherwise it moves from pi/2
303 // to 0.
304 void AddQuadrant(const RoundSuperellipseParam::Quadrant& param,
305 bool reverse,
306 Point scale_sign = Point(1, 1)) {
307 auto transform = Matrix::MakeTranslateScale(param.signed_scale * scale_sign,
308 param.offset);
309 if (param.top.se_n < 2 || param.right.se_n < 2) {
310 builder_.LineTo(transform * (param.top.offset +
311 Point(param.top.se_a, param.top.se_a)));
312 if (!reverse) {
313 builder_.LineTo(transform *
314 (param.top.offset + Point(param.top.se_a, 0)));
315 } else {
316 builder_.LineTo(transform *
317 (param.top.offset + Point(0, param.top.se_a)));
318 }
319 return;
320 }
321 if (!reverse) {
322 AddOctant(param.top, /*reverse=*/false, /*flip=*/false, transform);
323 AddOctant(param.right, /*reverse=*/true, /*flip=*/true, transform);
324 } else {
325 AddOctant(param.right, /*reverse=*/false, /*flip=*/true, transform);
326 AddOctant(param.top, /*reverse=*/true, /*flip=*/false, transform);
327 }
328 }
329
330 private:
331 std::array<Point, 4> SuperellipseArcPoints(
332 const RoundSuperellipseParam::Octant& param) {
333 Point start = {0, param.se_a};
334 const Point& end = param.circle_start;
335 constexpr Point start_tangent = {1, 0};
336 Point circle_start_vector = param.circle_start - param.circle_center;
337 Point end_tangent =
338 Point{-circle_start_vector.y, circle_start_vector.x}.Normalize();
339
340 std::array<Scalar, 2> factors = SuperellipseBezierFactors(param.se_n);
341
342 return std::array<Point, 4>{
343 start, start + start_tangent * factors[0] * param.se_a,
344 end + end_tangent * factors[1] * param.se_a, end};
345 };
346
347 std::array<Point, 4> CircularArcPoints(
348 const RoundSuperellipseParam::Octant& param) {
349 Point start_vector = param.circle_start - param.circle_center;
350 Point end_vector =
351 start_vector.Rotate(Radians(-param.circle_max_angle.radians));
352 Point circle_end = param.circle_center + end_vector;
353 Point start_tangent = Point{start_vector.y, -start_vector.x}.Normalize();
354 Point end_tangent = Point{-end_vector.y, end_vector.x}.Normalize();
355 Scalar bezier_factor = std::tan(param.circle_max_angle.radians / 4) * 4 / 3;
356 Scalar radius = start_vector.GetLength();
357
358 return std::array<Point, 4>{
359 param.circle_start,
360 param.circle_start + start_tangent * bezier_factor * radius,
361 circle_end + end_tangent * bezier_factor * radius, circle_end};
362 };
363
364 // Draws an arc representing 1/8 of a rounded superellipse.
365 //
366 // If `reverse` is false, the resulting arc spans from 0 to pi/4, moving
367 // clockwise starting from the positive Y-axis. Otherwise it moves from pi/4
368 // to 0.
369 //
370 // If `flip` is true, all points have their X and Y coordinates swapped,
371 // effectively mirrowing each point by the y=x line.
372 //
373 // All points are transformed by `external_transform` after the optional
374 // flipping before being used as control points for the cubic curves.
375 void AddOctant(const RoundSuperellipseParam::Octant& param,
376 bool reverse,
377 bool flip,
378 const Matrix& external_transform) {
379 Matrix transform =
380 external_transform * Matrix::MakeTranslation(param.offset);
381 if (flip) {
382 transform = transform * kFlip;
383 }
384
385 auto circle_points = CircularArcPoints(param);
386 auto se_points = SuperellipseArcPoints(param);
387
388 if (!reverse) {
389 builder_.CubicCurveTo(transform * se_points[1], transform * se_points[2],
390 transform * se_points[3]);
391 builder_.CubicCurveTo(transform * circle_points[1],
392 transform * circle_points[2],
393 transform * circle_points[3]);
394 } else {
395 builder_.CubicCurveTo(transform * circle_points[2],
396 transform * circle_points[1],
397 transform * circle_points[0]);
398 builder_.CubicCurveTo(transform * se_points[2], transform * se_points[1],
399 transform * se_points[0]);
400 }
401 };
402
403 // Get the Bezier factor for the superellipse arc in a rounded superellipse.
404 //
405 // The result will be assigned to output, where [0] will be the factor for the
406 // starting tangent and [1] for the ending tangent.
407 //
408 // These values are computed by brute-force searching for the minimal distance
409 // on a rounded superellipse and are not for general purpose superellipses.
410 std::array<Scalar, 2> SuperellipseBezierFactors(Scalar n) {
411 constexpr Scalar kPrecomputedVariables[][2] = {
412 /*n=2.0*/ {0.01339448, 0.05994973},
413 /*n=3.0*/ {0.13664115, 0.13592082},
414 /*n=4.0*/ {0.24545546, 0.14099516},
415 /*n=5.0*/ {0.32353151, 0.12808021},
416 /*n=6.0*/ {0.39093068, 0.11726264},
417 /*n=7.0*/ {0.44847800, 0.10808278},
418 /*n=8.0*/ {0.49817452, 0.10026175},
419 /*n=9.0*/ {0.54105583, 0.09344429},
420 /*n=10.0*/ {0.57812578, 0.08748984},
421 /*n=11.0*/ {0.61050961, 0.08224722},
422 /*n=12.0*/ {0.63903989, 0.07759639},
423 /*n=13.0*/ {0.66416338, 0.07346530},
424 /*n=14.0*/ {0.68675338, 0.06974996},
425 /*n=15.0*/ {0.70678034, 0.06529512}};
426 constexpr size_t kNumRecords =
427 sizeof(kPrecomputedVariables) / sizeof(kPrecomputedVariables[0]);
428 constexpr Scalar kStep = 1.00f;
429 constexpr Scalar kMinN = 2.00f;
430 constexpr Scalar kMaxN = kMinN + (kNumRecords - 1) * kStep;
431
432 if (n >= kMaxN) {
433 // Heuristic formula derived from fitting.
434 return {1.07f - expf(1.307649835) * powf(n, -0.8568516731),
435 -0.01f + expf(-0.9287690322) * powf(n, -0.6120901398)};
436 }
437
438 Scalar steps = std::clamp<Scalar>((n - kMinN) / kStep, 0, kNumRecords - 1);
439 size_t left = std::clamp<size_t>(static_cast<size_t>(std::floor(steps)), 0,
440 kNumRecords - 2);
441 Scalar frac = steps - left;
442
443 return std::array<Scalar, 2>{(1 - frac) * kPrecomputedVariables[left][0] +
444 frac * kPrecomputedVariables[left + 1][0],
445 (1 - frac) * kPrecomputedVariables[left][1] +
446 frac * kPrecomputedVariables[left + 1][1]};
447 }
448
449 PathBuilder& builder_;
450
451 // A matrix that swaps the coordinates of a point.
452 // clang-format off
453 static constexpr Matrix kFlip = Matrix(
454 0.0f, 1.0f, 0.0f, 0.0f,
455 1.0f, 0.0f, 0.0f, 0.0f,
456 0.0f, 0.0f, 1.0f, 0.0f,
457 0.0f, 0.0f, 0.0f, 1.0f);
458 // clang-format on
459 };
460
461 } // namespace
462
463 RoundSuperellipseParam RoundSuperellipseParam::MakeBoundsRadii(
464 const Rect& bounds,
465 const RoundingRadii& radii) {
466 if (radii.AreAllCornersSame() && !radii.top_left.IsEmpty()) {
467 // Having four empty corners indicate a rectangle, which needs special
468 // treatment on border containment and therefore is not `all_corners_same`.
469 return RoundSuperellipseParam{
470 .top_right = ComputeQuadrant(bounds.GetCenter(), bounds.GetRightTop(),
471 radii.top_right),
472 .all_corners_same = true,
473 };
474 }
475 Scalar top_split = Split(bounds.GetLeft(), bounds.GetRight(),
476 radii.top_left.width, radii.top_right.width);
477 Scalar right_split = Split(bounds.GetTop(), bounds.GetBottom(),
478 radii.top_right.height, radii.bottom_right.height);
479 Scalar bottom_split =
480 Split(bounds.GetLeft(), bounds.GetRight(), radii.bottom_left.width,
481 radii.bottom_right.width);
482 Scalar left_split = Split(bounds.GetTop(), bounds.GetBottom(),
483 radii.top_left.height, radii.bottom_left.height);
484
485 return RoundSuperellipseParam{
486 .top_right = ComputeQuadrant(Point{top_split, right_split},
487 bounds.GetRightTop(), radii.top_right),
488 .bottom_right =
489 ComputeQuadrant(Point{bottom_split, right_split},
490 bounds.GetRightBottom(), radii.bottom_right),
491 .bottom_left = ComputeQuadrant(Point{bottom_split, left_split},
492 bounds.GetLeftBottom(), radii.bottom_left),
493 .top_left = ComputeQuadrant(Point{top_split, left_split},
494 bounds.GetLeftTop(), radii.top_left),
495 .all_corners_same = false,
496 };
497 }
498
499 void RoundSuperellipseParam::AddToPath(PathBuilder& path_builder) const {
500 RoundSuperellipseBuilder builder(path_builder);
501
502 Point start = top_right.offset +
503 top_right.signed_scale *
504 (top_right.top.offset + Point(0, top_right.top.se_a));
505 path_builder.MoveTo(start);
506
507 if (all_corners_same) {
508 builder.AddQuadrant(top_right, /*reverse=*/false, Point(1, 1));
509 builder.AddQuadrant(top_right, /*reverse=*/true, Point(1, -1));
510 builder.AddQuadrant(top_right, /*reverse=*/false, Point(-1, -1));
511 builder.AddQuadrant(top_right, /*reverse=*/true, Point(-1, 1));
512 } else {
513 builder.AddQuadrant(top_right, /*reverse=*/false);
514 builder.AddQuadrant(bottom_right, /*reverse=*/true);
515 builder.AddQuadrant(bottom_left, /*reverse=*/false);
516 builder.AddQuadrant(top_left, /*reverse=*/true);
517 }
518
519 path_builder.LineTo(start);
520 path_builder.Close();
521 }
522
523 bool RoundSuperellipseParam::Contains(const Point& point) const {
524 if (all_corners_same) {
525 return CornerContains(top_right, point, /*check_quadrant=*/false);
526 }
527 return CornerContains(top_right, point) &&
528 CornerContains(bottom_right, point) &&
529 CornerContains(bottom_left, point) && CornerContains(top_left, point);
530 }
531
532 } // namespace impeller
impeller::PathBuilder
Definition: path_builder.h:16
impeller::PathBuilder::LineTo
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:66
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:47
impeller::PathBuilder::Close
PathBuilder & Close()
Definition: path_builder.cc:54
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller
Definition: allocation.cc:12
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::CornerContains
static bool CornerContains(const Point &p, const Point &corner, const Point &direction, const Size &radii)
Definition: round_rect.cc:30
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::Size
TSize< Scalar > Size
Definition: size.h:159
round_superellipse_param.h
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Matrix::MakeTranslateScale
static constexpr Matrix MakeTranslateScale(const Vector3 &s, const Vector3 &t)
Definition: matrix.h:113
impeller::RoundSuperellipseParam::Octant::offset
Point offset
Definition: round_superellipse_param.h:31
impeller::RoundSuperellipseParam::Octant::se_a
Scalar se_a
Definition: round_superellipse_param.h:34
impeller::RoundSuperellipseParam::Quadrant::signed_scale
Point signed_scale
Definition: round_superellipse_param.h:78
impeller::RoundSuperellipseParam::Quadrant::top
Octant top
Definition: round_superellipse_param.h:82
impeller::RoundSuperellipseParam::Quadrant::offset
Point offset
Definition: round_superellipse_param.h:63
impeller::RoundSuperellipseParam
Definition: round_superellipse_param.h:18
impeller::RoundSuperellipseParam::top_left
Quadrant top_left
Definition: round_superellipse_param.h:92
impeller::RoundSuperellipseParam::MakeBoundsRadii
static RoundSuperellipseParam MakeBoundsRadii(const Rect &bounds, const RoundingRadii &radii)
Definition: round_superellipse_param.cc:463
impeller::RoundSuperellipseParam::AddToPath
void AddToPath(PathBuilder &path) const
Definition: round_superellipse_param.cc:499
impeller::RoundSuperellipseParam::Contains
bool Contains(const Point &point) const
Definition: round_superellipse_param.cc:523
impeller::RoundSuperellipseParam::bottom_left
Quadrant bottom_left
Definition: round_superellipse_param.h:91
impeller::RoundSuperellipseParam::kGapFactor
static constexpr Scalar kGapFactor
Definition: round_superellipse_param.h:123
impeller::RoundSuperellipseParam::all_corners_same
bool all_corners_same
Definition: round_superellipse_param.h:95
impeller::RoundSuperellipseParam::top_right
Quadrant top_right
Definition: round_superellipse_param.h:89
impeller::RoundSuperellipseParam::bottom_right
Quadrant bottom_right
Definition: round_superellipse_param.h:90
impeller::RoundingRadii
Definition: rounding_radii.h:14
impeller::RoundingRadii::top_right
Size top_right
Definition: rounding_radii.h:16
impeller::RoundingRadii::bottom_left
Size bottom_left
Definition: rounding_radii.h:17
impeller::RoundingRadii::bottom_right
Size bottom_right
Definition: rounding_radii.h:18
impeller::RoundingRadii::top_left
Size top_left
Definition: rounding_radii.h:15
impeller::RoundingRadii::AreAllCornersSame
constexpr bool AreAllCornersSame(Scalar tolerance=kEhCloseEnough) const
Definition: rounding_radii.h:54
impeller::TPoint< Scalar >
impeller::TPoint::Abs
constexpr TPoint Abs() const
Definition: point.h:216
impeller::TPoint::Rotate
constexpr TPoint Rotate(const Radians &angle) const
Definition: point.h:226
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::TPoint::GetDistanceSquared
constexpr Type GetDistanceSquared(const TPoint &p) const
Definition: point.h:180
impeller::TRect< Scalar >
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:361
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:357
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:355
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:359
impeller::TRect::GetLeftBottom
constexpr TPoint< T > GetLeftBottom() const
Definition: rect.h:371
impeller::TRect::GetRightTop
constexpr TPoint< T > GetRightTop() const
Definition: rect.h:367
impeller::TRect::GetRightBottom
constexpr TPoint< T > GetRightBottom() const
Definition: rect.h:375
impeller::TRect::GetCenter
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:386
impeller::TRect::GetLeftTop
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:363
impeller::TSize::Abs
constexpr TSize Abs() const
Definition: size.h:108
impeller::TSize::height
Type height
Definition: size.h:29
impeller::TSize::width
Type width
Definition: size.h:28
impeller::TSize::IsEmpty
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: size.h:123