Unverified Commit ecb708ee authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Clean up lerp() methods and their documentation. (#13684)

This cleans up lerp, lerpFrom, lerpTo, and scale methods, and their
documentation.

Fixes https://github.com/flutter/flutter/issues/12377.
parent 075ba647
...@@ -589,23 +589,39 @@ class _CupertinoBackGestureController { ...@@ -589,23 +589,39 @@ class _CupertinoBackGestureController {
class _CupertinoEdgeShadowDecoration extends Decoration { class _CupertinoEdgeShadowDecoration extends Decoration {
const _CupertinoEdgeShadowDecoration({ this.edgeGradient }); const _CupertinoEdgeShadowDecoration({ this.edgeGradient });
/// A Decoration with no decorating properties. // An edge shadow decoration where the shadow is null. This is used
// for interpolating from no shadow.
static const _CupertinoEdgeShadowDecoration none = static const _CupertinoEdgeShadowDecoration none =
const _CupertinoEdgeShadowDecoration(); const _CupertinoEdgeShadowDecoration();
/// A gradient to draw to the left of the box being decorated. // A gradient to draw to the left of the box being decorated.
/// Alignments are relative to the original box translated one box // Alignments are relative to the original box translated one box
/// width to the left. // width to the left.
final LinearGradient edgeGradient; final LinearGradient edgeGradient;
/// Linearly interpolate between two edge shadow decorations decorations. // Linearly interpolate between two edge shadow decorations decorations.
/// //
/// See also [Decoration.lerp]. // The `t` argument represents position on the timeline, with 0.0 meaning
// that the interpolation has not started, returning `a` (or something
// equivalent to `a`), 1.0 meaning that the interpolation has finished,
// returning `b` (or something equivalent to `b`), and values in between
// meaning that the interpolation is at the relevant point on the timeline
// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
// 1.0, so negative values and values greater than 1.0 are valid (and can
// easily be generated by curves such as [Curves.elasticInOut]).
//
// Values for `t` are usually obtained from an [Animation<double>], such as
// an [AnimationController].
//
// See also:
//
// * [Decoration.lerp].
static _CupertinoEdgeShadowDecoration lerp( static _CupertinoEdgeShadowDecoration lerp(
_CupertinoEdgeShadowDecoration a, _CupertinoEdgeShadowDecoration a,
_CupertinoEdgeShadowDecoration b, _CupertinoEdgeShadowDecoration b,
double t double t,
) { ) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
return new _CupertinoEdgeShadowDecoration( return new _CupertinoEdgeShadowDecoration(
......
...@@ -519,40 +519,53 @@ class ThemeData { ...@@ -519,40 +519,53 @@ class ThemeData {
/// Linearly interpolate between two themes. /// Linearly interpolate between two themes.
/// ///
/// The arguments must not be null. /// The arguments must not be null.
static ThemeData lerp(ThemeData begin, ThemeData end, double t) { ///
assert(begin != null); /// The `t` argument represents position on the timeline, with 0.0 meaning
assert(end != null); /// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static ThemeData lerp(ThemeData a, ThemeData b, double t) {
assert(a != null);
assert(b != null);
assert(t != null);
return new ThemeData.raw( return new ThemeData.raw(
brightness: t < 0.5 ? begin.brightness : end.brightness, brightness: t < 0.5 ? a.brightness : b.brightness,
primaryColor: Color.lerp(begin.primaryColor, end.primaryColor, t), primaryColor: Color.lerp(a.primaryColor, b.primaryColor, t),
primaryColorBrightness: t < 0.5 ? begin.primaryColorBrightness : end.primaryColorBrightness, primaryColorBrightness: t < 0.5 ? a.primaryColorBrightness : b.primaryColorBrightness,
canvasColor: Color.lerp(begin.canvasColor, end.canvasColor, t), canvasColor: Color.lerp(a.canvasColor, b.canvasColor, t),
scaffoldBackgroundColor: Color.lerp(begin.scaffoldBackgroundColor, end.scaffoldBackgroundColor, t), scaffoldBackgroundColor: Color.lerp(a.scaffoldBackgroundColor, b.scaffoldBackgroundColor, t),
cardColor: Color.lerp(begin.cardColor, end.cardColor, t), cardColor: Color.lerp(a.cardColor, b.cardColor, t),
dividerColor: Color.lerp(begin.dividerColor, end.dividerColor, t), dividerColor: Color.lerp(a.dividerColor, b.dividerColor, t),
highlightColor: Color.lerp(begin.highlightColor, end.highlightColor, t), highlightColor: Color.lerp(a.highlightColor, b.highlightColor, t),
splashColor: Color.lerp(begin.splashColor, end.splashColor, t), splashColor: Color.lerp(a.splashColor, b.splashColor, t),
selectedRowColor: Color.lerp(begin.selectedRowColor, end.selectedRowColor, t), selectedRowColor: Color.lerp(a.selectedRowColor, b.selectedRowColor, t),
unselectedWidgetColor: Color.lerp(begin.unselectedWidgetColor, end.unselectedWidgetColor, t), unselectedWidgetColor: Color.lerp(a.unselectedWidgetColor, b.unselectedWidgetColor, t),
disabledColor: Color.lerp(begin.disabledColor, end.disabledColor, t), disabledColor: Color.lerp(a.disabledColor, b.disabledColor, t),
buttonColor: Color.lerp(begin.buttonColor, end.buttonColor, t), buttonColor: Color.lerp(a.buttonColor, b.buttonColor, t),
secondaryHeaderColor: Color.lerp(begin.secondaryHeaderColor, end.secondaryHeaderColor, t), secondaryHeaderColor: Color.lerp(a.secondaryHeaderColor, b.secondaryHeaderColor, t),
textSelectionColor: Color.lerp(begin.textSelectionColor, end.textSelectionColor, t), textSelectionColor: Color.lerp(a.textSelectionColor, b.textSelectionColor, t),
textSelectionHandleColor: Color.lerp(begin.textSelectionHandleColor, end.textSelectionHandleColor, t), textSelectionHandleColor: Color.lerp(a.textSelectionHandleColor, b.textSelectionHandleColor, t),
backgroundColor: Color.lerp(begin.backgroundColor, end.backgroundColor, t), backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
dialogBackgroundColor: Color.lerp(begin.dialogBackgroundColor, end.dialogBackgroundColor, t), dialogBackgroundColor: Color.lerp(a.dialogBackgroundColor, b.dialogBackgroundColor, t),
accentColor: Color.lerp(begin.accentColor, end.accentColor, t), accentColor: Color.lerp(a.accentColor, b.accentColor, t),
accentColorBrightness: t < 0.5 ? begin.accentColorBrightness : end.accentColorBrightness, accentColorBrightness: t < 0.5 ? a.accentColorBrightness : b.accentColorBrightness,
indicatorColor: Color.lerp(begin.indicatorColor, end.indicatorColor, t), indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t),
hintColor: Color.lerp(begin.hintColor, end.hintColor, t), hintColor: Color.lerp(a.hintColor, b.hintColor, t),
errorColor: Color.lerp(begin.errorColor, end.errorColor, t), errorColor: Color.lerp(a.errorColor, b.errorColor, t),
textTheme: TextTheme.lerp(begin.textTheme, end.textTheme, t), textTheme: TextTheme.lerp(a.textTheme, b.textTheme, t),
primaryTextTheme: TextTheme.lerp(begin.primaryTextTheme, end.primaryTextTheme, t), primaryTextTheme: TextTheme.lerp(a.primaryTextTheme, b.primaryTextTheme, t),
accentTextTheme: TextTheme.lerp(begin.accentTextTheme, end.accentTextTheme, t), accentTextTheme: TextTheme.lerp(a.accentTextTheme, b.accentTextTheme, t),
iconTheme: IconThemeData.lerp(begin.iconTheme, end.iconTheme, t), iconTheme: IconThemeData.lerp(a.iconTheme, b.iconTheme, t),
primaryIconTheme: IconThemeData.lerp(begin.primaryIconTheme, end.primaryIconTheme, t), primaryIconTheme: IconThemeData.lerp(a.primaryIconTheme, b.primaryIconTheme, t),
accentIconTheme: IconThemeData.lerp(begin.accentIconTheme, end.accentIconTheme, t), accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t),
platform: t < 0.5 ? begin.platform : end.platform platform: t < 0.5 ? a.platform : b.platform,
); );
} }
......
...@@ -284,19 +284,36 @@ class TextTheme { ...@@ -284,19 +284,36 @@ class TextTheme {
} }
/// Linearly interpolate between two text themes. /// Linearly interpolate between two text themes.
static TextTheme lerp(TextTheme begin, TextTheme end, double t) { ///
/// The arguments must not be null.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static TextTheme lerp(TextTheme a, TextTheme b, double t) {
assert(a != null);
assert(b != null);
assert(t != null);
return new TextTheme( return new TextTheme(
display4: TextStyle.lerp(begin.display4, end.display4, t), display4: TextStyle.lerp(a.display4, b.display4, t),
display3: TextStyle.lerp(begin.display3, end.display3, t), display3: TextStyle.lerp(a.display3, b.display3, t),
display2: TextStyle.lerp(begin.display2, end.display2, t), display2: TextStyle.lerp(a.display2, b.display2, t),
display1: TextStyle.lerp(begin.display1, end.display1, t), display1: TextStyle.lerp(a.display1, b.display1, t),
headline: TextStyle.lerp(begin.headline, end.headline, t), headline: TextStyle.lerp(a.headline, b.headline, t),
title: TextStyle.lerp(begin.title, end.title, t), title: TextStyle.lerp(a.title, b.title, t),
subhead: TextStyle.lerp(begin.subhead, end.subhead, t), subhead: TextStyle.lerp(a.subhead, b.subhead, t),
body2: TextStyle.lerp(begin.body2, end.body2, t), body2: TextStyle.lerp(a.body2, b.body2, t),
body1: TextStyle.lerp(begin.body1, end.body1, t), body1: TextStyle.lerp(a.body1, b.body1, t),
caption: TextStyle.lerp(begin.caption, end.caption, t), caption: TextStyle.lerp(a.caption, b.caption, t),
button: TextStyle.lerp(begin.button, end.button, t), button: TextStyle.lerp(a.button, b.button, t),
); );
} }
......
...@@ -84,7 +84,20 @@ abstract class AlignmentGeometry { ...@@ -84,7 +84,20 @@ abstract class AlignmentGeometry {
/// this is not reflected in the type system). Otherwise, an object /// this is not reflected in the type system). Otherwise, an object
/// representing a combination of both is returned. That object can be turned /// representing a combination of both is returned. That object can be turned
/// into a concrete [Alignment] using [resolve]. /// into a concrete [Alignment] using [resolve].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static AlignmentGeometry lerp(AlignmentGeometry a, AlignmentGeometry b, double t) { static AlignmentGeometry lerp(AlignmentGeometry a, AlignmentGeometry b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -320,7 +333,20 @@ class Alignment extends AlignmentGeometry { ...@@ -320,7 +333,20 @@ class Alignment extends AlignmentGeometry {
/// Linearly interpolate between two [Alignment]s. /// Linearly interpolate between two [Alignment]s.
/// ///
/// If either is null, this function interpolates from [Alignment.center]. /// If either is null, this function interpolates from [Alignment.center].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static Alignment lerp(Alignment a, Alignment b, double t) { static Alignment lerp(Alignment a, Alignment b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -498,7 +524,20 @@ class AlignmentDirectional extends AlignmentGeometry { ...@@ -498,7 +524,20 @@ class AlignmentDirectional extends AlignmentGeometry {
/// Linearly interpolate between two [AlignmentDirectional]s. /// Linearly interpolate between two [AlignmentDirectional]s.
/// ///
/// If either is null, this function interpolates from [AlignmentDirectional.center]. /// If either is null, this function interpolates from [AlignmentDirectional.center].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static AlignmentDirectional lerp(AlignmentDirectional a, AlignmentDirectional b, double t) { static AlignmentDirectional lerp(AlignmentDirectional a, AlignmentDirectional b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
......
...@@ -126,7 +126,20 @@ abstract class BorderRadiusGeometry { ...@@ -126,7 +126,20 @@ abstract class BorderRadiusGeometry {
/// this is not reflected in the type system). Otherwise, an object /// this is not reflected in the type system). Otherwise, an object
/// representing a combination of both is returned. That object can be turned /// representing a combination of both is returned. That object can be turned
/// into a concrete [BorderRadius] using [resolve]. /// into a concrete [BorderRadius] using [resolve].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static BorderRadiusGeometry lerp(BorderRadiusGeometry a, BorderRadiusGeometry b, double t) { static BorderRadiusGeometry lerp(BorderRadiusGeometry a, BorderRadiusGeometry b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
a ??= BorderRadius.zero; a ??= BorderRadius.zero;
...@@ -467,7 +480,20 @@ class BorderRadius extends BorderRadiusGeometry { ...@@ -467,7 +480,20 @@ class BorderRadius extends BorderRadiusGeometry {
/// Linearly interpolate between two [BorderRadius] objects. /// Linearly interpolate between two [BorderRadius] objects.
/// ///
/// If either is null, this function interpolates from [BorderRadius.zero]. /// If either is null, this function interpolates from [BorderRadius.zero].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static BorderRadius lerp(BorderRadius a, BorderRadius b, double t) { static BorderRadius lerp(BorderRadius a, BorderRadius b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -681,7 +707,20 @@ class BorderRadiusDirectional extends BorderRadiusGeometry { ...@@ -681,7 +707,20 @@ class BorderRadiusDirectional extends BorderRadiusGeometry {
/// Linearly interpolate between two [BorderRadiusDirectional] objects. /// Linearly interpolate between two [BorderRadiusDirectional] objects.
/// ///
/// If either is null, this function interpolates from [BorderRadiusDirectional.zero]. /// If either is null, this function interpolates from [BorderRadiusDirectional.zero].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static BorderRadiusDirectional lerp(BorderRadiusDirectional a, BorderRadiusDirectional b, double t) { static BorderRadiusDirectional lerp(BorderRadiusDirectional a, BorderRadiusDirectional b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
......
...@@ -130,11 +130,22 @@ class BorderSide { ...@@ -130,11 +130,22 @@ class BorderSide {
); );
} }
/// Creates a copy of this border but with the width scaled by the given factor. /// Creates a copy of this border side description but with the width scaled
/// /// by the factor `t`.
/// Since a zero width is painted as a hairline width rather than no border at ///
/// all, the zero factor is special-cased to instead change the style no /// The `t` argument represents the multiplicand, or the position on the
/// [BorderStyle.none]. /// timeline for an interpolation from nothing to `this`, with 0.0 meaning
/// that the the object returned should be the nil variant of this object, 1.0
/// meaning that no change should be applied, returning `this` (or something
/// equivalent to `this`), and other values meaning that the object should be
/// multiplied by `t`. Negative values are treated like zero.
///
/// Since a zero width is normally painted as a hairline width rather than no
/// border at all, the zero factor is special-cased to instead change the
/// style no [BorderStyle.none].
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
BorderSide scale(double t) { BorderSide scale(double t) {
assert(t != null); assert(t != null);
return new BorderSide( return new BorderSide(
...@@ -186,6 +197,18 @@ class BorderSide { ...@@ -186,6 +197,18 @@ class BorderSide {
/// Linearly interpolate between two border sides. /// Linearly interpolate between two border sides.
/// ///
/// The arguments must not be null. /// The arguments must not be null.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static BorderSide lerp(BorderSide a, BorderSide b, double t) { static BorderSide lerp(BorderSide a, BorderSide b, double t) {
assert(a != null); assert(a != null);
assert(b != null); assert(b != null);
...@@ -272,7 +295,7 @@ abstract class ShapeBorder { ...@@ -272,7 +295,7 @@ abstract class ShapeBorder {
/// computing their [dimensions]. /// computing their [dimensions].
EdgeInsetsGeometry get dimensions; EdgeInsetsGeometry get dimensions;
/// Attempts to create a new object that represents the amalgamation of [this] /// Attempts to create a new object that represents the amalgamation of `this`
/// border and the `other` border. /// border and the `other` border.
/// ///
/// If the type of the other border isn't known, or the given instance cannot /// If the type of the other border isn't known, or the given instance cannot
...@@ -297,10 +320,32 @@ abstract class ShapeBorder { ...@@ -297,10 +320,32 @@ abstract class ShapeBorder {
return add(other) ?? other.add(this, reversed: true) ?? new _CompoundBorder(<ShapeBorder>[other, this]); return add(other) ?? other.add(this, reversed: true) ?? new _CompoundBorder(<ShapeBorder>[other, this]);
} }
/// Creates a new border with the widths of this border multiplied by `t`. /// Creates a copy of this border, scaled by the factor `t`.
///
/// Typically this means scaling the width of the border's side, but it can
/// also include scaling other artifacts of the border, e.g. the border radius
/// of a [RoundedRectangleBorder].
///
/// The `t` argument represents the multiplicand, or the position on the
/// timeline for an interpolation from nothing to `this`, with 0.0 meaning
/// that the the object returned should be the nil variant of this object, 1.0
/// meaning that no change should be applied, returning `this` (or something
/// equivalent to `this`), and other values meaning that the object should be
/// multiplied by `t`. Negative values are allowed but may be meaningless
/// (they correspond to extrapolating the interpolation from this object to
/// nothing, and going beyond nothing)
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// See also:
///
/// * [BorderSide.scale], which most [ShapeBorder] subclasses defer to for
/// the actual computation.
ShapeBorder scale(double t); ShapeBorder scale(double t);
/// Linearly interpolates from `a` to [this]. /// Linearly interpolates from another [ShapeBorder] (possibly of another
/// class) to `this`.
/// ///
/// When implementing this method in subclasses, return null if this class /// When implementing this method in subclasses, return null if this class
/// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo] /// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo]
...@@ -309,6 +354,19 @@ abstract class ShapeBorder { ...@@ -309,6 +354,19 @@ abstract class ShapeBorder {
/// The base class implementation handles the case of `a` being null by /// The base class implementation handles the case of `a` being null by
/// deferring to [scale]. /// deferring to [scale].
/// ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `this` (or something equivalent to `this`), and values in
/// between meaning that the interpolation is at the relevant point on the
/// timeline between `a` and `this`. The interpolation can be extrapolated
/// beyond 0.0 and 1.0, so negative values and values greater than 1.0 are
/// valid (and can easily be generated by curves such as
/// [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// Instead of calling this directly, use [ShapeBorder.lerp]. /// Instead of calling this directly, use [ShapeBorder.lerp].
@protected @protected
ShapeBorder lerpFrom(ShapeBorder a, double t) { ShapeBorder lerpFrom(ShapeBorder a, double t) {
...@@ -317,7 +375,8 @@ abstract class ShapeBorder { ...@@ -317,7 +375,8 @@ abstract class ShapeBorder {
return null; return null;
} }
/// Linearly interpolates from [this] to `b`. /// Linearly interpolates from `this` to another [ShapeBorder] (possibly of
/// another class).
/// ///
/// This is called if `b`'s [lerpTo] did not know how to handle this class. /// This is called if `b`'s [lerpTo] did not know how to handle this class.
/// ///
...@@ -328,6 +387,18 @@ abstract class ShapeBorder { ...@@ -328,6 +387,18 @@ abstract class ShapeBorder {
/// The base class implementation handles the case of `b` being null by /// The base class implementation handles the case of `b` being null by
/// deferring to [scale]. /// deferring to [scale].
/// ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `this` (or something
/// equivalent to `this`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `this` and `b`. The interpolation can be extrapolated beyond 0.0
/// and 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// Instead of calling this directly, use [ShapeBorder.lerp]. /// Instead of calling this directly, use [ShapeBorder.lerp].
@protected @protected
ShapeBorder lerpTo(ShapeBorder b, double t) { ShapeBorder lerpTo(ShapeBorder b, double t) {
...@@ -336,19 +407,32 @@ abstract class ShapeBorder { ...@@ -336,19 +407,32 @@ abstract class ShapeBorder {
return null; return null;
} }
/// Linearly interpolates from `begin` to `end`. /// Linearly interpolates between two [ShapeBorder]s.
/// ///
/// This defers to `end`'s [lerpTo] function if `end` is not null. If `end` is /// This defers to `b`'s [lerpTo] function if `b` is not null. If `b` is
/// null or if its [lerpTo] returns null, it uses `begin`'s [lerpFrom] /// null or if its [lerpTo] returns null, it uses `a`'s [lerpFrom]
/// function instead. If both return null, it returns `begin` before `t=0.5` /// function instead. If both return null, it returns `a` before `t=0.5`
/// and `end` after `t=0.5`. /// and `b` after `t=0.5`.
static ShapeBorder lerp(ShapeBorder begin, ShapeBorder end, double t) { ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static ShapeBorder lerp(ShapeBorder a, ShapeBorder b, double t) {
assert(t != null);
ShapeBorder result; ShapeBorder result;
if (end != null) if (b != null)
result = end.lerpFrom(begin, t); result = b.lerpFrom(a, t);
if (result == null && begin != null) if (result == null && a != null)
result = begin.lerpTo(end, t); result = a.lerpTo(b, t);
return result ?? (t < 0.5 ? begin : end); return result ?? (t < 0.5 ? a : b);
} }
/// Create a [Path] that describes the outer edge of the border. /// Create a [Path] that describes the outer edge of the border.
...@@ -480,6 +564,7 @@ class _CompoundBorder extends ShapeBorder { ...@@ -480,6 +564,7 @@ class _CompoundBorder extends ShapeBorder {
} }
static _CompoundBorder lerp(ShapeBorder a, ShapeBorder b, double t) { static _CompoundBorder lerp(ShapeBorder a, ShapeBorder b, double t) {
assert(t != null);
assert(a is _CompoundBorder || b is _CompoundBorder); // Not really necessary, but all call sites currently intend this. assert(a is _CompoundBorder || b is _CompoundBorder); // Not really necessary, but all call sites currently intend this.
final List<ShapeBorder> aList = a is _CompoundBorder ? a.borders : <ShapeBorder>[a]; final List<ShapeBorder> aList = a is _CompoundBorder ? a.borders : <ShapeBorder>[a];
final List<ShapeBorder> bList = b is _CompoundBorder ? b.borders : <ShapeBorder>[b]; final List<ShapeBorder> bList = b is _CompoundBorder ? b.borders : <ShapeBorder>[b];
......
...@@ -34,6 +34,8 @@ enum BoxShape { ...@@ -34,6 +34,8 @@ enum BoxShape {
/// ///
/// * [CircleBorder], the equivalent [ShapeBorder]. /// * [CircleBorder], the equivalent [ShapeBorder].
circle, circle,
// Don't add more, instead create a new ShapeBorder.
} }
/// Base class for box borders that can paint as rectangle, circles, or rounded /// Base class for box borders that can paint as rectangle, circles, or rounded
...@@ -95,7 +97,20 @@ abstract class BoxBorder extends ShapeBorder { ...@@ -95,7 +97,20 @@ abstract class BoxBorder extends ShapeBorder {
/// ///
/// For a more flexible approach, consider [ShapeBorder.lerp], which would /// For a more flexible approach, consider [ShapeBorder.lerp], which would
/// instead [add] the two sets of sides and interpolate them simultaneously. /// instead [add] the two sets of sides and interpolate them simultaneously.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static BoxBorder lerp(BoxBorder a, BoxBorder b, double t) { static BoxBorder lerp(BoxBorder a, BoxBorder b, double t) {
assert(t != null);
if ((a is Border || a == null) && (b is Border || b == null)) if ((a is Border || a == null) && (b is Border || b == null))
return Border.lerp(a, b, t); return Border.lerp(a, b, t);
if ((a is BorderDirectional || a == null) && (b is BorderDirectional || b == null)) if ((a is BorderDirectional || a == null) && (b is BorderDirectional || b == null))
...@@ -387,7 +402,6 @@ class Border extends BoxBorder { ...@@ -387,7 +402,6 @@ class Border extends BoxBorder {
return null; return null;
} }
/// Creates a new border with the widths of this border multiplied by `t`.
@override @override
Border scale(double t) { Border scale(double t) {
return new Border( return new Border(
...@@ -398,13 +412,6 @@ class Border extends BoxBorder { ...@@ -398,13 +412,6 @@ class Border extends BoxBorder {
); );
} }
/// Linearly interpolates from `a` to [this].
///
/// If `a` is null, this defers to [scale].
///
/// If `a` is also a [Border], this uses [Border.lerp].
///
/// Otherwise, it defers to [ShapeBorder.lerpFrom].
@override @override
ShapeBorder lerpFrom(ShapeBorder a, double t) { ShapeBorder lerpFrom(ShapeBorder a, double t) {
if (a is Border) if (a is Border)
...@@ -412,13 +419,6 @@ class Border extends BoxBorder { ...@@ -412,13 +419,6 @@ class Border extends BoxBorder {
return super.lerpFrom(a, t); return super.lerpFrom(a, t);
} }
/// Linearly interpolates from [this] to `b`.
///
/// If `b` is null, this defers to [scale].
///
/// If `b` is also a [Border], this uses [Border.lerp].
///
/// Otherwise, it defers to [ShapeBorder.lerpTo].
@override @override
ShapeBorder lerpTo(ShapeBorder b, double t) { ShapeBorder lerpTo(ShapeBorder b, double t) {
if (b is Border) if (b is Border)
...@@ -430,7 +430,20 @@ class Border extends BoxBorder { ...@@ -430,7 +430,20 @@ class Border extends BoxBorder {
/// ///
/// If a border is null, it is treated as having four [BorderSide.none] /// If a border is null, it is treated as having four [BorderSide.none]
/// borders. /// borders.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static Border lerp(Border a, Border b, double t) { static Border lerp(Border a, Border b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -441,7 +454,7 @@ class Border extends BoxBorder { ...@@ -441,7 +454,7 @@ class Border extends BoxBorder {
top: BorderSide.lerp(a.top, b.top, t), top: BorderSide.lerp(a.top, b.top, t),
right: BorderSide.lerp(a.right, b.right, t), right: BorderSide.lerp(a.right, b.right, t),
bottom: BorderSide.lerp(a.bottom, b.bottom, t), bottom: BorderSide.lerp(a.bottom, b.bottom, t),
left: BorderSide.lerp(a.left, b.left, t) left: BorderSide.lerp(a.left, b.left, t),
); );
} }
...@@ -689,7 +702,6 @@ class BorderDirectional extends BoxBorder { ...@@ -689,7 +702,6 @@ class BorderDirectional extends BoxBorder {
return null; return null;
} }
/// Creates a new border with the widths of this border multiplied by `t`.
@override @override
BorderDirectional scale(double t) { BorderDirectional scale(double t) {
return new BorderDirectional( return new BorderDirectional(
...@@ -700,13 +712,6 @@ class BorderDirectional extends BoxBorder { ...@@ -700,13 +712,6 @@ class BorderDirectional extends BoxBorder {
); );
} }
/// Linearly interpolates from `a` to [this].
///
/// If `a` is null, this defers to [scale].
///
/// If `a` is also a [BorderDirectional], this uses [BorderDirectional.lerp].
///
/// Otherwise, it defers to [ShapeBorder.lerpFrom].
@override @override
ShapeBorder lerpFrom(ShapeBorder a, double t) { ShapeBorder lerpFrom(ShapeBorder a, double t) {
if (a is BorderDirectional) if (a is BorderDirectional)
...@@ -714,13 +719,6 @@ class BorderDirectional extends BoxBorder { ...@@ -714,13 +719,6 @@ class BorderDirectional extends BoxBorder {
return super.lerpFrom(a, t); return super.lerpFrom(a, t);
} }
/// Linearly interpolates from [this] to `b`.
///
/// If `b` is null, this defers to [scale].
///
/// If `b` is also a [BorderDirectional], this uses [BorderDirectional.lerp].
///
/// Otherwise, it defers to [ShapeBorder.lerpTo].
@override @override
ShapeBorder lerpTo(ShapeBorder b, double t) { ShapeBorder lerpTo(ShapeBorder b, double t) {
if (b is BorderDirectional) if (b is BorderDirectional)
...@@ -732,7 +730,20 @@ class BorderDirectional extends BoxBorder { ...@@ -732,7 +730,20 @@ class BorderDirectional extends BoxBorder {
/// ///
/// If a border is null, it is treated as having four [BorderSide.none] /// If a border is null, it is treated as having four [BorderSide.none]
/// borders. /// borders.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static BorderDirectional lerp(BorderDirectional a, BorderDirectional b, double t) { static BorderDirectional lerp(BorderDirectional a, BorderDirectional b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -743,7 +754,7 @@ class BorderDirectional extends BoxBorder { ...@@ -743,7 +754,7 @@ class BorderDirectional extends BoxBorder {
top: BorderSide.lerp(a.top, b.top, t), top: BorderSide.lerp(a.top, b.top, t),
end: BorderSide.lerp(a.end, b.end, t), end: BorderSide.lerp(a.end, b.end, t),
bottom: BorderSide.lerp(a.bottom, b.bottom, t), bottom: BorderSide.lerp(a.bottom, b.bottom, t),
start: BorderSide.lerp(a.start, b.start, t) start: BorderSide.lerp(a.start, b.start, t),
); );
} }
......
...@@ -200,6 +200,18 @@ class BoxDecoration extends Decoration { ...@@ -200,6 +200,18 @@ class BoxDecoration extends Decoration {
/// unmodified. Otherwise, the values are computed by interpolating the /// unmodified. Otherwise, the values are computed by interpolating the
/// properties appropriately. /// properties appropriately.
/// ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// See also: /// See also:
/// ///
/// * [Decoration.lerp], which can interpolate between any two types of /// * [Decoration.lerp], which can interpolate between any two types of
...@@ -208,6 +220,7 @@ class BoxDecoration extends Decoration { ...@@ -208,6 +220,7 @@ class BoxDecoration extends Decoration {
/// and which use [BoxDecoration.lerp] when interpolating two /// and which use [BoxDecoration.lerp] when interpolating two
/// [BoxDecoration]s or a [BoxDecoration] to or from null. /// [BoxDecoration]s or a [BoxDecoration] to or from null.
static BoxDecoration lerp(BoxDecoration a, BoxDecoration b, double t) { static BoxDecoration lerp(BoxDecoration a, BoxDecoration b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
......
...@@ -70,7 +70,20 @@ class BoxShadow { ...@@ -70,7 +70,20 @@ class BoxShadow {
/// If either box shadow is null, this function linearly interpolates from a /// If either box shadow is null, this function linearly interpolates from a
/// a box shadow that matches the other box shadow in color but has a zero /// a box shadow that matches the other box shadow in color but has a zero
/// offset and a zero blurRadius. /// offset and a zero blurRadius.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static BoxShadow lerp(BoxShadow a, BoxShadow b, double t) { static BoxShadow lerp(BoxShadow a, BoxShadow b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -81,25 +94,38 @@ class BoxShadow { ...@@ -81,25 +94,38 @@ class BoxShadow {
color: Color.lerp(a.color, b.color, t), color: Color.lerp(a.color, b.color, t),
offset: Offset.lerp(a.offset, b.offset, t), offset: Offset.lerp(a.offset, b.offset, t),
blurRadius: ui.lerpDouble(a.blurRadius, b.blurRadius, t), blurRadius: ui.lerpDouble(a.blurRadius, b.blurRadius, t),
spreadRadius: ui.lerpDouble(a.spreadRadius, b.spreadRadius, t) spreadRadius: ui.lerpDouble(a.spreadRadius, b.spreadRadius, t),
); );
} }
/// Linearly interpolate between two lists of box shadows. /// Linearly interpolate between two lists of box shadows.
/// ///
/// If the lists differ in length, excess items are lerped with null. /// If the lists differ in length, excess items are lerped with null.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static List<BoxShadow> lerpList(List<BoxShadow> a, List<BoxShadow> b, double t) { static List<BoxShadow> lerpList(List<BoxShadow> a, List<BoxShadow> b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
a ??= <BoxShadow>[]; a ??= <BoxShadow>[];
b ??= <BoxShadow>[]; b ??= <BoxShadow>[];
final List<BoxShadow> result = <BoxShadow>[]; final List<BoxShadow> result = <BoxShadow>[];
final int commonLength = math.min(a.length, b.length); final int commonLength = math.min(a.length, b.length);
for (int i = 0; i < commonLength; ++i) for (int i = 0; i < commonLength; i += 1)
result.add(BoxShadow.lerp(a[i], b[i], t)); result.add(BoxShadow.lerp(a[i], b[i], t));
for (int i = commonLength; i < a.length; ++i) for (int i = commonLength; i < a.length; i += 1)
result.add(a[i].scale(1.0 - t)); result.add(a[i].scale(1.0 - t));
for (int i = commonLength; i < b.length; ++i) for (int i = commonLength; i < b.length; i += 1)
result.add(b[i].scale(t)); result.add(b[i].scale(t));
return result; return result;
} }
......
...@@ -136,9 +136,30 @@ class HSVColor { ...@@ -136,9 +136,30 @@ class HSVColor {
/// Linearly interpolate between two HSVColors. /// Linearly interpolate between two HSVColors.
/// ///
/// The colors are interpolated by interpolating the [alpha], [hue],
/// [saturation], and [value] channels separately, which usually leads to a
/// more pleasing effect than [Color.lerp] (which interpolates the red, green,
/// and blue channels separately).
///
/// If either color is null, this function linearly interpolates from a /// If either color is null, this function linearly interpolates from a
/// transparent instance of the other color. /// transparent instance of the other color. This is usually preferable to
/// interpolating from [Colors.transparent] (`const Color(0x00000000)`) since
/// that will interpolate from a transparent red and cycle through the hues to
/// match the target color, regardless of what that color's hue is.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static HSVColor lerp(HSVColor a, HSVColor b, double t) { static HSVColor lerp(HSVColor a, HSVColor b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -149,7 +170,7 @@ class HSVColor { ...@@ -149,7 +170,7 @@ class HSVColor {
lerpDouble(a.alpha, b.alpha, t), lerpDouble(a.alpha, b.alpha, t),
lerpDouble(a.hue, b.hue, t), lerpDouble(a.hue, b.hue, t),
lerpDouble(a.saturation, b.saturation, t), lerpDouble(a.saturation, b.saturation, t),
lerpDouble(a.value, b.value, t) lerpDouble(a.value, b.value, t),
); );
} }
......
...@@ -61,7 +61,8 @@ abstract class Decoration extends Diagnosticable { ...@@ -61,7 +61,8 @@ abstract class Decoration extends Diagnosticable {
/// Whether this decoration is complex enough to benefit from caching its painting. /// Whether this decoration is complex enough to benefit from caching its painting.
bool get isComplex => false; bool get isComplex => false;
/// Linearly interpolates from `a` to [this]. /// Linearly interpolates from another [Decoration] (which may be of a
/// different class) to `this`.
/// ///
/// When implementing this method in subclasses, return null if this class /// When implementing this method in subclasses, return null if this class
/// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo] /// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo]
...@@ -71,11 +72,25 @@ abstract class Decoration extends Diagnosticable { ...@@ -71,11 +72,25 @@ abstract class Decoration extends Diagnosticable {
/// method uses this as a fallback when two classes can't interpolate between /// method uses this as a fallback when two classes can't interpolate between
/// each other. /// each other.
/// ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `this` (or something equivalent to `this`), and values in
/// between meaning that the interpolation is at the relevant point on the
/// timeline between `a` and `this`. The interpolation can be extrapolated
/// beyond 0.0 and 1.0, so negative values and values greater than 1.0 are
/// valid (and can easily be generated by curves such as
/// [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// Instead of calling this directly, use [Decoration.lerp]. /// Instead of calling this directly, use [Decoration.lerp].
@protected @protected
Decoration lerpFrom(Decoration a, double t) => null; Decoration lerpFrom(Decoration a, double t) => null;
/// Linearly interpolates from [this] to `b`. /// Linearly interpolates from `this` to another [Decoration] (which may be of
/// a different class).
/// ///
/// This is called if `b`'s [lerpTo] did not know how to handle this class. /// This is called if `b`'s [lerpTo] did not know how to handle this class.
/// ///
...@@ -87,32 +102,54 @@ abstract class Decoration extends Diagnosticable { ...@@ -87,32 +102,54 @@ abstract class Decoration extends Diagnosticable {
/// method uses this as a fallback when two classes can't interpolate between /// method uses this as a fallback when two classes can't interpolate between
/// each other. /// each other.
/// ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `this` (or something
/// equivalent to `this`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `this` and `b`. The interpolation can be extrapolated beyond 0.0
/// and 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// Instead of calling this directly, use [Decoration.lerp]. /// Instead of calling this directly, use [Decoration.lerp].
@protected @protected
Decoration lerpTo(Decoration b, double t) => null; Decoration lerpTo(Decoration b, double t) => null;
/// Linearly interpolates from `begin` to `end`. /// Linearly interpolates between two [Decoration]s.
/// ///
/// This attempts to use [lerpFrom] and [lerpTo] on `end` and `begin` /// This attempts to use [lerpFrom] and [lerpTo] on `b` and `a`
/// respectively to find a solution. If the two values can't directly be /// respectively to find a solution. If the two values can't directly be
/// interpolated, then the interpolation is done via null (at `t == 0.5`). /// interpolated, then the interpolation is done via null (at `t == 0.5`).
/// ///
/// If the values aren't null, then for `t == 0.0` and `t == 1.0` the values /// The `t` argument represents position on the timeline, with 0.0 meaning
/// `begin` and `end` are return verbatim. /// that the interpolation has not started, returning `a` (or something
static Decoration lerp(Decoration begin, Decoration end, double t) { /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
if (begin == null && end == null) /// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static Decoration lerp(Decoration a, Decoration b, double t) {
assert(t != null);
if (a == null && b == null)
return null; return null;
if (begin == null) if (a == null)
return end.lerpFrom(null, t) ?? end; return b.lerpFrom(null, t) ?? b;
if (end == null) if (b == null)
return begin.lerpTo(null, t) ?? begin; return a.lerpTo(null, t) ?? a;
if (t == 0.0) if (t == 0.0)
return begin; return a;
if (t == 1.0) if (t == 1.0)
return end; return b;
return end.lerpFrom(begin, t) return b.lerpFrom(a, t)
?? begin.lerpTo(end, t) ?? a.lerpTo(b, t)
?? (t < 0.5 ? begin.lerpTo(null, t * 2.0) : end.lerpFrom(null, (t - 0.5) * 2.0)); ?? (t < 0.5 ? (a.lerpTo(null, t * 2.0) ?? a) : (b.lerpFrom(null, (t - 0.5) * 2.0) ?? b));
} }
/// Tests whether the given point, on a rectangle of a given size, /// Tests whether the given point, on a rectangle of a given size,
......
...@@ -189,7 +189,20 @@ abstract class EdgeInsetsGeometry { ...@@ -189,7 +189,20 @@ abstract class EdgeInsetsGeometry {
/// this is not reflected in the type system). Otherwise, an object /// this is not reflected in the type system). Otherwise, an object
/// representing a combination of both is returned. That object can be turned /// representing a combination of both is returned. That object can be turned
/// into a concrete [EdgeInsets] using [resolve]. /// into a concrete [EdgeInsets] using [resolve].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static EdgeInsetsGeometry lerp(EdgeInsetsGeometry a, EdgeInsetsGeometry b, double t) { static EdgeInsetsGeometry lerp(EdgeInsetsGeometry a, EdgeInsetsGeometry b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -206,7 +219,7 @@ abstract class EdgeInsetsGeometry { ...@@ -206,7 +219,7 @@ abstract class EdgeInsetsGeometry {
ui.lerpDouble(a._start, b._start, t), ui.lerpDouble(a._start, b._start, t),
ui.lerpDouble(a._end, b._end, t), ui.lerpDouble(a._end, b._end, t),
ui.lerpDouble(a._top, b._top, t), ui.lerpDouble(a._top, b._top, t),
ui.lerpDouble(a._bottom, b._bottom, t) ui.lerpDouble(a._bottom, b._bottom, t),
); );
} }
...@@ -542,7 +555,20 @@ class EdgeInsets extends EdgeInsetsGeometry { ...@@ -542,7 +555,20 @@ class EdgeInsets extends EdgeInsetsGeometry {
/// Linearly interpolate between two [EdgeInsets]. /// Linearly interpolate between two [EdgeInsets].
/// ///
/// If either is null, this function interpolates from [EdgeInsets.zero]. /// If either is null, this function interpolates from [EdgeInsets.zero].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static EdgeInsets lerp(EdgeInsets a, EdgeInsets b, double t) { static EdgeInsets lerp(EdgeInsets a, EdgeInsets b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -763,7 +789,20 @@ class EdgeInsetsDirectional extends EdgeInsetsGeometry { ...@@ -763,7 +789,20 @@ class EdgeInsetsDirectional extends EdgeInsetsGeometry {
/// To interpolate between two [EdgeInsetsGeometry] objects of arbitrary type /// To interpolate between two [EdgeInsetsGeometry] objects of arbitrary type
/// (either [EdgeInsets] or [EdgeInsetsDirectional]), consider the /// (either [EdgeInsets] or [EdgeInsetsDirectional]), consider the
/// [EdgeInsetsGeometry.lerp] static method. /// [EdgeInsetsGeometry.lerp] static method.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static EdgeInsetsDirectional lerp(EdgeInsetsDirectional a, EdgeInsetsDirectional b, double t) { static EdgeInsetsDirectional lerp(EdgeInsetsDirectional a, EdgeInsetsDirectional b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
......
...@@ -125,13 +125,25 @@ class FlutterLogoDecoration extends Decoration { ...@@ -125,13 +125,25 @@ class FlutterLogoDecoration extends Decoration {
/// ///
/// If both values are null, this returns null. Otherwise, it returns a /// If both values are null, this returns null. Otherwise, it returns a
/// non-null value. If one of the values is null, then the result is obtained /// non-null value. If one of the values is null, then the result is obtained
/// by scaling the other value's opacity and [margin]. If neither value is /// by scaling the other value's opacity and [margin].
/// null and `t == 0.0`, then `a` is returned unmodified; if `t == 1.0` then
/// `b` is returned unmodified. Otherwise, the values are computed by
/// interpolating the properties appropriately.
/// ///
/// See also [Decoration.lerp]. /// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// See also:
///
/// * [Decoration.lerp], which interpolates between arbitrary decorations.
static FlutterLogoDecoration lerp(FlutterLogoDecoration a, FlutterLogoDecoration b, double t) { static FlutterLogoDecoration lerp(FlutterLogoDecoration a, FlutterLogoDecoration b, double t) {
assert(t != null);
assert(a == null || a.debugAssertIsValid()); assert(a == null || a.debugAssertIsValid());
assert(b == null || b.debugAssertIsValid()); assert(b == null || b.debugAssertIsValid());
if (a == null && b == null) if (a == null && b == null)
......
...@@ -177,7 +177,20 @@ class FractionalOffset extends Alignment { ...@@ -177,7 +177,20 @@ class FractionalOffset extends Alignment {
/// Linearly interpolate between two [FractionalOffset]s. /// Linearly interpolate between two [FractionalOffset]s.
/// ///
/// If either is null, this function interpolates from [FractionalOffset.center]. /// If either is null, this function interpolates from [FractionalOffset.center].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static FractionalOffset lerp(FractionalOffset a, FractionalOffset b, double t) { static FractionalOffset lerp(FractionalOffset a, FractionalOffset b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
......
...@@ -67,7 +67,7 @@ abstract class Gradient { ...@@ -67,7 +67,7 @@ abstract class Gradient {
/// Typically this is the same as interpolating from null (with [lerp]). /// Typically this is the same as interpolating from null (with [lerp]).
Gradient scale(double factor); Gradient scale(double factor);
/// Linearly interpolates from `a` to [this]. /// Linearly interpolates from another [Gradient] to `this`.
/// ///
/// When implementing this method in subclasses, return null if this class /// When implementing this method in subclasses, return null if this class
/// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo] /// cannot interpolate from `a`. In that case, [lerp] will try `a`'s [lerpTo]
...@@ -76,6 +76,19 @@ abstract class Gradient { ...@@ -76,6 +76,19 @@ abstract class Gradient {
/// If `a` is null, this must not return null. The base class implements this /// If `a` is null, this must not return null. The base class implements this
/// by deferring to [scale]. /// by deferring to [scale].
/// ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `this` (or something equivalent to `this`), and values in
/// between meaning that the interpolation is at the relevant point on the
/// timeline between `a` and `this`. The interpolation can be extrapolated
/// beyond 0.0 and 1.0, so negative values and values greater than 1.0 are
/// valid (and can easily be generated by curves such as
/// [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// Instead of calling this directly, use [Gradient.lerp]. /// Instead of calling this directly, use [Gradient.lerp].
@protected @protected
Gradient lerpFrom(Gradient a, double t) { Gradient lerpFrom(Gradient a, double t) {
...@@ -84,7 +97,7 @@ abstract class Gradient { ...@@ -84,7 +97,7 @@ abstract class Gradient {
return null; return null;
} }
/// Linearly interpolates from [this] to `b`. /// Linearly interpolates from `this` to another [Gradient].
/// ///
/// This is called if `b`'s [lerpTo] did not know how to handle this class. /// This is called if `b`'s [lerpTo] did not know how to handle this class.
/// ///
...@@ -95,6 +108,18 @@ abstract class Gradient { ...@@ -95,6 +108,18 @@ abstract class Gradient {
/// If `b` is null, this must not return null. The base class implements this /// If `b` is null, this must not return null. The base class implements this
/// by deferring to [scale]. /// by deferring to [scale].
/// ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `this` (or something
/// equivalent to `this`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `this` and `b`. The interpolation can be extrapolated beyond 0.0
/// and 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// Instead of calling this directly, use [Gradient.lerp]. /// Instead of calling this directly, use [Gradient.lerp].
@protected @protected
Gradient lerpTo(Gradient b, double t) { Gradient lerpTo(Gradient b, double t) {
...@@ -103,24 +128,37 @@ abstract class Gradient { ...@@ -103,24 +128,37 @@ abstract class Gradient {
return null; return null;
} }
/// Linearly interpolates from `begin` to `end`. /// Linearly interpolates between two [Gradient]s.
/// ///
/// This defers to `end`'s [lerpTo] function if `end` is not null. If `end` is /// This defers to `b`'s [lerpTo] function if `b` is not null. If `b` is
/// null or if its [lerpTo] returns null, it uses `begin`'s [lerpFrom] /// null or if its [lerpTo] returns null, it uses `a`'s [lerpFrom]
/// function instead. If both return null, it returns `begin` before `t=0.5` /// function instead. If both return null, it returns `a` before `t == 0.5`
/// and `end` after `t=0.5`. /// and `b` after `t == 0.5`.
static Gradient lerp(Gradient begin, Gradient end, double t) { ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static Gradient lerp(Gradient a, Gradient b, double t) {
assert(t != null);
Gradient result; Gradient result;
if (end != null) if (b != null)
result = end.lerpFrom(begin, t); // if begin is null, this must return non-null result = b.lerpFrom(a, t); // if a is null, this must return non-null
if (result == null && begin != null) if (result == null && a != null)
result = begin.lerpTo(end, t); // if end is null, this must return non-null result = a.lerpTo(b, t); // if b is null, this must return non-null
if (result != null) if (result != null)
return result; return result;
if (begin == null && end == null) if (a == null && b == null)
return null; return null;
assert(begin != null && end != null); assert(a != null && b != null);
return t < 0.5 ? begin.scale(1.0 - (t * 2.0)) : end.scale((t - 0.5) * 2.0); return t < 0.5 ? a.scale(1.0 - (t * 2.0)) : b.scale((t - 0.5) * 2.0);
} }
} }
...@@ -298,7 +336,20 @@ class LinearGradient extends Gradient { ...@@ -298,7 +336,20 @@ class LinearGradient extends Gradient {
/// [tileMode] and with the same [colors] but transparent (using [scale]). /// [tileMode] and with the same [colors] but transparent (using [scale]).
/// ///
/// If neither gradient is null, they must have the same number of [colors]. /// If neither gradient is null, they must have the same number of [colors].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static LinearGradient lerp(LinearGradient a, LinearGradient b, double t) { static LinearGradient lerp(LinearGradient a, LinearGradient b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -532,7 +583,20 @@ class RadialGradient extends Gradient { ...@@ -532,7 +583,20 @@ class RadialGradient extends Gradient {
/// [tileMode] and with the same [colors] but transparent (using [scale]). /// [tileMode] and with the same [colors] but transparent (using [scale]).
/// ///
/// If neither gradient is null, they must have the same number of [colors]. /// If neither gradient is null, they must have the same number of [colors].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static RadialGradient lerp(RadialGradient a, RadialGradient b, double t) { static RadialGradient lerp(RadialGradient a, RadialGradient b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
......
...@@ -55,6 +55,7 @@ class RoundedRectangleBorder extends ShapeBorder { ...@@ -55,6 +55,7 @@ class RoundedRectangleBorder extends ShapeBorder {
@override @override
ShapeBorder lerpFrom(ShapeBorder a, double t) { ShapeBorder lerpFrom(ShapeBorder a, double t) {
assert(t != null);
if (a is RoundedRectangleBorder) { if (a is RoundedRectangleBorder) {
return new RoundedRectangleBorder( return new RoundedRectangleBorder(
side: BorderSide.lerp(a.side, side, t), side: BorderSide.lerp(a.side, side, t),
...@@ -73,6 +74,7 @@ class RoundedRectangleBorder extends ShapeBorder { ...@@ -73,6 +74,7 @@ class RoundedRectangleBorder extends ShapeBorder {
@override @override
ShapeBorder lerpTo(ShapeBorder b, double t) { ShapeBorder lerpTo(ShapeBorder b, double t) {
assert(t != null);
if (b is RoundedRectangleBorder) { if (b is RoundedRectangleBorder) {
return new RoundedRectangleBorder( return new RoundedRectangleBorder(
side: BorderSide.lerp(side, b.side, t), side: BorderSide.lerp(side, b.side, t),
...@@ -169,6 +171,7 @@ class _RoundedRectangleToCircleBorder extends ShapeBorder { ...@@ -169,6 +171,7 @@ class _RoundedRectangleToCircleBorder extends ShapeBorder {
@override @override
ShapeBorder lerpFrom(ShapeBorder a, double t) { ShapeBorder lerpFrom(ShapeBorder a, double t) {
assert(t != null);
if (a is RoundedRectangleBorder) { if (a is RoundedRectangleBorder) {
return new _RoundedRectangleToCircleBorder( return new _RoundedRectangleToCircleBorder(
side: BorderSide.lerp(a.side, side, t), side: BorderSide.lerp(a.side, side, t),
......
...@@ -186,13 +186,22 @@ class ShapeDecoration extends Decoration { ...@@ -186,13 +186,22 @@ class ShapeDecoration extends Decoration {
/// Interpolates each parameter of the decoration separately. /// Interpolates each parameter of the decoration separately.
/// ///
/// If both values are null, this returns null. Otherwise, it returns a /// If both values are null, this returns null. Otherwise, it returns a
/// non-null value. If neither value is null and `t == 0.0`, then `a` is /// non-null value, with null arguments treated like a [ShapeDecoration] whose
/// returned unmodified; if `t == 1.0` then `b` is returned unmodified.
/// Otherwise, the values are computed by interpolating the properties
/// appropriately, treating a null argument like a [ShapeDecoration] whose
/// fields are all null (including the [shape], which cannot normally be /// fields are all null (including the [shape], which cannot normally be
/// null). /// null).
/// ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// See also: /// See also:
/// ///
/// * [Decoration.lerp], which can interpolate between any two types of /// * [Decoration.lerp], which can interpolate between any two types of
...@@ -201,6 +210,7 @@ class ShapeDecoration extends Decoration { ...@@ -201,6 +210,7 @@ class ShapeDecoration extends Decoration {
/// and which use [ShapeDecoration.lerp] when interpolating two /// and which use [ShapeDecoration.lerp] when interpolating two
/// [ShapeDecoration]s or a [ShapeDecoration] to or from null. /// [ShapeDecoration]s or a [ShapeDecoration] to or from null.
static ShapeDecoration lerp(ShapeDecoration a, ShapeDecoration b, double t) { static ShapeDecoration lerp(ShapeDecoration a, ShapeDecoration b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a != null && b != null) { if (a != null && b != null) {
......
...@@ -41,6 +41,7 @@ class StadiumBorder extends ShapeBorder { ...@@ -41,6 +41,7 @@ class StadiumBorder extends ShapeBorder {
@override @override
ShapeBorder lerpFrom(ShapeBorder a, double t) { ShapeBorder lerpFrom(ShapeBorder a, double t) {
assert(t != null);
if (a is StadiumBorder) if (a is StadiumBorder)
return new StadiumBorder(side: BorderSide.lerp(a.side, side, t)); return new StadiumBorder(side: BorderSide.lerp(a.side, side, t));
if (a is CircleBorder) { if (a is CircleBorder) {
...@@ -61,6 +62,7 @@ class StadiumBorder extends ShapeBorder { ...@@ -61,6 +62,7 @@ class StadiumBorder extends ShapeBorder {
@override @override
ShapeBorder lerpTo(ShapeBorder b, double t) { ShapeBorder lerpTo(ShapeBorder b, double t) {
assert(t != null);
if (b is StadiumBorder) if (b is StadiumBorder)
return new StadiumBorder(side: BorderSide.lerp(side, b.side, t)); return new StadiumBorder(side: BorderSide.lerp(side, b.side, t));
if (b is CircleBorder) { if (b is CircleBorder) {
...@@ -151,6 +153,7 @@ class _StadiumToCircleBorder extends ShapeBorder { ...@@ -151,6 +153,7 @@ class _StadiumToCircleBorder extends ShapeBorder {
@override @override
ShapeBorder lerpFrom(ShapeBorder a, double t) { ShapeBorder lerpFrom(ShapeBorder a, double t) {
assert(t != null);
if (a is StadiumBorder) { if (a is StadiumBorder) {
return new _StadiumToCircleBorder( return new _StadiumToCircleBorder(
side: BorderSide.lerp(a.side, side, t), side: BorderSide.lerp(a.side, side, t),
...@@ -174,6 +177,7 @@ class _StadiumToCircleBorder extends ShapeBorder { ...@@ -174,6 +177,7 @@ class _StadiumToCircleBorder extends ShapeBorder {
@override @override
ShapeBorder lerpTo(ShapeBorder b, double t) { ShapeBorder lerpTo(ShapeBorder b, double t) {
assert(t != null);
if (b is StadiumBorder) { if (b is StadiumBorder) {
return new _StadiumToCircleBorder( return new _StadiumToCircleBorder(
side: BorderSide.lerp(side, b.side, t), side: BorderSide.lerp(side, b.side, t),
...@@ -303,6 +307,7 @@ class _StadiumToRoundedRectangleBorder extends ShapeBorder { ...@@ -303,6 +307,7 @@ class _StadiumToRoundedRectangleBorder extends ShapeBorder {
@override @override
ShapeBorder lerpFrom(ShapeBorder a, double t) { ShapeBorder lerpFrom(ShapeBorder a, double t) {
assert(t != null);
if (a is StadiumBorder) { if (a is StadiumBorder) {
return new _StadiumToRoundedRectangleBorder( return new _StadiumToRoundedRectangleBorder(
side: BorderSide.lerp(a.side, side, t), side: BorderSide.lerp(a.side, side, t),
...@@ -329,6 +334,7 @@ class _StadiumToRoundedRectangleBorder extends ShapeBorder { ...@@ -329,6 +334,7 @@ class _StadiumToRoundedRectangleBorder extends ShapeBorder {
@override @override
ShapeBorder lerpTo(ShapeBorder b, double t) { ShapeBorder lerpTo(ShapeBorder b, double t) {
assert(t != null);
if (b is StadiumBorder) { if (b is StadiumBorder) {
return new _StadiumToRoundedRectangleBorder( return new _StadiumToRoundedRectangleBorder(
side: BorderSide.lerp(side, b.side, t), side: BorderSide.lerp(side, b.side, t),
......
...@@ -338,7 +338,7 @@ class TextStyle extends Diagnosticable { ...@@ -338,7 +338,7 @@ class TextStyle extends Diagnosticable {
String newDebugLabel; String newDebugLabel;
assert(() { assert(() {
if (this.debugLabel != null) if (this.debugLabel != null)
newDebugLabel = debugLabel ?? 'copy of ${this.debugLabel}'; newDebugLabel = debugLabel ?? '(${this.debugLabel}).copyWith';
return true; return true;
}()); }());
return new TextStyle( return new TextStyle(
...@@ -414,7 +414,7 @@ class TextStyle extends Diagnosticable { ...@@ -414,7 +414,7 @@ class TextStyle extends Diagnosticable {
String modifiedDebugLabel; String modifiedDebugLabel;
assert(() { assert(() {
if (debugLabel != null) if (debugLabel != null)
modifiedDebugLabel = 'modified $debugLabel'; modifiedDebugLabel = '($debugLabel).apply';
return true; return true;
}()); }());
...@@ -459,7 +459,7 @@ class TextStyle extends Diagnosticable { ...@@ -459,7 +459,7 @@ class TextStyle extends Diagnosticable {
String mergedDebugLabel; String mergedDebugLabel;
assert(() { assert(() {
if (other.debugLabel != null || debugLabel != null) if (other.debugLabel != null || debugLabel != null)
mergedDebugLabel = '${other.debugLabel ?? _kDefaultDebugLabel} < ${debugLabel ?? _kDefaultDebugLabel}'; mergedDebugLabel = '(${debugLabel ?? _kDefaultDebugLabel}).merge(${other.debugLabel ?? _kDefaultDebugLabel})';
return true; return true;
}()); }());
...@@ -483,29 +483,44 @@ class TextStyle extends Diagnosticable { ...@@ -483,29 +483,44 @@ class TextStyle extends Diagnosticable {
/// Interpolate between two text styles. /// Interpolate between two text styles.
/// ///
/// This will not work well if the styles don't set the same fields. /// This will not work well if the styles don't set the same fields.
static TextStyle lerp(TextStyle begin, TextStyle end, double t) { ///
assert(begin.inherit == end.inherit); /// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static TextStyle lerp(TextStyle a, TextStyle b, double t) {
assert(a != null);
assert(b != null);
assert(t != null);
assert(a.inherit == b.inherit);
String lerpDebugLabel; String lerpDebugLabel;
assert(() { assert(() {
lerpDebugLabel = 'lerp(${begin.debugLabel ?? _kDefaultDebugLabel}, ${end.debugLabel ?? _kDefaultDebugLabel})'; lerpDebugLabel = 'lerp(${a.debugLabel ?? _kDefaultDebugLabel}${t.toStringAsFixed(1)}${b.debugLabel ?? _kDefaultDebugLabel})';
return true; return true;
}()); }());
return new TextStyle( return new TextStyle(
inherit: end.inherit, inherit: b.inherit,
color: Color.lerp(begin.color, end.color, t), color: Color.lerp(a.color, b.color, t),
fontFamily: t < 0.5 ? begin.fontFamily : end.fontFamily, fontFamily: t < 0.5 ? a.fontFamily : b.fontFamily,
fontSize: ui.lerpDouble(begin.fontSize ?? end.fontSize, end.fontSize ?? begin.fontSize, t), fontSize: ui.lerpDouble(a.fontSize ?? b.fontSize, b.fontSize ?? a.fontSize, t),
fontWeight: FontWeight.lerp(begin.fontWeight, end.fontWeight, t), fontWeight: FontWeight.lerp(a.fontWeight, b.fontWeight, t),
fontStyle: t < 0.5 ? begin.fontStyle : end.fontStyle, fontStyle: t < 0.5 ? a.fontStyle : b.fontStyle,
letterSpacing: ui.lerpDouble(begin.letterSpacing ?? end.letterSpacing, end.letterSpacing ?? begin.letterSpacing, t), letterSpacing: ui.lerpDouble(a.letterSpacing ?? b.letterSpacing, b.letterSpacing ?? a.letterSpacing, t),
wordSpacing: ui.lerpDouble(begin.wordSpacing ?? end.wordSpacing, end.wordSpacing ?? begin.wordSpacing, t), wordSpacing: ui.lerpDouble(a.wordSpacing ?? b.wordSpacing, b.wordSpacing ?? a.wordSpacing, t),
textBaseline: t < 0.5 ? begin.textBaseline : end.textBaseline, textBaseline: t < 0.5 ? a.textBaseline : b.textBaseline,
height: ui.lerpDouble(begin.height ?? end.height, end.height ?? begin.height, t), height: ui.lerpDouble(a.height ?? b.height, b.height ?? a.height, t),
decoration: t < 0.5 ? begin.decoration : end.decoration, decoration: t < 0.5 ? a.decoration : b.decoration,
decorationColor: Color.lerp(begin.decorationColor, end.decorationColor, t), decorationColor: Color.lerp(a.decorationColor, b.decorationColor, t),
decorationStyle: t < 0.5 ? begin.decorationStyle : end.decorationStyle, decorationStyle: t < 0.5 ? a.decorationStyle : b.decorationStyle,
debugLabel: lerpDebugLabel, debugLabel: lerpDebugLabel,
); );
} }
......
...@@ -402,7 +402,20 @@ class BoxConstraints extends Constraints { ...@@ -402,7 +402,20 @@ class BoxConstraints extends Constraints {
/// ///
/// If either is null, this function interpolates from a [BoxConstraints] /// If either is null, this function interpolates from a [BoxConstraints]
/// object whose fields are all set to 0.0. /// object whose fields are all set to 0.0.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static BoxConstraints lerp(BoxConstraints a, BoxConstraints b, double t) { static BoxConstraints lerp(BoxConstraints a, BoxConstraints b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
......
...@@ -124,7 +124,20 @@ class RelativeRect { ...@@ -124,7 +124,20 @@ class RelativeRect {
/// Linearly interpolate between two RelativeRects. /// Linearly interpolate between two RelativeRects.
/// ///
/// If either rect is null, this function interpolates from [RelativeRect.fill]. /// If either rect is null, this function interpolates from [RelativeRect.fill].
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static RelativeRect lerp(RelativeRect a, RelativeRect b, double t) { static RelativeRect lerp(RelativeRect a, RelativeRect b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -137,7 +150,7 @@ class RelativeRect { ...@@ -137,7 +150,7 @@ class RelativeRect {
lerpDouble(a.left, b.left, t), lerpDouble(a.left, b.left, t),
lerpDouble(a.top, b.top, t), lerpDouble(a.top, b.top, t),
lerpDouble(a.right, b.right, t), lerpDouble(a.right, b.right, t),
lerpDouble(a.bottom, b.bottom, t) lerpDouble(a.bottom, b.bottom, t),
); );
} }
......
...@@ -116,15 +116,29 @@ class TableBorder { ...@@ -116,15 +116,29 @@ class TableBorder {
return true; return true;
} }
/// Creates a new border with the widths of this border multiplied by `t`. /// Creates a copy of this border but with the widths scaled by the factor `t`.
///
/// The `t` argument represents the multiplicand, or the position on the
/// timeline for an interpolation from nothing to `this`, with 0.0 meaning
/// that the the object returned should be the nil variant of this object, 1.0
/// meaning that no change should be applied, returning `this` (or something
/// equivalent to `this`), and other values meaning that the object should be
/// multiplied by `t`. Negative values are treated like zero.
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
///
/// See also:
///
/// * [BorderSide.scale], which is used to implement this method.
TableBorder scale(double t) { TableBorder scale(double t) {
return new TableBorder( return new TableBorder(
top: top.copyWith(width: t * top.width), top: top.scale(t),
right: right.copyWith(width: t * right.width), right: right.scale(t),
bottom: bottom.copyWith(width: t * bottom.width), bottom: bottom.scale(t),
left: left.copyWith(width: t * left.width), left: left.scale(t),
horizontalInside: horizontalInside.copyWith(width: t * horizontalInside.width), horizontalInside: horizontalInside.scale(t),
verticalInside: verticalInside.copyWith(width: t * verticalInside.width) verticalInside: verticalInside.scale(t),
); );
} }
...@@ -132,7 +146,20 @@ class TableBorder { ...@@ -132,7 +146,20 @@ class TableBorder {
/// ///
/// If a border is null, it is treated as having only [BorderSide.none] /// If a border is null, it is treated as having only [BorderSide.none]
/// borders. /// borders.
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static TableBorder lerp(TableBorder a, TableBorder b, double t) { static TableBorder lerp(TableBorder a, TableBorder b, double t) {
assert(t != null);
if (a == null && b == null) if (a == null && b == null)
return null; return null;
if (a == null) if (a == null)
...@@ -145,7 +172,7 @@ class TableBorder { ...@@ -145,7 +172,7 @@ class TableBorder {
bottom: BorderSide.lerp(a.bottom, b.bottom, t), bottom: BorderSide.lerp(a.bottom, b.bottom, t),
left: BorderSide.lerp(a.left, b.left, t), left: BorderSide.lerp(a.left, b.left, t),
horizontalInside: BorderSide.lerp(a.horizontalInside, b.horizontalInside, t), horizontalInside: BorderSide.lerp(a.horizontalInside, b.horizontalInside, t),
verticalInside: BorderSide.lerp(a.verticalInside, b.verticalInside, t) verticalInside: BorderSide.lerp(a.verticalInside, b.verticalInside, t),
); );
} }
......
...@@ -65,11 +65,24 @@ class IconThemeData { ...@@ -65,11 +65,24 @@ class IconThemeData {
final double size; final double size;
/// Linearly interpolate between two icon theme data objects. /// Linearly interpolate between two icon theme data objects.
static IconThemeData lerp(IconThemeData begin, IconThemeData end, double t) { ///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]).
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static IconThemeData lerp(IconThemeData a, IconThemeData b, double t) {
assert(t != null);
return new IconThemeData( return new IconThemeData(
color: Color.lerp(begin.color, end.color, t), color: Color.lerp(a.color, b.color, t),
opacity: ui.lerpDouble(begin.opacity, end.opacity, t), opacity: ui.lerpDouble(a.opacity, b.opacity, t),
size: ui.lerpDouble(begin.size, end.size, t) size: ui.lerpDouble(a.size, b.size, t),
); );
} }
......
...@@ -417,7 +417,7 @@ void main() { ...@@ -417,7 +417,7 @@ void main() {
} }
} }
expect(theme.textTheme.display4.debugLabel, 'blackMountainView display4 < englishLike display4'); expect(theme.textTheme.display4.debugLabel, '(englishLike display4).merge(blackMountainView display4)');
}); });
} }
......
...@@ -156,11 +156,11 @@ void main() { ...@@ -156,11 +156,11 @@ void main() {
expect(foo.debugLabel, 'foo'); expect(foo.debugLabel, 'foo');
expect(foo.toString(), 'TextStyle(debugLabel: foo, inherit: true, size: 1.0)'); expect(foo.toString(), 'TextStyle(debugLabel: foo, inherit: true, size: 1.0)');
expect(foo.merge(bar).debugLabel, 'bar < foo'); expect(foo.merge(bar).debugLabel, '(foo).merge(bar)');
expect(foo.merge(bar).merge(baz).debugLabel, 'baz < bar < foo'); expect(foo.merge(bar).merge(baz).debugLabel, '((foo).merge(bar)).merge(baz)');
expect(foo.copyWith().debugLabel, 'copy of foo'); expect(foo.copyWith().debugLabel, '(foo).copyWith');
expect(foo.apply().debugLabel, 'modified foo'); expect(foo.apply().debugLabel, '(foo).apply');
expect(TextStyle.lerp(foo, bar, 0.5).debugLabel, 'lerp(foo, bar)'); expect(TextStyle.lerp(foo, bar, 0.5).debugLabel, 'lerp(foo ⎯0.5→ bar)');
expect(TextStyle.lerp(foo.merge(bar), baz, 0.5).copyWith().debugLabel, 'copy of lerp(bar < foo, baz)'); expect(TextStyle.lerp(foo.merge(bar), baz, 0.51).copyWith().debugLabel, '(lerp((foo).merge(bar) ⎯0.5→ baz)).copyWith');
}); });
} }
...@@ -57,8 +57,8 @@ void main() { ...@@ -57,8 +57,8 @@ void main() {
expect(border3.dimensions, const EdgeInsets.symmetric(horizontal: 1.0, vertical: 1.0)); expect(border3.dimensions, const EdgeInsets.symmetric(horizontal: 1.0, vertical: 1.0));
expect(border3.isUniform, isFalse); expect(border3.isUniform, isFalse);
expect(border3.scale(0.0), new TableBorder.symmetric( expect(border3.scale(0.0), new TableBorder.symmetric(
inside: const BorderSide(width: 0.0), inside: const BorderSide(width: 0.0, style: BorderStyle.none),
outside: const BorderSide(width: 0.0, color: const Color(0xFFFF0000)), outside: const BorderSide(width: 0.0, color: const Color(0xFFFF0000), style: BorderStyle.none),
)); ));
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment