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