• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5part of dart.ui;
6
7/// Base class for [Size] and [Offset], which are both ways to describe
8/// a distance as a two-dimensional axis-aligned vector.
9abstract class OffsetBase {
10  /// Abstract const constructor. This constructor enables subclasses to provide
11  /// const constructors so that they can be used in const expressions.
12  ///
13  /// The first argument sets the horizontal component, and the second the
14  /// vertical component.
15  const OffsetBase(this._dx, this._dy);
16
17  final double _dx;
18  final double _dy;
19
20  /// Returns true if either component is [double.infinity], and false if both
21  /// are finite (or negative infinity, or NaN).
22  ///
23  /// This is different than comparing for equality with an instance that has
24  /// _both_ components set to [double.infinity].
25  ///
26  /// See also:
27  ///
28  ///  * [isFinite], which is true if both components are finite (and not NaN).
29  bool get isInfinite => _dx >= double.infinity || _dy >= double.infinity;
30
31  /// Whether both components are finite (neither infinite nor NaN).
32  ///
33  /// See also:
34  ///
35  ///  * [isInfinite], which returns true if either component is equal to
36  ///    positive infinity.
37  bool get isFinite => _dx.isFinite && _dy.isFinite;
38
39  /// Less-than operator. Compares an [Offset] or [Size] to another [Offset] or
40  /// [Size], and returns true if both the horizontal and vertical values of the
41  /// left-hand-side operand are smaller than the horizontal and vertical values
42  /// of the right-hand-side operand respectively. Returns false otherwise.
43  ///
44  /// This is a partial ordering. It is possible for two values to be neither
45  /// less, nor greater than, nor equal to, another.
46  bool operator <(OffsetBase other) => _dx < other._dx && _dy < other._dy;
47
48  /// Less-than-or-equal-to operator. Compares an [Offset] or [Size] to another
49  /// [Offset] or [Size], and returns true if both the horizontal and vertical
50  /// values of the left-hand-side operand are smaller than or equal to the
51  /// horizontal and vertical values of the right-hand-side operand
52  /// respectively. Returns false otherwise.
53  ///
54  /// This is a partial ordering. It is possible for two values to be neither
55  /// less, nor greater than, nor equal to, another.
56  bool operator <=(OffsetBase other) => _dx <= other._dx && _dy <= other._dy;
57
58  /// Greater-than operator. Compares an [Offset] or [Size] to another [Offset]
59  /// or [Size], and returns true if both the horizontal and vertical values of
60  /// the left-hand-side operand are bigger than the horizontal and vertical
61  /// values of the right-hand-side operand respectively. Returns false
62  /// otherwise.
63  ///
64  /// This is a partial ordering. It is possible for two values to be neither
65  /// less, nor greater than, nor equal to, another.
66  bool operator >(OffsetBase other) => _dx > other._dx && _dy > other._dy;
67
68  /// Greater-than-or-equal-to operator. Compares an [Offset] or [Size] to
69  /// another [Offset] or [Size], and returns true if both the horizontal and
70  /// vertical values of the left-hand-side operand are bigger than or equal to
71  /// the horizontal and vertical values of the right-hand-side operand
72  /// respectively. Returns false otherwise.
73  ///
74  /// This is a partial ordering. It is possible for two values to be neither
75  /// less, nor greater than, nor equal to, another.
76  bool operator >=(OffsetBase other) => _dx >= other._dx && _dy >= other._dy;
77
78  /// Equality operator. Compares an [Offset] or [Size] to another [Offset] or
79  /// [Size], and returns true if the horizontal and vertical values of the
80  /// left-hand-side operand are equal to the horizontal and vertical values of
81  /// the right-hand-side operand respectively. Returns false otherwise.
82  @override
83  bool operator ==(dynamic other) {
84    if (other is! OffsetBase)
85      return false;
86    final OffsetBase typedOther = other;
87    return _dx == typedOther._dx &&
88           _dy == typedOther._dy;
89  }
90
91  @override
92  int get hashCode => hashValues(_dx, _dy);
93
94  @override
95  String toString() => 'OffsetBase(${_dx?.toStringAsFixed(1)}, ${_dy?.toStringAsFixed(1)})';
96}
97
98/// An immutable 2D floating-point offset.
99///
100/// Generally speaking, Offsets can be interpreted in two ways:
101///
102/// 1. As representing a point in Cartesian space a specified distance from a
103///    separately-maintained origin. For example, the top-left position of
104///    children in the [RenderBox] protocol is typically represented as an
105///    [Offset] from the top left of the parent box.
106///
107/// 2. As a vector that can be applied to coordinates. For example, when
108///    painting a [RenderObject], the parent is passed an [Offset] from the
109///    screen's origin which it can add to the offsets of its children to find
110///    the [Offset] from the screen's origin to each of the children.
111///
112/// Because a particular [Offset] can be interpreted as one sense at one time
113/// then as the other sense at a later time, the same class is used for both
114/// senses.
115///
116/// See also:
117///
118///  * [Size], which represents a vector describing the size of a rectangle.
119class Offset extends OffsetBase {
120  /// Creates an offset. The first argument sets [dx], the horizontal component,
121  /// and the second sets [dy], the vertical component.
122  const Offset(double dx, double dy) : super(dx, dy);
123
124  /// Creates an offset from its [direction] and [distance].
125  ///
126  /// The direction is in radians clockwise from the positive x-axis.
127  ///
128  /// The distance can be omitted, to create a unit vector (distance = 1.0).
129  factory Offset.fromDirection(double direction, [ double distance = 1.0 ]) {
130    return Offset(distance * math.cos(direction), distance * math.sin(direction));
131  }
132
133  /// The x component of the offset.
134  ///
135  /// The y component is given by [dy].
136  double get dx => _dx;
137
138  /// The y component of the offset.
139  ///
140  /// The x component is given by [dx].
141  double get dy => _dy;
142
143  /// The magnitude of the offset.
144  ///
145  /// If you need this value to compare it to another [Offset]'s distance,
146  /// consider using [distanceSquared] instead, since it is cheaper to compute.
147  double get distance => math.sqrt(dx * dx + dy * dy);
148
149  /// The square of the magnitude of the offset.
150  ///
151  /// This is cheaper than computing the [distance] itself.
152  double get distanceSquared => dx * dx + dy * dy;
153
154  /// The angle of this offset as radians clockwise from the positive x-axis, in
155  /// the range -[pi] to [pi], assuming positive values of the x-axis go to the
156  /// left and positive values of the y-axis go down.
157  ///
158  /// Zero means that [dy] is zero and [dx] is zero or positive.
159  ///
160  /// Values from zero to [pi]/2 indicate positive values of [dx] and [dy], the
161  /// bottom-right quadrant.
162  ///
163  /// Values from [pi]/2 to [pi] indicate negative values of [dx] and positive
164  /// values of [dy], the bottom-left quadrant.
165  ///
166  /// Values from zero to -[pi]/2 indicate positive values of [dx] and negative
167  /// values of [dy], the top-right quadrant.
168  ///
169  /// Values from -[pi]/2 to -[pi] indicate negative values of [dx] and [dy],
170  /// the top-left quadrant.
171  ///
172  /// When [dy] is zero and [dx] is negative, the [direction] is [pi].
173  ///
174  /// When [dx] is zero, [direction] is [pi]/2 if [dy] is positive and -[pi]/2
175  /// if [dy] is negative.
176  ///
177  /// See also:
178  ///
179  ///  * [distance], to compute the magnitude of the vector.
180  ///  * [Canvas.rotate], which uses the same convention for its angle.
181  double get direction => math.atan2(dy, dx);
182
183  /// An offset with zero magnitude.
184  ///
185  /// This can be used to represent the origin of a coordinate space.
186  static const Offset zero = Offset(0.0, 0.0);
187
188  /// An offset with infinite x and y components.
189  ///
190  /// See also:
191  ///
192  ///  * [isInfinite], which checks whether either component is infinite.
193  ///  * [isFinite], which checks whether both components are finite.
194  // This is included for completeness, because [Size.infinite] exists.
195  static const Offset infinite = Offset(double.infinity, double.infinity);
196
197  /// Returns a new offset with the x component scaled by `scaleX` and the y
198  /// component scaled by `scaleY`.
199  ///
200  /// If the two scale arguments are the same, consider using the `*` operator
201  /// instead:
202  ///
203  /// ```dart
204  /// Offset a = const Offset(10.0, 10.0);
205  /// Offset b = a * 2.0; // same as: a.scale(2.0, 2.0)
206  /// ```
207  ///
208  /// If the two arguments are -1, consider using the unary `-` operator
209  /// instead:
210  ///
211  /// ```dart
212  /// Offset a = const Offset(10.0, 10.0);
213  /// Offset b = -a; // same as: a.scale(-1.0, -1.0)
214  /// ```
215  Offset scale(double scaleX, double scaleY) => Offset(dx * scaleX, dy * scaleY);
216
217  /// Returns a new offset with translateX added to the x component and
218  /// translateY added to the y component.
219  ///
220  /// If the arguments come from another [Offset], consider using the `+` or `-`
221  /// operators instead:
222  ///
223  /// ```dart
224  /// Offset a = const Offset(10.0, 10.0);
225  /// Offset b = const Offset(10.0, 10.0);
226  /// Offset c = a + b; // same as: a.translate(b.dx, b.dy)
227  /// Offset d = a - b; // same as: a.translate(-b.dx, -b.dy)
228  /// ```
229  Offset translate(double translateX, double translateY) => Offset(dx + translateX, dy + translateY);
230
231  /// Unary negation operator.
232  ///
233  /// Returns an offset with the coordinates negated.
234  ///
235  /// If the [Offset] represents an arrow on a plane, this operator returns the
236  /// same arrow but pointing in the reverse direction.
237  Offset operator -() => Offset(-dx, -dy);
238
239  /// Binary subtraction operator.
240  ///
241  /// Returns an offset whose [dx] value is the left-hand-side operand's [dx]
242  /// minus the right-hand-side operand's [dx] and whose [dy] value is the
243  /// left-hand-side operand's [dy] minus the right-hand-side operand's [dy].
244  ///
245  /// See also [translate].
246  Offset operator -(Offset other) => Offset(dx - other.dx, dy - other.dy);
247
248  /// Binary addition operator.
249  ///
250  /// Returns an offset whose [dx] value is the sum of the [dx] values of the
251  /// two operands, and whose [dy] value is the sum of the [dy] values of the
252  /// two operands.
253  ///
254  /// See also [translate].
255  Offset operator +(Offset other) => Offset(dx + other.dx, dy + other.dy);
256
257  /// Multiplication operator.
258  ///
259  /// Returns an offset whose coordinates are the coordinates of the
260  /// left-hand-side operand (an Offset) multiplied by the scalar
261  /// right-hand-side operand (a double).
262  ///
263  /// See also [scale].
264  Offset operator *(double operand) => Offset(dx * operand, dy * operand);
265
266  /// Division operator.
267  ///
268  /// Returns an offset whose coordinates are the coordinates of the
269  /// left-hand-side operand (an Offset) divided by the scalar right-hand-side
270  /// operand (a double).
271  ///
272  /// See also [scale].
273  Offset operator /(double operand) => Offset(dx / operand, dy / operand);
274
275  /// Integer (truncating) division operator.
276  ///
277  /// Returns an offset whose coordinates are the coordinates of the
278  /// left-hand-side operand (an Offset) divided by the scalar right-hand-side
279  /// operand (a double), rounded towards zero.
280  Offset operator ~/(double operand) => Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble());
281
282  /// Modulo (remainder) operator.
283  ///
284  /// Returns an offset whose coordinates are the remainder of dividing the
285  /// coordinates of the left-hand-side operand (an Offset) by the scalar
286  /// right-hand-side operand (a double).
287  Offset operator %(double operand) => Offset(dx % operand, dy % operand);
288
289  /// Rectangle constructor operator.
290  ///
291  /// Combines an [Offset] and a [Size] to form a [Rect] whose top-left
292  /// coordinate is the point given by adding this offset, the left-hand-side
293  /// operand, to the origin, and whose size is the right-hand-side operand.
294  ///
295  /// ```dart
296  /// Rect myRect = Offset.zero & const Size(100.0, 100.0);
297  /// // same as: Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)
298  /// ```
299  Rect operator &(Size other) => Rect.fromLTWH(dx, dy, other.width, other.height);
300
301  /// Linearly interpolate between two offsets.
302  ///
303  /// If either offset is null, this function interpolates from [Offset.zero].
304  ///
305  /// The `t` argument represents position on the timeline, with 0.0 meaning
306  /// that the interpolation has not started, returning `a` (or something
307  /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
308  /// returning `b` (or something equivalent to `b`), and values in between
309  /// meaning that the interpolation is at the relevant point on the timeline
310  /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
311  /// 1.0, so negative values and values greater than 1.0 are valid (and can
312  /// easily be generated by curves such as [Curves.elasticInOut]).
313  ///
314  /// Values for `t` are usually obtained from an [Animation<double>], such as
315  /// an [AnimationController].
316  static Offset lerp(Offset a, Offset b, double t) {
317    assert(t != null);
318    if (a == null && b == null)
319      return null;
320    if (a == null)
321      return b * t;
322    if (b == null)
323      return a * (1.0 - t);
324    return Offset(lerpDouble(a.dx, b.dx, t), lerpDouble(a.dy, b.dy, t));
325  }
326
327  /// Compares two Offsets for equality.
328  @override
329  bool operator ==(dynamic other) {
330    if (other is! Offset)
331      return false;
332    final Offset typedOther = other;
333    return dx == typedOther.dx &&
334           dy == typedOther.dy;
335  }
336
337  @override
338  int get hashCode => hashValues(dx, dy);
339
340  @override
341  String toString() => 'Offset(${dx?.toStringAsFixed(1)}, ${dy?.toStringAsFixed(1)})';
342}
343
344/// Holds a 2D floating-point size.
345///
346/// You can think of this as an [Offset] from the origin.
347class Size extends OffsetBase {
348  /// Creates a [Size] with the given [width] and [height].
349  const Size(double width, double height) : super(width, height);
350
351  /// Creates an instance of [Size] that has the same values as another.
352  // Used by the rendering library's _DebugSize hack.
353  Size.copy(Size source) : super(source.width, source.height);
354
355  /// Creates a square [Size] whose [width] and [height] are the given dimension.
356  ///
357  /// See also:
358  ///
359  ///  * [Size.fromRadius], which is more convenient when the available size
360  ///    is the radius of a circle.
361  const Size.square(double dimension) : super(dimension, dimension);
362
363  /// Creates a [Size] with the given [width] and an infinite [height].
364  const Size.fromWidth(double width) : super(width, double.infinity);
365
366  /// Creates a [Size] with the given [height] and an infinite [width].
367  const Size.fromHeight(double height) : super(double.infinity, height);
368
369  /// Creates a square [Size] whose [width] and [height] are twice the given
370  /// dimension.
371  ///
372  /// This is a square that contains a circle with the given radius.
373  ///
374  /// See also:
375  ///
376  ///  * [Size.square], which creates a square with the given dimension.
377  const Size.fromRadius(double radius) : super(radius * 2.0, radius * 2.0);
378
379  /// The horizontal extent of this size.
380  double get width => _dx;
381
382  /// The vertical extent of this size.
383  double get height => _dy;
384
385  /// The aspect ratio of this size.
386  ///
387  /// This returns the [width] divided by the [height].
388  ///
389  /// If the [width] is zero, the result will be zero. If the [height] is zero
390  /// (and the [width] is not), the result will be [double.infinity] or
391  /// [double.negativeInfinity] as determined by the sign of [width].
392  ///
393  /// See also:
394  ///
395  ///  * [AspectRatio], a widget for giving a child widget a specific aspect
396  ///    ratio.
397  ///  * [FittedBox], a widget that (in most modes) attempts to maintain a
398  ///    child widget's aspect ratio while changing its size.
399  double get aspectRatio {
400    if (height != 0.0)
401      return width / height;
402    if (width > 0.0)
403      return double.infinity;
404    if (width < 0.0)
405      return double.negativeInfinity;
406    return 0.0;
407  }
408
409  /// An empty size, one with a zero width and a zero height.
410  static const Size zero = Size(0.0, 0.0);
411
412  /// A size whose [width] and [height] are infinite.
413  ///
414  /// See also:
415  ///
416  ///  * [isInfinite], which checks whether either dimension is infinite.
417  ///  * [isFinite], which checks whether both dimensions are finite.
418  static const Size infinite = Size(double.infinity, double.infinity);
419
420  /// Whether this size encloses a non-zero area.
421  ///
422  /// Negative areas are considered empty.
423  bool get isEmpty => width <= 0.0 || height <= 0.0;
424
425  /// Binary subtraction operator for [Size].
426  ///
427  /// Subtracting a [Size] from a [Size] returns the [Offset] that describes how
428  /// much bigger the left-hand-side operand is than the right-hand-side
429  /// operand. Adding that resulting [Offset] to the [Size] that was the
430  /// right-hand-side operand would return a [Size] equal to the [Size] that was
431  /// the left-hand-side operand. (i.e. if `sizeA - sizeB -> offsetA`, then
432  /// `offsetA + sizeB -> sizeA`)
433  ///
434  /// Subtracting an [Offset] from a [Size] returns the [Size] that is smaller than
435  /// the [Size] operand by the difference given by the [Offset] operand. In other
436  /// words, the returned [Size] has a [width] consisting of the [width] of the
437  /// left-hand-side operand minus the [Offset.dx] dimension of the
438  /// right-hand-side operand, and a [height] consisting of the [height] of the
439  /// left-hand-side operand minus the [Offset.dy] dimension of the
440  /// right-hand-side operand.
441  OffsetBase operator -(OffsetBase other) {
442    if (other is Size)
443      return Offset(width - other.width, height - other.height);
444    if (other is Offset)
445      return Size(width - other.dx, height - other.dy);
446    throw ArgumentError(other);
447  }
448
449  /// Binary addition operator for adding an [Offset] to a [Size].
450  ///
451  /// Returns a [Size] whose [width] is the sum of the [width] of the
452  /// left-hand-side operand, a [Size], and the [Offset.dx] dimension of the
453  /// right-hand-side operand, an [Offset], and whose [height] is the sum of the
454  /// [height] of the left-hand-side operand and the [Offset.dy] dimension of
455  /// the right-hand-side operand.
456  Size operator +(Offset other) => Size(width + other.dx, height + other.dy);
457
458  /// Multiplication operator.
459  ///
460  /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side
461  /// operand (a [Size]) multiplied by the scalar right-hand-side operand (a
462  /// [double]).
463  Size operator *(double operand) => Size(width * operand, height * operand);
464
465  /// Division operator.
466  ///
467  /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side
468  /// operand (a [Size]) divided by the scalar right-hand-side operand (a
469  /// [double]).
470  Size operator /(double operand) => Size(width / operand, height / operand);
471
472  /// Integer (truncating) division operator.
473  ///
474  /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side
475  /// operand (a [Size]) divided by the scalar right-hand-side operand (a
476  /// [double]), rounded towards zero.
477  Size operator ~/(double operand) => Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble());
478
479  /// Modulo (remainder) operator.
480  ///
481  /// Returns a [Size] whose dimensions are the remainder of dividing the
482  /// left-hand-side operand (a [Size]) by the scalar right-hand-side operand (a
483  /// [double]).
484  Size operator %(double operand) => Size(width % operand, height % operand);
485
486  /// The lesser of the magnitudes of the [width] and the [height].
487  double get shortestSide => math.min(width.abs(), height.abs());
488
489  /// The greater of the magnitudes of the [width] and the [height].
490  double get longestSide => math.max(width.abs(), height.abs());
491
492  // Convenience methods that do the equivalent of calling the similarly named
493  // methods on a Rect constructed from the given origin and this size.
494
495  /// The offset to the intersection of the top and left edges of the rectangle
496  /// described by the given [Offset] (which is interpreted as the top-left corner)
497  /// and this [Size].
498  ///
499  /// See also [Rect.topLeft].
500  Offset topLeft(Offset origin) => origin;
501
502  /// The offset to the center of the top edge of the rectangle described by the
503  /// given offset (which is interpreted as the top-left corner) and this size.
504  ///
505  /// See also [Rect.topCenter].
506  Offset topCenter(Offset origin) => Offset(origin.dx + width / 2.0, origin.dy);
507
508  /// The offset to the intersection of the top and right edges of the rectangle
509  /// described by the given offset (which is interpreted as the top-left corner)
510  /// and this size.
511  ///
512  /// See also [Rect.topRight].
513  Offset topRight(Offset origin) => Offset(origin.dx + width, origin.dy);
514
515  /// The offset to the center of the left edge of the rectangle described by the
516  /// given offset (which is interpreted as the top-left corner) and this size.
517  ///
518  /// See also [Rect.centerLeft].
519  Offset centerLeft(Offset origin) => Offset(origin.dx, origin.dy + height / 2.0);
520
521  /// The offset to the point halfway between the left and right and the top and
522  /// bottom edges of the rectangle described by the given offset (which is
523  /// interpreted as the top-left corner) and this size.
524  ///
525  /// See also [Rect.center].
526  Offset center(Offset origin) => Offset(origin.dx + width / 2.0, origin.dy + height / 2.0);
527
528  /// The offset to the center of the right edge of the rectangle described by the
529  /// given offset (which is interpreted as the top-left corner) and this size.
530  ///
531  /// See also [Rect.centerLeft].
532  Offset centerRight(Offset origin) => Offset(origin.dx + width, origin.dy + height / 2.0);
533
534  /// The offset to the intersection of the bottom and left edges of the
535  /// rectangle described by the given offset (which is interpreted as the
536  /// top-left corner) and this size.
537  ///
538  /// See also [Rect.bottomLeft].
539  Offset bottomLeft(Offset origin) => Offset(origin.dx, origin.dy + height);
540
541  /// The offset to the center of the bottom edge of the rectangle described by
542  /// the given offset (which is interpreted as the top-left corner) and this
543  /// size.
544  ///
545  /// See also [Rect.bottomLeft].
546  Offset bottomCenter(Offset origin) => Offset(origin.dx + width / 2.0, origin.dy + height);
547
548  /// The offset to the intersection of the bottom and right edges of the
549  /// rectangle described by the given offset (which is interpreted as the
550  /// top-left corner) and this size.
551  ///
552  /// See also [Rect.bottomRight].
553  Offset bottomRight(Offset origin) => Offset(origin.dx + width, origin.dy + height);
554
555  /// Whether the point specified by the given offset (which is assumed to be
556  /// relative to the top left of the size) lies between the left and right and
557  /// the top and bottom edges of a rectangle of this size.
558  ///
559  /// Rectangles include their top and left edges but exclude their bottom and
560  /// right edges.
561  bool contains(Offset offset) {
562    return offset.dx >= 0.0 && offset.dx < width && offset.dy >= 0.0 && offset.dy < height;
563  }
564
565  /// A [Size] with the [width] and [height] swapped.
566  Size get flipped => Size(height, width);
567
568  /// Linearly interpolate between two sizes
569  ///
570  /// If either size is null, this function interpolates from [Size.zero].
571  ///
572  /// The `t` argument represents position on the timeline, with 0.0 meaning
573  /// that the interpolation has not started, returning `a` (or something
574  /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
575  /// returning `b` (or something equivalent to `b`), and values in between
576  /// meaning that the interpolation is at the relevant point on the timeline
577  /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
578  /// 1.0, so negative values and values greater than 1.0 are valid (and can
579  /// easily be generated by curves such as [Curves.elasticInOut]).
580  ///
581  /// Values for `t` are usually obtained from an [Animation<double>], such as
582  /// an [AnimationController].
583  static Size lerp(Size a, Size b, double t) {
584    assert(t != null);
585    if (a == null && b == null)
586      return null;
587    if (a == null)
588      return b * t;
589    if (b == null)
590      return a * (1.0 - t);
591    return Size(lerpDouble(a.width, b.width, t), lerpDouble(a.height, b.height, t));
592  }
593
594  /// Compares two Sizes for equality.
595  // We don't compare the runtimeType because of _DebugSize in the framework.
596  @override
597  bool operator ==(dynamic other) {
598    if (other is! Size)
599      return false;
600    final Size typedOther = other;
601    return _dx == typedOther._dx &&
602           _dy == typedOther._dy;
603  }
604
605  @override
606  int get hashCode => hashValues(_dx, _dy);
607
608  @override
609  String toString() => 'Size(${width?.toStringAsFixed(1)}, ${height?.toStringAsFixed(1)})';
610}
611
612/// An immutable, 2D, axis-aligned, floating-point rectangle whose coordinates
613/// are relative to a given origin.
614///
615/// A Rect can be created with one its constructors or from an [Offset] and a
616/// [Size] using the `&` operator:
617///
618/// ```dart
619/// Rect myRect = const Offset(1.0, 2.0) & const Size(3.0, 4.0);
620/// ```
621class Rect {
622  /// Construct a rectangle from its left, top, right, and bottom edges.
623  @pragma('vm:entry-point')
624  const Rect.fromLTRB(this.left, this.top, this.right, this.bottom)
625    : assert(left != null),
626      assert(top != null),
627      assert(right != null),
628      assert(bottom != null);
629
630  /// Construct a rectangle from its left and top edges, its width, and its
631  /// height.
632  ///
633  /// To construct a [Rect] from an [Offset] and a [Size], you can use the
634  /// rectangle constructor operator `&`. See [Offset.&].
635  const Rect.fromLTWH(double left, double top, double width, double height) : this.fromLTRB(left, top, left + width, top + height);
636
637  /// Construct a rectangle that bounds the given circle.
638  ///
639  /// The `center` argument is assumed to be an offset from the origin.
640  Rect.fromCircle({ Offset center, double radius }) : this.fromCenter(
641    center: center,
642    width: radius * 2,
643    height: radius * 2,
644  );
645
646  /// Constructs a rectangle from its center point, width, and height.
647  ///
648  /// The `center` argument is assumed to be an offset from the origin.
649  Rect.fromCenter({ Offset center, double width, double height }) : this.fromLTRB(
650    center.dx - width / 2,
651    center.dy - height / 2,
652    center.dx + width / 2,
653    center.dy + height / 2,
654  );
655
656  /// Construct the smallest rectangle that encloses the given offsets, treating
657  /// them as vectors from the origin.
658  Rect.fromPoints(Offset a, Offset b) : this.fromLTRB(
659    math.min(a.dx, b.dx),
660    math.min(a.dy, b.dy),
661    math.max(a.dx, b.dx),
662    math.max(a.dy, b.dy),
663  );
664
665  Float32List get _value32 => Float32List.fromList(<double>[left, top, right, bottom]);
666
667  /// The offset of the left edge of this rectangle from the x axis.
668  final double left;
669
670  /// The offset of the top edge of this rectangle from the y axis.
671  final double top;
672
673  /// The offset of the right edge of this rectangle from the x axis.
674  final double right;
675
676  /// The offset of the bottom edge of this rectangle from the y axis.
677  final double bottom;
678
679  /// The distance between the left and right edges of this rectangle.
680  double get width => right - left;
681
682  /// The distance between the top and bottom edges of this rectangle.
683  double get height => bottom - top;
684
685  /// The distance between the upper-left corner and the lower-right corner of
686  /// this rectangle.
687  Size get size => Size(width, height);
688
689  /// Whether any of the dimensions are `NaN`.
690  bool get hasNaN => left.isNaN || top.isNaN || right.isNaN || bottom.isNaN;
691
692  /// A rectangle with left, top, right, and bottom edges all at zero.
693  static const Rect zero = Rect.fromLTRB(0.0, 0.0, 0.0, 0.0);
694
695  static const double _giantScalar = 1.0E+9; // matches kGiantRect from layer.h
696
697  /// A rectangle that covers the entire coordinate space.
698  ///
699  /// This covers the space from -1e9,-1e9 to 1e9,1e9.
700  /// This is the space over which graphics operations are valid.
701  static const Rect largest = Rect.fromLTRB(-_giantScalar, -_giantScalar, _giantScalar, _giantScalar);
702
703  /// Whether any of the coordinates of this rectangle are equal to positive infinity.
704  // included for consistency with Offset and Size
705  bool get isInfinite {
706    return left >= double.infinity
707        || top >= double.infinity
708        || right >= double.infinity
709        || bottom >= double.infinity;
710  }
711
712  /// Whether all coordinates of this rectangle are finite.
713  bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite;
714
715  /// Whether this rectangle encloses a non-zero area. Negative areas are
716  /// considered empty.
717  bool get isEmpty => left >= right || top >= bottom;
718
719  /// Returns a new rectangle translated by the given offset.
720  ///
721  /// To translate a rectangle by separate x and y components rather than by an
722  /// [Offset], consider [translate].
723  Rect shift(Offset offset) {
724    return Rect.fromLTRB(left + offset.dx, top + offset.dy, right + offset.dx, bottom + offset.dy);
725  }
726
727  /// Returns a new rectangle with translateX added to the x components and
728  /// translateY added to the y components.
729  ///
730  /// To translate a rectangle by an [Offset] rather than by separate x and y
731  /// components, consider [shift].
732  Rect translate(double translateX, double translateY) {
733    return Rect.fromLTRB(left + translateX, top + translateY, right + translateX, bottom + translateY);
734  }
735
736  /// Returns a new rectangle with edges moved outwards by the given delta.
737  Rect inflate(double delta) {
738    return Rect.fromLTRB(left - delta, top - delta, right + delta, bottom + delta);
739  }
740
741  /// Returns a new rectangle with edges moved inwards by the given delta.
742  Rect deflate(double delta) => inflate(-delta);
743
744  /// Returns a new rectangle that is the intersection of the given
745  /// rectangle and this rectangle. The two rectangles must overlap
746  /// for this to be meaningful. If the two rectangles do not overlap,
747  /// then the resulting Rect will have a negative width or height.
748  Rect intersect(Rect other) {
749    return Rect.fromLTRB(
750      math.max(left, other.left),
751      math.max(top, other.top),
752      math.min(right, other.right),
753      math.min(bottom, other.bottom)
754    );
755  }
756
757  /// Returns a new rectangle which is the bounding box containing this
758  /// rectangle and the given rectangle.
759  Rect expandToInclude(Rect other) {
760    return Rect.fromLTRB(
761        math.min(left, other.left),
762        math.min(top, other.top),
763        math.max(right, other.right),
764        math.max(bottom, other.bottom),
765    );
766  }
767
768  /// Whether `other` has a nonzero area of overlap with this rectangle.
769  bool overlaps(Rect other) {
770    if (right <= other.left || other.right <= left)
771      return false;
772    if (bottom <= other.top || other.bottom <= top)
773      return false;
774    return true;
775  }
776
777  /// The lesser of the magnitudes of the [width] and the [height] of this
778  /// rectangle.
779  double get shortestSide => math.min(width.abs(), height.abs());
780
781  /// The greater of the magnitudes of the [width] and the [height] of this
782  /// rectangle.
783  double get longestSide => math.max(width.abs(), height.abs());
784
785  /// The offset to the intersection of the top and left edges of this rectangle.
786  ///
787  /// See also [Size.topLeft].
788  Offset get topLeft => Offset(left, top);
789
790  /// The offset to the center of the top edge of this rectangle.
791  ///
792  /// See also [Size.topCenter].
793  Offset get topCenter => Offset(left + width / 2.0, top);
794
795  /// The offset to the intersection of the top and right edges of this rectangle.
796  ///
797  /// See also [Size.topRight].
798  Offset get topRight => Offset(right, top);
799
800  /// The offset to the center of the left edge of this rectangle.
801  ///
802  /// See also [Size.centerLeft].
803  Offset get centerLeft => Offset(left, top + height / 2.0);
804
805  /// The offset to the point halfway between the left and right and the top and
806  /// bottom edges of this rectangle.
807  ///
808  /// See also [Size.center].
809  Offset get center => Offset(left + width / 2.0, top + height / 2.0);
810
811  /// The offset to the center of the right edge of this rectangle.
812  ///
813  /// See also [Size.centerLeft].
814  Offset get centerRight => Offset(right, top + height / 2.0);
815
816  /// The offset to the intersection of the bottom and left edges of this rectangle.
817  ///
818  /// See also [Size.bottomLeft].
819  Offset get bottomLeft => Offset(left, bottom);
820
821  /// The offset to the center of the bottom edge of this rectangle.
822  ///
823  /// See also [Size.bottomLeft].
824  Offset get bottomCenter => Offset(left + width / 2.0, bottom);
825
826  /// The offset to the intersection of the bottom and right edges of this rectangle.
827  ///
828  /// See also [Size.bottomRight].
829  Offset get bottomRight => Offset(right, bottom);
830
831  /// Whether the point specified by the given offset (which is assumed to be
832  /// relative to the origin) lies between the left and right and the top and
833  /// bottom edges of this rectangle.
834  ///
835  /// Rectangles include their top and left edges but exclude their bottom and
836  /// right edges.
837  bool contains(Offset offset) {
838    return offset.dx >= left && offset.dx < right && offset.dy >= top && offset.dy < bottom;
839  }
840
841  /// Linearly interpolate between two rectangles.
842  ///
843  /// If either rect is null, [Rect.zero] is used as a substitute.
844  ///
845  /// The `t` argument represents position on the timeline, with 0.0 meaning
846  /// that the interpolation has not started, returning `a` (or something
847  /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
848  /// returning `b` (or something equivalent to `b`), and values in between
849  /// meaning that the interpolation is at the relevant point on the timeline
850  /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
851  /// 1.0, so negative values and values greater than 1.0 are valid (and can
852  /// easily be generated by curves such as [Curves.elasticInOut]).
853  ///
854  /// Values for `t` are usually obtained from an [Animation<double>], such as
855  /// an [AnimationController].
856  static Rect lerp(Rect a, Rect b, double t) {
857    assert(t != null);
858    if (a == null && b == null)
859      return null;
860    if (a == null)
861      return Rect.fromLTRB(b.left * t, b.top * t, b.right * t, b.bottom * t);
862    if (b == null) {
863      final double k = 1.0 - t;
864      return Rect.fromLTRB(a.left * k, a.top * k, a.right * k, a.bottom * k);
865    }
866    return Rect.fromLTRB(
867      lerpDouble(a.left, b.left, t),
868      lerpDouble(a.top, b.top, t),
869      lerpDouble(a.right, b.right, t),
870      lerpDouble(a.bottom, b.bottom, t),
871    );
872  }
873
874  @override
875  bool operator ==(dynamic other) {
876    if (identical(this, other))
877      return true;
878    if (runtimeType != other.runtimeType)
879      return false;
880    final Rect typedOther = other;
881    return left   == typedOther.left   &&
882           top    == typedOther.top    &&
883           right  == typedOther.right  &&
884           bottom == typedOther.bottom;
885  }
886
887  @override
888  int get hashCode => hashValues(left, top, right, bottom);
889
890  @override
891  String toString() => 'Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})';
892}
893
894/// A radius for either circular or elliptical shapes.
895class Radius {
896  /// Constructs a circular radius. [x] and [y] will have the same radius value.
897  const Radius.circular(double radius) : this.elliptical(radius, radius);
898
899  /// Constructs an elliptical radius with the given radii.
900  const Radius.elliptical(this.x, this.y);
901
902  /// The radius value on the horizontal axis.
903  final double x;
904
905  /// The radius value on the vertical axis.
906  final double y;
907
908  /// A radius with [x] and [y] values set to zero.
909  ///
910  /// You can use [Radius.zero] with [RRect] to have right-angle corners.
911  static const Radius zero = Radius.circular(0.0);
912
913  /// Unary negation operator.
914  ///
915  /// Returns a Radius with the distances negated.
916  ///
917  /// Radiuses with negative values aren't geometrically meaningful, but could
918  /// occur as part of expressions. For example, negating a radius of one pixel
919  /// and then adding the result to another radius is equivalent to subtracting
920  /// a radius of one pixel from the other.
921  Radius operator -() => Radius.elliptical(-x, -y);
922
923  /// Binary subtraction operator.
924  ///
925  /// Returns a radius whose [x] value is the left-hand-side operand's [x]
926  /// minus the right-hand-side operand's [x] and whose [y] value is the
927  /// left-hand-side operand's [y] minus the right-hand-side operand's [y].
928  Radius operator -(Radius other) => Radius.elliptical(x - other.x, y - other.y);
929
930  /// Binary addition operator.
931  ///
932  /// Returns a radius whose [x] value is the sum of the [x] values of the
933  /// two operands, and whose [y] value is the sum of the [y] values of the
934  /// two operands.
935  Radius operator +(Radius other) => Radius.elliptical(x + other.x, y + other.y);
936
937  /// Multiplication operator.
938  ///
939  /// Returns a radius whose coordinates are the coordinates of the
940  /// left-hand-side operand (a radius) multiplied by the scalar
941  /// right-hand-side operand (a double).
942  Radius operator *(double operand) => Radius.elliptical(x * operand, y * operand);
943
944  /// Division operator.
945  ///
946  /// Returns a radius whose coordinates are the coordinates of the
947  /// left-hand-side operand (a radius) divided by the scalar right-hand-side
948  /// operand (a double).
949  Radius operator /(double operand) => Radius.elliptical(x / operand, y / operand);
950
951  /// Integer (truncating) division operator.
952  ///
953  /// Returns a radius whose coordinates are the coordinates of the
954  /// left-hand-side operand (a radius) divided by the scalar right-hand-side
955  /// operand (a double), rounded towards zero.
956  Radius operator ~/(double operand) => Radius.elliptical((x ~/ operand).toDouble(), (y ~/ operand).toDouble());
957
958  /// Modulo (remainder) operator.
959  ///
960  /// Returns a radius whose coordinates are the remainder of dividing the
961  /// coordinates of the left-hand-side operand (a radius) by the scalar
962  /// right-hand-side operand (a double).
963  Radius operator %(double operand) => Radius.elliptical(x % operand, y % operand);
964
965  /// Linearly interpolate between two radii.
966  ///
967  /// If either is null, this function substitutes [Radius.zero] instead.
968  ///
969  /// The `t` argument represents position on the timeline, with 0.0 meaning
970  /// that the interpolation has not started, returning `a` (or something
971  /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
972  /// returning `b` (or something equivalent to `b`), and values in between
973  /// meaning that the interpolation is at the relevant point on the timeline
974  /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
975  /// 1.0, so negative values and values greater than 1.0 are valid (and can
976  /// easily be generated by curves such as [Curves.elasticInOut]).
977  ///
978  /// Values for `t` are usually obtained from an [Animation<double>], such as
979  /// an [AnimationController].
980  static Radius lerp(Radius a, Radius b, double t) {
981    assert(t != null);
982    if (a == null && b == null)
983      return null;
984    if (a == null)
985      return Radius.elliptical(b.x * t, b.y * t);
986    if (b == null) {
987      final double k = 1.0 - t;
988      return Radius.elliptical(a.x * k, a.y * k);
989    }
990    return Radius.elliptical(
991      lerpDouble(a.x, b.x, t),
992      lerpDouble(a.y, b.y, t),
993    );
994  }
995
996  @override
997  bool operator ==(dynamic other) {
998    if (identical(this, other))
999      return true;
1000    if (runtimeType != other.runtimeType)
1001      return false;
1002    final Radius typedOther = other;
1003    return typedOther.x == x && typedOther.y == y;
1004  }
1005
1006  @override
1007  int get hashCode => hashValues(x, y);
1008
1009  @override
1010  String toString() {
1011    return x == y ? 'Radius.circular(${x.toStringAsFixed(1)})' :
1012                    'Radius.elliptical(${x.toStringAsFixed(1)}, '
1013                    '${y.toStringAsFixed(1)})';
1014  }
1015}
1016
1017/// An immutable rounded rectangle with the custom radii for all four corners.
1018class RRect {
1019  /// Construct a rounded rectangle from its left, top, right, and bottom edges,
1020  /// and the same radii along its horizontal axis and its vertical axis.
1021  const RRect.fromLTRBXY(double left, double top, double right, double bottom,
1022                   double radiusX, double radiusY) : this._raw(
1023    top: top,
1024    left: left,
1025    right: right,
1026    bottom: bottom,
1027    tlRadiusX: radiusX,
1028    tlRadiusY: radiusY,
1029    trRadiusX: radiusX,
1030    trRadiusY: radiusY,
1031    blRadiusX: radiusX,
1032    blRadiusY: radiusY,
1033    brRadiusX: radiusX,
1034    brRadiusY: radiusY,
1035  );
1036
1037  /// Construct a rounded rectangle from its left, top, right, and bottom edges,
1038  /// and the same radius in each corner.
1039  RRect.fromLTRBR(double left, double top, double right, double bottom,
1040                  Radius radius)
1041    : this._raw(
1042        top: top,
1043        left: left,
1044        right: right,
1045        bottom: bottom,
1046        tlRadiusX: radius.x,
1047        tlRadiusY: radius.y,
1048        trRadiusX: radius.x,
1049        trRadiusY: radius.y,
1050        blRadiusX: radius.x,
1051        blRadiusY: radius.y,
1052        brRadiusX: radius.x,
1053        brRadiusY: radius.y,
1054      );
1055
1056  /// Construct a rounded rectangle from its bounding box and the same radii
1057  /// along its horizontal axis and its vertical axis.
1058  RRect.fromRectXY(Rect rect, double radiusX, double radiusY)
1059    : this._raw(
1060        top: rect.top,
1061        left: rect.left,
1062        right: rect.right,
1063        bottom: rect.bottom,
1064        tlRadiusX: radiusX,
1065        tlRadiusY: radiusY,
1066        trRadiusX: radiusX,
1067        trRadiusY: radiusY,
1068        blRadiusX: radiusX,
1069        blRadiusY: radiusY,
1070        brRadiusX: radiusX,
1071        brRadiusY: radiusY,
1072      );
1073
1074  /// Construct a rounded rectangle from its bounding box and a radius that is
1075  /// the same in each corner.
1076  RRect.fromRectAndRadius(Rect rect, Radius radius)
1077    : this._raw(
1078        top: rect.top,
1079        left: rect.left,
1080        right: rect.right,
1081        bottom: rect.bottom,
1082        tlRadiusX: radius.x,
1083        tlRadiusY: radius.y,
1084        trRadiusX: radius.x,
1085        trRadiusY: radius.y,
1086        blRadiusX: radius.x,
1087        blRadiusY: radius.y,
1088        brRadiusX: radius.x,
1089        brRadiusY: radius.y,
1090      );
1091
1092  /// Construct a rounded rectangle from its left, top, right, and bottom edges,
1093  /// and topLeft, topRight, bottomRight, and bottomLeft radii.
1094  ///
1095  /// The corner radii default to [Radius.zero], i.e. right-angled corners.
1096  RRect.fromLTRBAndCorners(
1097    double left,
1098    double top,
1099    double right,
1100    double bottom, {
1101    Radius topLeft = Radius.zero,
1102    Radius topRight = Radius.zero,
1103    Radius bottomRight = Radius.zero,
1104    Radius bottomLeft = Radius.zero,
1105  }) : this._raw(
1106         top: top,
1107         left: left,
1108         right: right,
1109         bottom: bottom,
1110         tlRadiusX: topLeft.x,
1111         tlRadiusY: topLeft.y,
1112         trRadiusX: topRight.x,
1113         trRadiusY: topRight.y,
1114         blRadiusX: bottomLeft.x,
1115         blRadiusY: bottomLeft.y,
1116         brRadiusX: bottomRight.x,
1117         brRadiusY: bottomRight.y,
1118       );
1119
1120  /// Construct a rounded rectangle from its bounding box and and topLeft,
1121  /// topRight, bottomRight, and bottomLeft radii.
1122  ///
1123  /// The corner radii default to [Radius.zero], i.e. right-angled corners
1124  RRect.fromRectAndCorners(
1125    Rect rect,
1126    {
1127      Radius topLeft = Radius.zero,
1128      Radius topRight = Radius.zero,
1129      Radius bottomRight = Radius.zero,
1130      Radius bottomLeft = Radius.zero
1131    }
1132  ) : this._raw(
1133        top: rect.top,
1134        left: rect.left,
1135        right: rect.right,
1136        bottom: rect.bottom,
1137        tlRadiusX: topLeft.x,
1138        tlRadiusY: topLeft.y,
1139        trRadiusX: topRight.x,
1140        trRadiusY: topRight.y,
1141        blRadiusX: bottomLeft.x,
1142        blRadiusY: bottomLeft.y,
1143        brRadiusX: bottomRight.x,
1144        brRadiusY: bottomRight.y,
1145      );
1146
1147  const RRect._raw({
1148    this.left = 0.0,
1149    this.top = 0.0,
1150    this.right = 0.0,
1151    this.bottom = 0.0,
1152    this.tlRadiusX = 0.0,
1153    this.tlRadiusY = 0.0,
1154    this.trRadiusX = 0.0,
1155    this.trRadiusY = 0.0,
1156    this.brRadiusX = 0.0,
1157    this.brRadiusY = 0.0,
1158    this.blRadiusX = 0.0,
1159    this.blRadiusY = 0.0,
1160  }) : assert(left != null),
1161       assert(top != null),
1162       assert(right != null),
1163       assert(bottom != null),
1164       assert(tlRadiusX != null),
1165       assert(tlRadiusY != null),
1166       assert(trRadiusX != null),
1167       assert(trRadiusY != null),
1168       assert(brRadiusX != null),
1169       assert(brRadiusY != null),
1170       assert(blRadiusX != null),
1171       assert(blRadiusY != null);
1172
1173  Float32List get _value32 => Float32List.fromList(<double>[
1174    left,
1175    top,
1176    right,
1177    bottom,
1178    tlRadiusX,
1179    tlRadiusY,
1180    trRadiusX,
1181    trRadiusY,
1182    brRadiusX,
1183    brRadiusY,
1184    blRadiusX,
1185    blRadiusY,
1186  ]);
1187
1188  /// The offset of the left edge of this rectangle from the x axis.
1189  final double left;
1190
1191  /// The offset of the top edge of this rectangle from the y axis.
1192  final double top;
1193
1194  /// The offset of the right edge of this rectangle from the x axis.
1195  final double right;
1196
1197  /// The offset of the bottom edge of this rectangle from the y axis.
1198  final double bottom;
1199
1200  /// The top-left horizontal radius.
1201  final double tlRadiusX;
1202
1203  /// The top-left vertical radius.
1204  final double tlRadiusY;
1205
1206  /// The top-left [Radius].
1207  Radius get tlRadius => Radius.elliptical(tlRadiusX, tlRadiusY);
1208
1209  /// The top-right horizontal radius.
1210  final double trRadiusX;
1211
1212  /// The top-right vertical radius.
1213  final double trRadiusY;
1214
1215  /// The top-right [Radius].
1216  Radius get trRadius => Radius.elliptical(trRadiusX, trRadiusY);
1217
1218  /// The bottom-right horizontal radius.
1219  final double brRadiusX;
1220
1221  /// The bottom-right vertical radius.
1222  final double brRadiusY;
1223
1224  /// The bottom-right [Radius].
1225  Radius get brRadius => Radius.elliptical(brRadiusX, brRadiusY);
1226
1227  /// The bottom-left horizontal radius.
1228  final double blRadiusX;
1229
1230  /// The bottom-left vertical radius.
1231  final double blRadiusY;
1232
1233  /// The bottom-left [Radius].
1234  Radius get blRadius => Radius.elliptical(blRadiusX, blRadiusY);
1235
1236  /// A rounded rectangle with all the values set to zero.
1237  static const RRect zero = RRect._raw();
1238
1239  /// Returns a new [RRect] translated by the given offset.
1240  RRect shift(Offset offset) {
1241    return RRect._raw(
1242      left: left + offset.dx,
1243      top: top + offset.dy,
1244      right: right + offset.dx,
1245      bottom: bottom + offset.dy,
1246      tlRadiusX: tlRadiusX,
1247      tlRadiusY: tlRadiusY,
1248      trRadiusX: trRadiusX,
1249      trRadiusY: trRadiusY,
1250      blRadiusX: blRadiusX,
1251      blRadiusY: blRadiusY,
1252      brRadiusX: brRadiusX,
1253      brRadiusY: brRadiusY,
1254    );
1255  }
1256
1257  /// Returns a new [RRect] with edges and radii moved outwards by the given
1258  /// delta.
1259  RRect inflate(double delta) {
1260    return RRect._raw(
1261      left: left - delta,
1262      top: top - delta,
1263      right: right + delta,
1264      bottom: bottom + delta,
1265      tlRadiusX: tlRadiusX + delta,
1266      tlRadiusY: tlRadiusY + delta,
1267      trRadiusX: trRadiusX + delta,
1268      trRadiusY: trRadiusY + delta,
1269      blRadiusX: blRadiusX + delta,
1270      blRadiusY: blRadiusY + delta,
1271      brRadiusX: brRadiusX + delta,
1272      brRadiusY: brRadiusY + delta,
1273    );
1274  }
1275
1276  /// Returns a new [RRect] with edges and radii moved inwards by the given delta.
1277  RRect deflate(double delta) => inflate(-delta);
1278
1279  /// The distance between the left and right edges of this rectangle.
1280  double get width => right - left;
1281
1282  /// The distance between the top and bottom edges of this rectangle.
1283  double get height => bottom - top;
1284
1285  /// The bounding box of this rounded rectangle (the rectangle with no rounded corners).
1286  Rect get outerRect => Rect.fromLTRB(left, top, right, bottom);
1287
1288  /// The non-rounded rectangle that is constrained by the smaller of the two
1289  /// diagonals, with each diagonal traveling through the middle of the curve
1290  /// corners. The middle of a corner is the intersection of the curve with its
1291  /// respective quadrant bisector.
1292  Rect get safeInnerRect {
1293    const double kInsetFactor = 0.29289321881; // 1-cos(pi/4)
1294
1295    final double leftRadius = math.max(blRadiusX, tlRadiusX);
1296    final double topRadius = math.max(tlRadiusY, trRadiusY);
1297    final double rightRadius = math.max(trRadiusX, brRadiusX);
1298    final double bottomRadius = math.max(brRadiusY, blRadiusY);
1299
1300    return Rect.fromLTRB(
1301      left + leftRadius * kInsetFactor,
1302      top + topRadius * kInsetFactor,
1303      right - rightRadius * kInsetFactor,
1304      bottom - bottomRadius * kInsetFactor
1305    );
1306  }
1307
1308  /// The rectangle that would be formed using the axis-aligned intersection of
1309  /// the sides of the rectangle, i.e., the rectangle formed from the
1310  /// inner-most centers of the ellipses that form the corners. This is the
1311  /// intersection of the [wideMiddleRect] and the [tallMiddleRect]. If any of
1312  /// the intersections are void, the resulting [Rect] will have negative width
1313  /// or height.
1314  Rect get middleRect {
1315    final double leftRadius = math.max(blRadiusX, tlRadiusX);
1316    final double topRadius = math.max(tlRadiusY, trRadiusY);
1317    final double rightRadius = math.max(trRadiusX, brRadiusX);
1318    final double bottomRadius = math.max(brRadiusY, blRadiusY);
1319    return Rect.fromLTRB(
1320      left + leftRadius,
1321      top + topRadius,
1322      right - rightRadius,
1323      bottom - bottomRadius
1324    );
1325  }
1326
1327  /// The biggest rectangle that is entirely inside the rounded rectangle and
1328  /// has the full width of the rounded rectangle. If the rounded rectangle does
1329  /// not have an axis-aligned intersection of its left and right side, the
1330  /// resulting [Rect] will have negative width or height.
1331  Rect get wideMiddleRect {
1332    final double topRadius = math.max(tlRadiusY, trRadiusY);
1333    final double bottomRadius = math.max(brRadiusY, blRadiusY);
1334    return Rect.fromLTRB(
1335      left,
1336      top + topRadius,
1337      right,
1338      bottom - bottomRadius
1339    );
1340  }
1341
1342  /// The biggest rectangle that is entirely inside the rounded rectangle and
1343  /// has the full height of the rounded rectangle. If the rounded rectangle
1344  /// does not have an axis-aligned intersection of its top and bottom side, the
1345  /// resulting [Rect] will have negative width or height.
1346  Rect get tallMiddleRect {
1347    final double leftRadius = math.max(blRadiusX, tlRadiusX);
1348    final double rightRadius = math.max(trRadiusX, brRadiusX);
1349    return Rect.fromLTRB(
1350      left + leftRadius,
1351      top,
1352      right - rightRadius,
1353      bottom
1354    );
1355  }
1356
1357  /// Whether this rounded rectangle encloses a non-zero area.
1358  /// Negative areas are considered empty.
1359  bool get isEmpty => left >= right || top >= bottom;
1360
1361  /// Whether all coordinates of this rounded rectangle are finite.
1362  bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite;
1363
1364  /// Whether this rounded rectangle is a simple rectangle with zero
1365  /// corner radii.
1366  bool get isRect {
1367    return (tlRadiusX == 0.0 || tlRadiusY == 0.0) &&
1368           (trRadiusX == 0.0 || trRadiusY == 0.0) &&
1369           (blRadiusX == 0.0 || blRadiusY == 0.0) &&
1370           (brRadiusX == 0.0 || brRadiusY == 0.0);
1371  }
1372
1373  /// Whether this rounded rectangle has a side with no straight section.
1374  bool get isStadium {
1375    return tlRadius == trRadius
1376        && trRadius == brRadius
1377        && brRadius == blRadius
1378        && (width <= 2.0 * tlRadiusX || height <= 2.0 * tlRadiusY);
1379  }
1380
1381  /// Whether this rounded rectangle has no side with a straight section.
1382  bool get isEllipse {
1383    return tlRadius == trRadius
1384        && trRadius == brRadius
1385        && brRadius == blRadius
1386        && width <= 2.0 * tlRadiusX
1387        && height <= 2.0 * tlRadiusY;
1388  }
1389
1390  /// Whether this rounded rectangle would draw as a circle.
1391  bool get isCircle => width == height && isEllipse;
1392
1393  /// The lesser of the magnitudes of the [width] and the [height] of this
1394  /// rounded rectangle.
1395  double get shortestSide => math.min(width.abs(), height.abs());
1396
1397  /// The greater of the magnitudes of the [width] and the [height] of this
1398  /// rounded rectangle.
1399  double get longestSide => math.max(width.abs(), height.abs());
1400
1401  /// Whether any of the dimensions are `NaN`.
1402  bool get hasNaN => left.isNaN || top.isNaN || right.isNaN || bottom.isNaN ||
1403                     trRadiusX.isNaN || trRadiusY.isNaN || tlRadiusX.isNaN || tlRadiusY.isNaN ||
1404                     brRadiusX.isNaN || brRadiusY.isNaN || blRadiusX.isNaN || blRadiusY.isNaN;
1405
1406  /// The offset to the point halfway between the left and right and the top and
1407  /// bottom edges of this rectangle.
1408  Offset get center => Offset(left + width / 2.0, top + height / 2.0);
1409
1410  // Returns the minimum between min and scale to which radius1 and radius2
1411  // should be scaled with in order not to exceed the limit.
1412  double _getMin(double min, double radius1, double radius2, double limit) {
1413    final double sum = radius1 + radius2;
1414    if (sum > limit && sum != 0.0)
1415      return math.min(min, limit / sum);
1416    return min;
1417  }
1418
1419  /// Scales all radii so that on each side their sum will not exceed the size
1420  /// of the width/height.
1421  ///
1422  /// Skia already handles RRects with radii that are too large in this way.
1423  /// Therefore, this method is only needed for RRect use cases that require
1424  /// the appropriately scaled radii values.
1425  ///
1426  /// See the [Skia scaling implementation](https://github.com/google/skia/blob/master/src/core/SkRRect.cpp)
1427  /// for more details.
1428  RRect scaleRadii() {
1429    double scale = 1.0;
1430    scale = _getMin(scale, blRadiusY, tlRadiusY, height);
1431    scale = _getMin(scale, tlRadiusX, trRadiusX, width);
1432    scale = _getMin(scale, trRadiusY, brRadiusY, height);
1433    scale = _getMin(scale, brRadiusX, blRadiusX, width);
1434
1435    if (scale < 1.0) {
1436      return RRect._raw(
1437        top: top,
1438        left: left,
1439        right: right,
1440        bottom: bottom,
1441        tlRadiusX: tlRadiusX * scale,
1442        tlRadiusY: tlRadiusY * scale,
1443        trRadiusX: trRadiusX * scale,
1444        trRadiusY: trRadiusY * scale,
1445        blRadiusX: blRadiusX * scale,
1446        blRadiusY: blRadiusY * scale,
1447        brRadiusX: brRadiusX * scale,
1448        brRadiusY: brRadiusY * scale,
1449      );
1450    }
1451
1452    return RRect._raw(
1453      top: top,
1454      left: left,
1455      right: right,
1456      bottom: bottom,
1457      tlRadiusX: tlRadiusX,
1458      tlRadiusY: tlRadiusY,
1459      trRadiusX: trRadiusX,
1460      trRadiusY: trRadiusY,
1461      blRadiusX: blRadiusX,
1462      blRadiusY: blRadiusY,
1463      brRadiusX: brRadiusX,
1464      brRadiusY: brRadiusY,
1465    );
1466  }
1467
1468  /// Whether the point specified by the given offset (which is assumed to be
1469  /// relative to the origin) lies inside the rounded rectangle.
1470  ///
1471  /// This method may allocate (and cache) a copy of the object with normalized
1472  /// radii the first time it is called on a particular [RRect] instance. When
1473  /// using this method, prefer to reuse existing [RRect]s rather than
1474  /// recreating the object each time.
1475  bool contains(Offset point) {
1476    if (point.dx < left || point.dx >= right || point.dy < top || point.dy >= bottom)
1477      return false; // outside bounding box
1478
1479    final RRect scaled = scaleRadii();
1480
1481    double x;
1482    double y;
1483    double radiusX;
1484    double radiusY;
1485    // check whether point is in one of the rounded corner areas
1486    // x, y -> translate to ellipse center
1487    if (point.dx < left + scaled.tlRadiusX &&
1488        point.dy < top + scaled.tlRadiusY) {
1489      x = point.dx - left - scaled.tlRadiusX;
1490      y = point.dy - top - scaled.tlRadiusY;
1491      radiusX = scaled.tlRadiusX;
1492      radiusY = scaled.tlRadiusY;
1493    } else if (point.dx > right - scaled.trRadiusX &&
1494               point.dy < top + scaled.trRadiusY) {
1495      x = point.dx - right + scaled.trRadiusX;
1496      y = point.dy - top - scaled.trRadiusY;
1497      radiusX = scaled.trRadiusX;
1498      radiusY = scaled.trRadiusY;
1499    } else if (point.dx > right - scaled.brRadiusX &&
1500               point.dy > bottom - scaled.brRadiusY) {
1501      x = point.dx - right + scaled.brRadiusX;
1502      y = point.dy - bottom + scaled.brRadiusY;
1503      radiusX = scaled.brRadiusX;
1504      radiusY = scaled.brRadiusY;
1505    } else if (point.dx < left + scaled.blRadiusX &&
1506               point.dy > bottom - scaled.blRadiusY) {
1507      x = point.dx - left - scaled.blRadiusX;
1508      y = point.dy - bottom + scaled.blRadiusY;
1509      radiusX = scaled.blRadiusX;
1510      radiusY = scaled.blRadiusY;
1511    } else {
1512      return true; // inside and not within the rounded corner area
1513    }
1514
1515    x = x / radiusX;
1516    y = y / radiusY;
1517    // check if the point is outside the unit circle
1518    if (x * x + y * y > 1.0)
1519      return false;
1520    return true;
1521  }
1522
1523  /// Linearly interpolate between two rounded rectangles.
1524  ///
1525  /// If either is null, this function substitutes [RRect.zero] instead.
1526  ///
1527  /// The `t` argument represents position on the timeline, with 0.0 meaning
1528  /// that the interpolation has not started, returning `a` (or something
1529  /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
1530  /// returning `b` (or something equivalent to `b`), and values in between
1531  /// meaning that the interpolation is at the relevant point on the timeline
1532  /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
1533  /// 1.0, so negative values and values greater than 1.0 are valid (and can
1534  /// easily be generated by curves such as [Curves.elasticInOut]).
1535  ///
1536  /// Values for `t` are usually obtained from an [Animation<double>], such as
1537  /// an [AnimationController].
1538  static RRect lerp(RRect a, RRect b, double t) {
1539    assert(t != null);
1540    if (a == null && b == null)
1541      return null;
1542    if (a == null) {
1543      return RRect._raw(
1544        left: b.left * t,
1545        top: b.top * t,
1546        right: b.right * t,
1547        bottom: b.bottom * t,
1548        tlRadiusX: b.tlRadiusX * t,
1549        tlRadiusY: b.tlRadiusY * t,
1550        trRadiusX: b.trRadiusX * t,
1551        trRadiusY: b.trRadiusY * t,
1552        brRadiusX: b.brRadiusX * t,
1553        brRadiusY: b.brRadiusY * t,
1554        blRadiusX: b.blRadiusX * t,
1555        blRadiusY: b.blRadiusY * t,
1556      );
1557    }
1558    if (b == null) {
1559      final double k = 1.0 - t;
1560      return RRect._raw(
1561        left: a.left * k,
1562        top: a.top * k,
1563        right: a.right * k,
1564        bottom: a.bottom * k,
1565        tlRadiusX: a.tlRadiusX * k,
1566        tlRadiusY: a.tlRadiusY * k,
1567        trRadiusX: a.trRadiusX * k,
1568        trRadiusY: a.trRadiusY * k,
1569        brRadiusX: a.brRadiusX * k,
1570        brRadiusY: a.brRadiusY * k,
1571        blRadiusX: a.blRadiusX * k,
1572        blRadiusY: a.blRadiusY * k,
1573      );
1574    }
1575    return RRect._raw(
1576      left: lerpDouble(a.left, b.left, t),
1577      top: lerpDouble(a.top, b.top, t),
1578      right: lerpDouble(a.right, b.right, t),
1579      bottom: lerpDouble(a.bottom, b.bottom, t),
1580      tlRadiusX: lerpDouble(a.tlRadiusX, b.tlRadiusX, t),
1581      tlRadiusY: lerpDouble(a.tlRadiusY, b.tlRadiusY, t),
1582      trRadiusX: lerpDouble(a.trRadiusX, b.trRadiusX, t),
1583      trRadiusY: lerpDouble(a.trRadiusY, b.trRadiusY, t),
1584      brRadiusX: lerpDouble(a.brRadiusX, b.brRadiusX, t),
1585      brRadiusY: lerpDouble(a.brRadiusY, b.brRadiusY, t),
1586      blRadiusX: lerpDouble(a.blRadiusX, b.blRadiusX, t),
1587      blRadiusY: lerpDouble(a.blRadiusY, b.blRadiusY, t),
1588    );
1589  }
1590
1591  @override
1592  bool operator ==(dynamic other) {
1593    if (identical(this, other))
1594      return true;
1595    if (runtimeType != other.runtimeType)
1596      return false;
1597    final RRect typedOther = other;
1598    return left      == typedOther.left      &&
1599           top       == typedOther.top       &&
1600           right     == typedOther.right     &&
1601           bottom    == typedOther.bottom    &&
1602           tlRadiusX == typedOther.tlRadiusX &&
1603           tlRadiusY == typedOther.tlRadiusY &&
1604           trRadiusX == typedOther.trRadiusX &&
1605           trRadiusY == typedOther.trRadiusY &&
1606           blRadiusX == typedOther.blRadiusX &&
1607           blRadiusY == typedOther.blRadiusY &&
1608           brRadiusX == typedOther.brRadiusX &&
1609           brRadiusY == typedOther.brRadiusY;
1610  }
1611
1612  @override
1613  int get hashCode => hashValues(left, top, right, bottom,
1614    tlRadiusX, tlRadiusY, trRadiusX, trRadiusY,
1615    blRadiusX, blRadiusY, brRadiusX, brRadiusY);
1616
1617  @override
1618  String toString() {
1619    final String rect = '${left.toStringAsFixed(1)}, '
1620                        '${top.toStringAsFixed(1)}, '
1621                        '${right.toStringAsFixed(1)}, '
1622                        '${bottom.toStringAsFixed(1)}';
1623    if (tlRadius == trRadius &&
1624        trRadius == brRadius &&
1625        brRadius == blRadius) {
1626      if (tlRadius.x == tlRadius.y)
1627        return 'RRect.fromLTRBR($rect, ${tlRadius.x.toStringAsFixed(1)})';
1628      return 'RRect.fromLTRBXY($rect, ${tlRadius.x.toStringAsFixed(1)}, ${tlRadius.y.toStringAsFixed(1)})';
1629    }
1630    return 'RRect.fromLTRBAndCorners('
1631             '$rect, '
1632             'topLeft: $tlRadius, '
1633             'topRight: $trRadius, '
1634             'bottomRight: $brRadius, '
1635             'bottomLeft: $blRadius'
1636           ')';
1637  }
1638}
1639
1640/// A transform consisting of a translation, a rotation, and a uniform scale.
1641///
1642/// Used by [Canvas.drawAtlas]. This is a more efficient way to represent these
1643/// simple transformations than a full matrix.
1644// Modeled after Skia's SkRSXform.
1645class RSTransform {
1646  /// Creates an RSTransform.
1647  ///
1648  /// An [RSTransform] expresses the combination of a translation, a rotation
1649  /// around a particular point, and a scale factor.
1650  ///
1651  /// The first argument, `scos`, is the cosine of the rotation, multiplied by
1652  /// the scale factor.
1653  ///
1654  /// The second argument, `ssin`, is the sine of the rotation, multiplied by
1655  /// that same scale factor.
1656  ///
1657  /// The third argument is the x coordinate of the translation, minus the
1658  /// `scos` argument multiplied by the x-coordinate of the rotation point, plus
1659  /// the `ssin` argument multiplied by the y-coordinate of the rotation point.
1660  ///
1661  /// The fourth argument is the y coordinate of the translation, minus the `ssin`
1662  /// argument multiplied by the x-coordinate of the rotation point, minus the
1663  /// `scos` argument multiplied by the y-coordinate of the rotation point.
1664  ///
1665  /// The [RSTransform.fromComponents] method may be a simpler way to
1666  /// construct these values. However, if there is a way to factor out the
1667  /// computations of the sine and cosine of the rotation so that they can be
1668  /// reused over multiple calls to this constructor, it may be more efficient
1669  /// to directly use this constructor instead.
1670  RSTransform(double scos, double ssin, double tx, double ty) {
1671    _value
1672      ..[0] = scos
1673      ..[1] = ssin
1674      ..[2] = tx
1675      ..[3] = ty;
1676  }
1677
1678  /// Creates an RSTransform from its individual components.
1679  ///
1680  /// The `rotation` parameter gives the rotation in radians.
1681  ///
1682  /// The `scale` parameter describes the uniform scale factor.
1683  ///
1684  /// The `anchorX` and `anchorY` parameters give the coordinate of the point
1685  /// around which to rotate.
1686  ///
1687  /// The `translateX` and `translateY` parameters give the coordinate of the
1688  /// offset by which to translate.
1689  ///
1690  /// This constructor computes the arguments of the [new RSTransform]
1691  /// constructor and then defers to that constructor to actually create the
1692  /// object. If many [RSTransform] objects are being created and there is a way
1693  /// to factor out the computations of the sine and cosine of the rotation
1694  /// (which are computed each time this constructor is called) and reuse them
1695  /// over multiple [RSTransform] objects, it may be more efficient to directly
1696  /// use the more direct [new RSTransform] constructor instead.
1697  factory RSTransform.fromComponents({
1698    double rotation,
1699    double scale,
1700    double anchorX,
1701    double anchorY,
1702    double translateX,
1703    double translateY
1704  }) {
1705    final double scos = math.cos(rotation) * scale;
1706    final double ssin = math.sin(rotation) * scale;
1707    final double tx = translateX + -scos * anchorX + ssin * anchorY;
1708    final double ty = translateY + -ssin * anchorX - scos * anchorY;
1709    return RSTransform(scos, ssin, tx, ty);
1710  }
1711
1712  final Float32List _value = Float32List(4);
1713
1714  /// The cosine of the rotation multiplied by the scale factor.
1715  double get scos => _value[0];
1716
1717  /// The sine of the rotation multiplied by that same scale factor.
1718  double get ssin => _value[1];
1719
1720  /// The x coordinate of the translation, minus [scos] multiplied by the
1721  /// x-coordinate of the rotation point, plus [ssin] multiplied by the
1722  /// y-coordinate of the rotation point.
1723  double get tx => _value[2];
1724
1725  /// The y coordinate of the translation, minus [ssin] multiplied by the
1726  /// x-coordinate of the rotation point, minus [scos] multiplied by the
1727  /// y-coordinate of the rotation point.
1728  double get ty => _value[3];
1729}
1730