From 8329cf6b675beb9d7a8403e6a512a208bb84efb1 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 16 May 2019 10:10:54 -0700 Subject: [PATCH 01/15] initial --- .../flutter/lib/src/painting/gradient.dart | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index df4043a6a5416..5b93f7796a5fd 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -4,6 +4,7 @@ import 'dart:collection'; import 'dart:math' as math; +import 'dart:typed_data'; import 'dart:ui' as ui show Gradient, lerpDouble; import 'package:flutter/foundation.dart'; @@ -124,7 +125,11 @@ abstract class Gradient { /// If the gradient's configuration is text-direction-dependent, for example /// it uses [AlignmentDirectional] objects instead of [Alignment] /// objects, then the `textDirection` argument must not be null. - Shader createShader(Rect rect, { TextDirection textDirection }); + /// + /// The `transform` property will be applied as a local transform to the + /// gradient, for example if you wish to rotate only the gradient with respect + /// to the drawing. + Shader createShader(Rect rect, { TextDirection textDirection, Float64List transform }); /// Returns a new gradient with its properties scaled by the given factor. /// @@ -330,11 +335,11 @@ class LinearGradient extends Gradient { final TileMode tileMode; @override - Shader createShader(Rect rect, { TextDirection textDirection }) { + Shader createShader(Rect rect, { TextDirection textDirection, Float64List transform }) { return ui.Gradient.linear( begin.resolve(textDirection).withinRect(rect), end.resolve(textDirection).withinRect(rect), - colors, _impliedStops(), tileMode, + colors, _impliedStops(), tileMode, transform, ); } @@ -600,12 +605,12 @@ class RadialGradient extends Gradient { final double focalRadius; @override - Shader createShader(Rect rect, { TextDirection textDirection }) { + Shader createShader(Rect rect, { TextDirection textDirection, Float64List transform }) { return ui.Gradient.radial( center.resolve(textDirection).withinRect(rect), radius * rect.shortestSide, colors, _impliedStops(), tileMode, - null, // transform + transform, focal == null ? null : focal.resolve(textDirection).withinRect(rect), focalRadius * rect.shortestSide, ); @@ -840,12 +845,13 @@ class SweepGradient extends Gradient { final TileMode tileMode; @override - Shader createShader(Rect rect, { TextDirection textDirection }) { + Shader createShader(Rect rect, { TextDirection textDirection, Float64List transform }) { return ui.Gradient.sweep( center.resolve(textDirection).withinRect(rect), colors, _impliedStops(), tileMode, startAngle, endAngle, + transform, ); } From d522884cac74b9a45b0d63c0ed0463693c33449e Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 12:59:40 -0700 Subject: [PATCH 02/15] .. --- packages/flutter/lib/src/painting/gradient.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index bd71b779dec2c..7fa0df7c40ded 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -863,14 +863,12 @@ class SweepGradient extends Gradient { @override Shader createShader(Rect rect, { TextDirection textDirection, Matrix4 transform }) { final Offset resolvedCenter = center.resolve(textDirection).withinRect(rect); - - print(resolvedCenter); return ui.Gradient.sweep( resolvedCenter, colors, _impliedStops(), tileMode, startAngle, endAngle, - transform == null ? null : (transform..translate(-resolvedCenter.dx, -resolvedCenter.dy)).storage, + transform?.storage, ); } From b345595465a552ea30da4ddea5d3decfca40f723 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 14:26:33 -0700 Subject: [PATCH 03/15] fix exmaple --- packages/flutter/lib/src/painting/gradient.dart | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index 7fa0df7c40ded..8483dde9e6418 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -135,8 +135,22 @@ abstract class Gradient { /// [Canvas] or the [Rect] that the gradient is shading. /// /// ```dart + /// import 'dart:math' as math; + /// /// void paint(Canvas canvas, Rect rect) { - /// final Matrix4 transform = Matrix4.identity()..rotateZ(0.785398); // 45 degrees. + /// // Calculate 45 degrees (clockwise rotation) as radians. + /// final double radians = 45 * math.pi / 180; + /// + /// // Calculate the point to rotate about. + /// final double sinRadians = math.sin(radians); + /// final double oneMinusCosRadians = 1 - math.cos(radians); + /// final Offset center = rect.center; + /// final double originX = sinRadians * center.dy + oneMinusCosRadians * center.dx; + /// final double originY = -sinRadians * center.dy + oneMinusCosRadians * center.dx; + /// + /// final Matrix4 transform = Matrix4.identity() + /// ..translate(originX, originY) + /// ..rotateZ(radians); /// final SweepGradient gradient = SweepGradient(colors: [Colors.white, Colors.blue]); /// final Paint paint = Paint() /// ..shader = gradient.createShader(rect, transform: transform) From cf2e0a3a96b9b5ee10866f45fffabcfac1df361d Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 14:56:24 -0700 Subject: [PATCH 04/15] tests --- .../flutter/test/painting/gradient_test.dart | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/flutter/test/painting/gradient_test.dart b/packages/flutter/test/painting/gradient_test.dart index e11381da4be08..17fc5faa35a42 100644 --- a/packages/flutter/test/painting/gradient_test.dart +++ b/packages/flutter/test/painting/gradient_test.dart @@ -3,8 +3,10 @@ // found in the LICENSE file. import 'dart:math' as math; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/painting.dart'; +import 'package:vector_math/vector_math_64.dart'; void main() { test('LinearGradient scale test', () { @@ -765,4 +767,51 @@ void main() { expect(() { test2a.createShader(rect); }, throwsArgumentError); expect(() { test2b.createShader(rect); }, throwsArgumentError); }); + + group('Transforms', () { + const List colors = [Color(0xFFFFFFFF), Color(0xFF009900)]; + const Rect rect = Rect.fromLTWH(0.0, 0.0, 300.0, 400.0); + + double radians(double degrees) => degrees * math.pi / 180; + + Matrix4 rotatedTransform(double radians, Rect rect) { + // Calculate the point to rotate about. + final double sinRadians = math.sin(radians); + final double oneMinusCosRadians = 1 - math.cos(radians); + final Offset center = rect.center; + final double originX = sinRadians * center.dy + oneMinusCosRadians * center.dx; + final double originY = -sinRadians * center.dy + oneMinusCosRadians * center.dx; + + return Matrix4.identity() + ..translate(originX, originY) + ..rotateZ(radians); + } + + testWidgets('LinearGradient - 45 degrees', (WidgetTester tester) async { + final Shader shader = const LinearGradient( + colors: colors + ).createShader( + rect, + transform: rotatedTransform(radians(45), rect), + ); + await tester.pumpWidget(CustomPaint(painter: GradientPainter(shader, rect))); + expect(find.byType(CustomPaint), matchesGoldenFile('linear_gradient_45.png')); + }); + }); +} + +class GradientPainter extends CustomPainter { + const GradientPainter(this.shader, this.rect); + + final Shader shader; + final Rect rect; + + @override + void paint(Canvas canvas, Size size) { + canvas.drawRect(rect, Paint()..shader = shader); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; + } From db13dc8db958c1687af67ff1612fbf8bce73d3aa Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 14:56:24 -0700 Subject: [PATCH 05/15] tests --- .../flutter/test/painting/gradient_test.dart | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/packages/flutter/test/painting/gradient_test.dart b/packages/flutter/test/painting/gradient_test.dart index e11381da4be08..8bec08843bccd 100644 --- a/packages/flutter/test/painting/gradient_test.dart +++ b/packages/flutter/test/painting/gradient_test.dart @@ -3,8 +3,10 @@ // found in the LICENSE file. import 'dart:math' as math; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/painting.dart'; +import 'package:vector_math/vector_math_64.dart'; void main() { test('LinearGradient scale test', () { @@ -765,4 +767,68 @@ void main() { expect(() { test2a.createShader(rect); }, throwsArgumentError); expect(() { test2b.createShader(rect); }, throwsArgumentError); }); + + group('Transforms', () { + const List colors = [Color(0xFFFFFFFF), Color(0xFF009900)]; + const Rect rect = Rect.fromLTWH(0.0, 0.0, 300.0, 400.0); + const List gradients = [ + LinearGradient(colors: colors), + RadialGradient(colors: colors), + SweepGradient(colors: colors), + ]; + + double radians(double degrees) => degrees * math.pi / 180; + + Matrix4 rotatedTransform(double radians, Rect rect) { + // Calculate the point to rotate about. + final double sinRadians = math.sin(radians); + final double oneMinusCosRadians = 1 - math.cos(radians); + final Offset center = rect.center; + final double originX = sinRadians * center.dy + oneMinusCosRadians * center.dx; + final double originY = -sinRadians * center.dy + oneMinusCosRadians * center.dx; + + return Matrix4.identity() + ..translate(originX, originY) + ..rotateZ(radians); + } + + Future runTest( + WidgetTester tester, + double degrees, + ) async { + for (Gradient gradient in gradients) { + final String goldenName = '${gradient.runtimeType}_$degrees.png'; + final Shader shader = gradient.createShader( + rect, + transform: rotatedTransform(radians(degrees), rect), + ); + await tester.pumpWidget(CustomPaint(painter: GradientPainter(shader, rect))); + expect(find.byType(CustomPaint), matchesGoldenFile(goldenName)); + } + } + + testWidgets('Gradients - 45 degrees', (WidgetTester tester) async { + await runTest(tester, 45); + }); + + testWidgets('Gradients - 90 degrees', (WidgetTester tester) async { + await runTest(tester, 90); + }); + }); +} + +class GradientPainter extends CustomPainter { + const GradientPainter(this.shader, this.rect); + + final Shader shader; + final Rect rect; + + @override + void paint(Canvas canvas, Size size) { + canvas.drawRect(rect, Paint()..shader = shader); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; + } From db8df6bc8df9d4308cc0665f5d27a3abda4ef447 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 17:00:05 -0700 Subject: [PATCH 06/15] tests --- .../flutter/lib/src/painting/gradient.dart | 2 +- .../flutter/test/painting/gradient_test.dart | 32 +++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index 8483dde9e6418..f0f2be4fbb4bd 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -146,7 +146,7 @@ abstract class Gradient { /// final double oneMinusCosRadians = 1 - math.cos(radians); /// final Offset center = rect.center; /// final double originX = sinRadians * center.dy + oneMinusCosRadians * center.dx; - /// final double originY = -sinRadians * center.dy + oneMinusCosRadians * center.dx; + /// final double originY = -sinRadians * center.dx + oneMinusCosRadians * center.dy; /// /// final Matrix4 transform = Matrix4.identity() /// ..translate(originX, originY) diff --git a/packages/flutter/test/painting/gradient_test.dart b/packages/flutter/test/painting/gradient_test.dart index dcd6384eda442..5c131d97ba752 100644 --- a/packages/flutter/test/painting/gradient_test.dart +++ b/packages/flutter/test/painting/gradient_test.dart @@ -769,14 +769,21 @@ void main() { }); group('Transforms', () { - const List colors = [Color(0xFFFFFFFF), Color(0xFF009900)]; + const List colors = [Color(0xFFFFFFFF), Color(0xFF000088)]; const Rect rect = Rect.fromLTWH(0.0, 0.0, 300.0, 400.0); const List gradients = [ LinearGradient(colors: colors), - RadialGradient(colors: colors), + // A radial gradient won't be interesting to rotate unless the center is changed. + RadialGradient(colors: colors, center: Alignment.topCenter), SweepGradient(colors: colors), ]; + const Map gradientSnakeCase = { + LinearGradient: 'linear_gradient', + RadialGradient: 'radial_gradient', + SweepGradient: 'sweep_gradient', + }; + double radians(double degrees) => degrees * math.pi / 180; Matrix4 rotatedTransform(double radians, Rect rect) { @@ -785,7 +792,7 @@ void main() { final double oneMinusCosRadians = 1 - math.cos(radians); final Offset center = rect.center; final double originX = sinRadians * center.dy + oneMinusCosRadians * center.dx; - final double originY = -sinRadians * center.dy + oneMinusCosRadians * center.dx; + final double originY = -sinRadians * center.dx + oneMinusCosRadians * center.dy; return Matrix4.identity() ..translate(originX, originY) @@ -794,13 +801,24 @@ void main() { Future runTest(WidgetTester tester, double degrees) async { for (Gradient gradient in gradients) { - final String goldenName = '${gradient.runtimeType}_$degrees.png'; + final String goldenName = '${gradientSnakeCase[gradient.runtimeType]}_$degrees.png'; final Shader shader = gradient.createShader( rect, transform: rotatedTransform(radians(degrees), rect), ); - await tester.pumpWidget(CustomPaint(painter: GradientPainter(shader, rect))); - expect(find.byType(CustomPaint), matchesGoldenFile(goldenName)); + final Key painterKey = UniqueKey(); + await tester.pumpWidget(Center( + child: SizedBox.fromSize( + size: rect.size, + child: RepaintBoundary( + key: painterKey, + child: CustomPaint( + painter: GradientPainter(shader, rect) + ), + ), + ), + )); + await expectLater(find.byKey(painterKey), matchesGoldenFile(goldenName)); } } @@ -826,6 +844,6 @@ class GradientPainter extends CustomPainter { } @override - bool shouldRepaint(CustomPainter oldDelegate) => false; + bool shouldRepaint(CustomPainter oldDelegate) => true; } From 13d7f9915b08c0971db43c461249dbaa9c7ffa4d Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 20:32:07 -0700 Subject: [PATCH 07/15] GradientTransform --- .../flutter/lib/src/painting/gradient.dart | 137 ++++++++++++------ .../flutter/test/painting/gradient_test.dart | 72 ++++----- 2 files changed, 124 insertions(+), 85 deletions(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index f0f2be4fbb4bd..219a2da67d51c 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -4,6 +4,7 @@ import 'dart:collection'; import 'dart:math' as math; +import 'dart:typed_data'; import 'dart:ui' as ui show Gradient, lerpDouble; import 'package:flutter/foundation.dart'; @@ -58,6 +59,63 @@ _ColorsAndStops _interpolateColorsAndStops( return _ColorsAndStops(interpolatedColors, interpolatedStops); } +/// Base class for transforming gradient shaders without applying the same +/// transform to the entire canvas. +/// +/// For example, a [SweepGradient] normally starts its gradation at 3 o'clock +/// and draws clockwise. To have the sweep appear to start at 6 o'clock, supply +/// a [GradientRotation] of `0.785398` radians (i.e. `45` degrees). +@immutable +abstract class GradientTransform { + /// A const constructor that allows subclasses to be const. + const GradientTransform(); + + /// When a [Gradient] creates its [Shader], it will call this method to + /// determine what transform to apply to the shader for the given [Rect] and + /// [TextDirection]. + /// + /// Implementers may return null from this method, which achieves the same + /// final effect as returning [Matrix4.identity]. + Matrix4 transform(Rect bounds, {TextDirection textDirection}); +} + +/// A [GradientTransform] that rotates the gradient around the center-point of +/// its bounding box. +/// +/// For example, the following would rotate a sweep gradient by a quarter turn +/// clockwise: +/// +/// ```dart +/// SweepGradient( +/// colors: colors, +/// transform: GradientRotation(0.785398), +/// ); +/// ``` +@immutable +class GradientRotation extends GradientTransform { + /// Constructs a `GradientRotation` for the specified angle. + /// + /// The angle is in radians in the clockwise direction. + const GradientRotation(this.radians); + + /// The angle of rotation. + final double radians; + + @override + Matrix4 transform(Rect bounds, {TextDirection textDirection}) { + assert(bounds != null); + final double sinRadians = math.sin(radians); + final double oneMinusCosRadians = 1 - math.cos(radians); + final Offset center = bounds.center; + final double originX = sinRadians * center.dy + oneMinusCosRadians * center.dx; + final double originY = -sinRadians * center.dx + oneMinusCosRadians * center.dy; + + return Matrix4.identity() + ..translate(originX, originY) + ..rotateZ(radians); + } +} + /// A 2D gradient. /// /// This is an interface that allows [LinearGradient], [RadialGradient], and @@ -77,9 +135,17 @@ abstract class Gradient { /// If specified, the [stops] argument must have the same number of entries as /// [colors] (this is also not verified until the [createShader] method is /// called). + /// + /// The [transform] argument can be applied to transform _only_ the gradient, + /// without rotating the canvas itself or other geometry on the canvas. For + /// example, a `GradientRotation(0.785398)` will result in a [SweepGradient] + /// that starts from a position of 6 o'clock instead of 3 o'clock, assuming + /// no other rotation or perspective transformations have been applied to the + /// [Canvas]. If null, no transformation is applied. const Gradient({ @required this.colors, this.stops, + this.transform, }) : assert(colors != null); /// The colors the gradient should obtain at each of the stops. @@ -108,6 +174,12 @@ abstract class Gradient { /// with the first stop at 0.0 and the last stop at 1.0. final List stops; + /// The transform, if any, to apply to the gradient. + /// + /// This transform is in addition to any other transformations applied to the + /// canvas, but does not add any transformations to the canvas. + final GradientTransform transform; + List _impliedStops() { if (stops != null) return stops; @@ -126,40 +198,9 @@ abstract class Gradient { /// it uses [AlignmentDirectional] objects instead of [Alignment] /// objects, then the `textDirection` argument must not be null. /// - /// The `transform` property will be applied as a local transform to the - /// gradient, allowing the shader to be transformed without transforming the - /// entire canvas. - /// - /// {@tool sample} - /// This example shows how to rotate a [SweepGradient] without rotating the - /// [Canvas] or the [Rect] that the gradient is shading. - /// - /// ```dart - /// import 'dart:math' as math; - /// - /// void paint(Canvas canvas, Rect rect) { - /// // Calculate 45 degrees (clockwise rotation) as radians. - /// final double radians = 45 * math.pi / 180; - /// - /// // Calculate the point to rotate about. - /// final double sinRadians = math.sin(radians); - /// final double oneMinusCosRadians = 1 - math.cos(radians); - /// final Offset center = rect.center; - /// final double originX = sinRadians * center.dy + oneMinusCosRadians * center.dx; - /// final double originY = -sinRadians * center.dx + oneMinusCosRadians * center.dy; - /// - /// final Matrix4 transform = Matrix4.identity() - /// ..translate(originX, originY) - /// ..rotateZ(radians); - /// final SweepGradient gradient = SweepGradient(colors: [Colors.white, Colors.blue]); - /// final Paint paint = Paint() - /// ..shader = gradient.createShader(rect, transform: transform) - /// ..color = Colors.blue; - /// canvas.drawRect(rect, paint); - /// } - /// ``` - /// {@end-tool} - Shader createShader(Rect rect, { TextDirection textDirection, Matrix4 transform }); + /// The shader's transform will be resolved from the [transform] of this + /// gradient. + Shader createShader(Rect rect, { TextDirection textDirection }); /// Returns a new gradient with its properties scaled by the given factor. /// @@ -255,6 +296,10 @@ abstract class Gradient { assert(a != null && b != null); return t < 0.5 ? a.scale(1.0 - (t * 2.0)) : b.scale((t - 0.5) * 2.0); } + + Float64List _resolveTransform(Rect bounds, TextDirection textDirection) { + return transform?.transform(bounds, textDirection: textDirection)?.storage; + } } /// A 2D linear gradient. @@ -319,10 +364,11 @@ class LinearGradient extends Gradient { @required List colors, List stops, this.tileMode = TileMode.clamp, + GradientTransform transform, }) : assert(begin != null), assert(end != null), assert(tileMode != null), - super(colors: colors, stops: stops); + super(colors: colors, stops: stops, transform: transform); /// The offset at which stop 0.0 of the gradient is placed. /// @@ -365,11 +411,11 @@ class LinearGradient extends Gradient { final TileMode tileMode; @override - Shader createShader(Rect rect, { TextDirection textDirection, Matrix4 transform }) { + Shader createShader(Rect rect, { TextDirection textDirection }) { return ui.Gradient.linear( begin.resolve(textDirection).withinRect(rect), end.resolve(textDirection).withinRect(rect), - colors, _impliedStops(), tileMode, transform?.storage, + colors, _impliedStops(), tileMode, _resolveTransform(rect, textDirection), ); } @@ -568,11 +614,12 @@ class RadialGradient extends Gradient { this.tileMode = TileMode.clamp, this.focal, this.focalRadius = 0.0, + GradientTransform transform, }) : assert(center != null), assert(radius != null), assert(tileMode != null), assert(focalRadius != null), - super(colors: colors, stops: stops); + super(colors: colors, stops: stops, transform: transform); /// The center of the gradient, as an offset into the (-1.0, -1.0) x (1.0, 1.0) /// square describing the gradient which will be mapped onto the paint box. @@ -635,12 +682,12 @@ class RadialGradient extends Gradient { final double focalRadius; @override - Shader createShader(Rect rect, { TextDirection textDirection, Matrix4 transform }) { + Shader createShader(Rect rect, { TextDirection textDirection }) { return ui.Gradient.radial( center.resolve(textDirection).withinRect(rect), radius * rect.shortestSide, colors, _impliedStops(), tileMode, - transform?.storage, + _resolveTransform(rect, textDirection), focal == null ? null : focal.resolve(textDirection).withinRect(rect), focalRadius * rect.shortestSide, ); @@ -832,11 +879,12 @@ class SweepGradient extends Gradient { @required List colors, List stops, this.tileMode = TileMode.clamp, + GradientTransform transform, }) : assert(center != null), assert(startAngle != null), assert(endAngle != null), assert(tileMode != null), - super(colors: colors, stops: stops); + super(colors: colors, stops: stops, transform: transform); /// The center of the gradient, as an offset into the (-1.0, -1.0) x (1.0, 1.0) /// square describing the gradient which will be mapped onto the paint box. @@ -875,14 +923,13 @@ class SweepGradient extends Gradient { final TileMode tileMode; @override - Shader createShader(Rect rect, { TextDirection textDirection, Matrix4 transform }) { - final Offset resolvedCenter = center.resolve(textDirection).withinRect(rect); + Shader createShader(Rect rect, { TextDirection textDirection }) { return ui.Gradient.sweep( - resolvedCenter, + center.resolve(textDirection).withinRect(rect), colors, _impliedStops(), tileMode, startAngle, endAngle, - transform?.storage, + _resolveTransform(rect, textDirection), ); } diff --git a/packages/flutter/test/painting/gradient_test.dart b/packages/flutter/test/painting/gradient_test.dart index 5c131d97ba752..e22e01a64c8d0 100644 --- a/packages/flutter/test/painting/gradient_test.dart +++ b/packages/flutter/test/painting/gradient_test.dart @@ -771,11 +771,17 @@ void main() { group('Transforms', () { const List colors = [Color(0xFFFFFFFF), Color(0xFF000088)]; const Rect rect = Rect.fromLTWH(0.0, 0.0, 300.0, 400.0); - const List gradients = [ - LinearGradient(colors: colors), + const List gradients45 = [ + LinearGradient(colors: colors, transform: GradientRotation(0.785398)), // A radial gradient won't be interesting to rotate unless the center is changed. - RadialGradient(colors: colors, center: Alignment.topCenter), - SweepGradient(colors: colors), + RadialGradient(colors: colors, center: Alignment.topCenter, transform: GradientRotation(0.785398)), + SweepGradient(colors: colors, transform: GradientRotation(0.785398)), + ]; + const List gradients90 = [ + LinearGradient(colors: colors, transform: GradientRotation(1.5708)), + // A radial gradient won't be interesting to rotate unless the center is changed. + RadialGradient(colors: colors, center: Alignment.topCenter, transform: GradientRotation(1.5708)), + SweepGradient(colors: colors, transform: GradientRotation(1.5708)), ]; const Map gradientSnakeCase = { @@ -784,50 +790,36 @@ void main() { SweepGradient: 'sweep_gradient', }; - double radians(double degrees) => degrees * math.pi / 180; - - Matrix4 rotatedTransform(double radians, Rect rect) { - // Calculate the point to rotate about. - final double sinRadians = math.sin(radians); - final double oneMinusCosRadians = 1 - math.cos(radians); - final Offset center = rect.center; - final double originX = sinRadians * center.dy + oneMinusCosRadians * center.dx; - final double originY = -sinRadians * center.dx + oneMinusCosRadians * center.dy; - - return Matrix4.identity() - ..translate(originX, originY) - ..rotateZ(radians); - } - - Future runTest(WidgetTester tester, double degrees) async { - for (Gradient gradient in gradients) { - final String goldenName = '${gradientSnakeCase[gradient.runtimeType]}_$degrees.png'; - final Shader shader = gradient.createShader( - rect, - transform: rotatedTransform(radians(degrees), rect), - ); - final Key painterKey = UniqueKey(); - await tester.pumpWidget(Center( - child: SizedBox.fromSize( - size: rect.size, - child: RepaintBoundary( - key: painterKey, - child: CustomPaint( - painter: GradientPainter(shader, rect) - ), + Future runTest(WidgetTester tester, Gradient gradient, double degrees) async { + final String goldenName = '${gradientSnakeCase[gradient.runtimeType]}_$degrees.png'; + final Shader shader = gradient.createShader( + rect, + ); + final Key painterKey = UniqueKey(); + await tester.pumpWidget(Center( + child: SizedBox.fromSize( + size: rect.size, + child: RepaintBoundary( + key: painterKey, + child: CustomPaint( + painter: GradientPainter(shader, rect) ), ), - )); - await expectLater(find.byKey(painterKey), matchesGoldenFile(goldenName)); - } + ), + )); + await expectLater(find.byKey(painterKey), matchesGoldenFile(goldenName)); } testWidgets('Gradients - 45 degrees', (WidgetTester tester) async { - await runTest(tester, 45); + for (Gradient gradient in gradients45) { + await runTest(tester, gradient, 45); + } }); testWidgets('Gradients - 90 degrees', (WidgetTester tester) async { - await runTest(tester, 90); + for (Gradient gradient in gradients90) { + await runTest(tester, gradient, 90); + } }); }); } From 6060c6b7e03c925e12f62accd9c81dbbc7ba86bb Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 20:43:07 -0700 Subject: [PATCH 08/15] sample --- .../flutter/lib/src/painting/gradient.dart | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index 219a2da67d51c..777050caf49e6 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -853,9 +853,36 @@ class RadialGradient extends Gradient { /// Color(0xFF4285F4), // blue again to seamlessly transition to the start /// ], /// stops: const [0.0, 0.25, 0.5, 0.75, 1.0], -/// ), +/// ), +/// ) +/// ) +/// ``` +/// {@end-tool} +/// +/// {@tool sample} +/// +/// This sample takes the above gradient and rotates it by 0.785398 radians, +/// i.e. 45 degrees. +/// +/// ```dart +/// Container( +/// decoration: BoxDecoration( +/// gradient: SweepGradient( +/// center: FractionalOffset.center, +/// startAngle: 0.0, +/// endAngle: math.pi * 2, +/// colors: const [ +/// Color(0xFF4285F4), // blue +/// Color(0xFF34A853), // green +/// Color(0xFFFBBC05), // yellow +/// Color(0xFFEA4335), // red +/// Color(0xFF4285F4), // blue again to seamlessly transition to the start +/// ], +/// stops: const [0.0, 0.25, 0.5, 0.75, 1.0], +/// transform: GradientTransform(0.785398), +/// ), /// ), -/// ) +/// ) /// ``` /// {@end-tool} /// From 7662328349465fdabb8f91b97fccfffa8496a203 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 20:44:56 -0700 Subject: [PATCH 09/15] unused import --- packages/flutter/test/painting/gradient_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/flutter/test/painting/gradient_test.dart b/packages/flutter/test/painting/gradient_test.dart index e22e01a64c8d0..7a1d89fc28083 100644 --- a/packages/flutter/test/painting/gradient_test.dart +++ b/packages/flutter/test/painting/gradient_test.dart @@ -6,7 +6,6 @@ import 'dart:math' as math; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/painting.dart'; -import 'package:vector_math/vector_math_64.dart'; void main() { test('LinearGradient scale test', () { From 3c964f7c31e0a19a9bcc36c4849c67bc0bc41f19 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 20:48:17 -0700 Subject: [PATCH 10/15] reapply golden update... --- bin/internal/goldens.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/goldens.version b/bin/internal/goldens.version index 9a7f8f1d588ca..80258d29cffba 100644 --- a/bin/internal/goldens.version +++ b/bin/internal/goldens.version @@ -1 +1 @@ -7efcec3e8b0bbb6748a992b23a0a89300aa323c7 +9a2854e5a94f563b1452cbc688b4ff8f7e746991 From 122e5aa43c256af3e2a5d5cae985500f5028249f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 10 Oct 2019 20:59:02 -0700 Subject: [PATCH 11/15] fix sample --- packages/flutter/lib/src/painting/gradient.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index 777050caf49e6..95abe76041dc3 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -879,7 +879,7 @@ class RadialGradient extends Gradient { /// Color(0xFF4285F4), // blue again to seamlessly transition to the start /// ], /// stops: const [0.0, 0.25, 0.5, 0.75, 1.0], -/// transform: GradientTransform(0.785398), +/// transform: GradientRotation(0.785398), /// ), /// ), /// ) From 326acfa2de765e31bd7c72169c77942b8b9df844 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 11 Oct 2019 08:04:06 -0700 Subject: [PATCH 12/15] review .. may require on emore goldnes bump --- .../flutter/lib/src/painting/gradient.dart | 21 ++++++++++--------- .../flutter/test/painting/gradient_test.dart | 12 +++++------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index 95abe76041dc3..780835ecfe8c3 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -64,10 +64,10 @@ _ColorsAndStops _interpolateColorsAndStops( /// /// For example, a [SweepGradient] normally starts its gradation at 3 o'clock /// and draws clockwise. To have the sweep appear to start at 6 o'clock, supply -/// a [GradientRotation] of `0.785398` radians (i.e. `45` degrees). +/// a [GradientRotation] of `pi/4` radians (i.e. 45 degrees). @immutable abstract class GradientTransform { - /// A const constructor that allows subclasses to be const. + /// A const constructor so that subclasses may be const. const GradientTransform(); /// When a [Gradient] creates its [Shader], it will call this method to @@ -82,23 +82,24 @@ abstract class GradientTransform { /// A [GradientTransform] that rotates the gradient around the center-point of /// its bounding box. /// -/// For example, the following would rotate a sweep gradient by a quarter turn -/// clockwise: +/// {@tool sample} +/// +/// This sample would rotate a sweep gradient by a quarter turn clockwise: /// /// ```dart /// SweepGradient( /// colors: colors, -/// transform: GradientRotation(0.785398), +/// transform: GradientRotation(math.pi/4), /// ); /// ``` @immutable class GradientRotation extends GradientTransform { - /// Constructs a `GradientRotation` for the specified angle. + /// Constructs a [GradientRotation] for the specified angle. /// /// The angle is in radians in the clockwise direction. const GradientRotation(this.radians); - /// The angle of rotation. + /// The angle of rotation in radians in the clockwise direction. final double radians; @override @@ -138,7 +139,7 @@ abstract class Gradient { /// /// The [transform] argument can be applied to transform _only_ the gradient, /// without rotating the canvas itself or other geometry on the canvas. For - /// example, a `GradientRotation(0.785398)` will result in a [SweepGradient] + /// example, a `GradientRotation(math.pi/4)` will result in a [SweepGradient] /// that starts from a position of 6 o'clock instead of 3 o'clock, assuming /// no other rotation or perspective transformations have been applied to the /// [Canvas]. If null, no transformation is applied. @@ -861,7 +862,7 @@ class RadialGradient extends Gradient { /// /// {@tool sample} /// -/// This sample takes the above gradient and rotates it by 0.785398 radians, +/// This sample takes the above gradient and rotates it by `math.pi/4` radians, /// i.e. 45 degrees. /// /// ```dart @@ -879,7 +880,7 @@ class RadialGradient extends Gradient { /// Color(0xFF4285F4), // blue again to seamlessly transition to the start /// ], /// stops: const [0.0, 0.25, 0.5, 0.75, 1.0], -/// transform: GradientRotation(0.785398), +/// transform: GradientRotation(math.pi/4), /// ), /// ), /// ) diff --git a/packages/flutter/test/painting/gradient_test.dart b/packages/flutter/test/painting/gradient_test.dart index 7a1d89fc28083..6e567ae6884fe 100644 --- a/packages/flutter/test/painting/gradient_test.dart +++ b/packages/flutter/test/painting/gradient_test.dart @@ -771,16 +771,16 @@ void main() { const List colors = [Color(0xFFFFFFFF), Color(0xFF000088)]; const Rect rect = Rect.fromLTWH(0.0, 0.0, 300.0, 400.0); const List gradients45 = [ - LinearGradient(colors: colors, transform: GradientRotation(0.785398)), + LinearGradient(colors: colors, transform: GradientRotation(math.pi/4)), // A radial gradient won't be interesting to rotate unless the center is changed. - RadialGradient(colors: colors, center: Alignment.topCenter, transform: GradientRotation(0.785398)), - SweepGradient(colors: colors, transform: GradientRotation(0.785398)), + RadialGradient(colors: colors, center: Alignment.topCenter, transform: GradientRotation(math.pi/4)), + SweepGradient(colors: colors, transform: GradientRotation(math.pi/4)), ]; const List gradients90 = [ - LinearGradient(colors: colors, transform: GradientRotation(1.5708)), + LinearGradient(colors: colors, transform: GradientRotation(math.pi/2)), // A radial gradient won't be interesting to rotate unless the center is changed. - RadialGradient(colors: colors, center: Alignment.topCenter, transform: GradientRotation(1.5708)), - SweepGradient(colors: colors, transform: GradientRotation(1.5708)), + RadialGradient(colors: colors, center: Alignment.topCenter, transform: GradientRotation(math.pi/2)), + SweepGradient(colors: colors, transform: GradientRotation(math.pi/2)), ]; const Map gradientSnakeCase = { From 36b0cf813c136d5b7761bc5ad0987384417fda75 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 11 Oct 2019 08:22:42 -0700 Subject: [PATCH 13/15] sample --- packages/flutter/lib/src/painting/gradient.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index 780835ecfe8c3..0769a00957ebf 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -87,8 +87,8 @@ abstract class GradientTransform { /// This sample would rotate a sweep gradient by a quarter turn clockwise: /// /// ```dart -/// SweepGradient( -/// colors: colors, +/// const SweepGradient( +/// colors: [Color(0xFFFFFFFF), Color(0xFF009900)], /// transform: GradientRotation(math.pi/4), /// ); /// ``` From b13c116f8aff281219253e8fe89cebb8d3d26496 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 11 Oct 2019 08:42:05 -0700 Subject: [PATCH 14/15] rev goldens again --- bin/internal/goldens.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/goldens.version b/bin/internal/goldens.version index 80258d29cffba..db957bdeb5ace 100644 --- a/bin/internal/goldens.version +++ b/bin/internal/goldens.version @@ -1 +1 @@ -9a2854e5a94f563b1452cbc688b4ff8f7e746991 +fa13c1b039e693123888e434e4ee1f9ff79d3b6e From cbde19298ae260477d2d119385b179058b4bbf88 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 11 Oct 2019 10:06:43 -0700 Subject: [PATCH 15/15] Fix sample agian. --- packages/flutter/lib/src/painting/gradient.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index 0769a00957ebf..083aedf0112c4 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -87,7 +87,7 @@ abstract class GradientTransform { /// This sample would rotate a sweep gradient by a quarter turn clockwise: /// /// ```dart -/// const SweepGradient( +/// const SweepGradient gradient = SweepGradient( /// colors: [Color(0xFFFFFFFF), Color(0xFF009900)], /// transform: GradientRotation(math.pi/4), /// );