From 8fbeacfe62cca8fb4997736893d55c7480de7609 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 7 Mar 2022 15:04:48 -0800 Subject: [PATCH 1/2] Add some missing tests, fix bugs --- .../lib/src/geometry/matrix.dart | 34 +++++- .../lib/src/paint.dart | 32 +++--- .../lib/src/svg/parsers.dart | 18 +-- .../lib/src/svg/theme.dart | 5 +- .../test/matrix_test.dart | 7 ++ .../test/paint_test.dart | 83 ++++++++++++++ .../test/parsers_test.dart | 105 ++++++++++++++++++ .../test/theme_test.dart | 85 ++++++++++++++ 8 files changed, 327 insertions(+), 42 deletions(-) create mode 100644 packages/vector_graphics_compiler/test/parsers_test.dart create mode 100644 packages/vector_graphics_compiler/test/theme_test.dart diff --git a/packages/vector_graphics_compiler/lib/src/geometry/matrix.dart b/packages/vector_graphics_compiler/lib/src/geometry/matrix.dart index e70b186c..7ffb4da6 100644 --- a/packages/vector_graphics_compiler/lib/src/geometry/matrix.dart +++ b/packages/vector_graphics_compiler/lib/src/geometry/matrix.dart @@ -18,8 +18,8 @@ class AffineMatrix { this.d, this.e, this.f, [ - this._m4_10 = 1.0, - ]); + double? m4_10, + ]) : _m4_10 = m4_10 ?? (1.0 * a); /// The identity affine matrix. static const AffineMatrix identity = AffineMatrix(1, 0, 0, 1, 0, 0); @@ -95,6 +95,32 @@ class AffineMatrix { ); } + /// Creates a new affine matrix, skewed along the x axis. + AffineMatrix xSkewed(double x) { + return multiplied(AffineMatrix( + identity.a, + identity.b, + math.tan(x), + identity.d, + identity.e, + identity.f, + identity._m4_10, + )); + } + + /// Creates a new affine matrix, skewed along the y axis. + AffineMatrix ySkewed(double y) { + return multiplied(AffineMatrix( + identity.a, + math.tan(y), + identity.c, + identity.d, + identity.e, + identity.f, + identity._m4_10, + )); + } + /// Creates a new affine matrix of this concatenated with `other`. AffineMatrix multiplied(AffineMatrix other) { return AffineMatrix( @@ -104,7 +130,7 @@ class AffineMatrix { (b * other.c) + (d * other.d), (a * other.e) + (c * other.f) + e, (b * other.e) + (d * other.f) + f, - _m4_10, + _m4_10 * other._m4_10, ); } @@ -188,6 +214,6 @@ class AffineMatrix { String toString() => ''' [ $a, $c, $e ] [ $b, $d, $f ] -[ 0.0, 0.0, 1.0 ] +[ 0.0, 0.0, 1.0 ] // _m4_10 = $_m4_10 '''; } diff --git a/packages/vector_graphics_compiler/lib/src/paint.dart b/packages/vector_graphics_compiler/lib/src/paint.dart index e4a987f6..ac907321 100644 --- a/packages/vector_graphics_compiler/lib/src/paint.dart +++ b/packages/vector_graphics_compiler/lib/src/paint.dart @@ -76,12 +76,8 @@ class Color { /// A shading program to apply to a [Paint]. Implemented in [LinearGradient] and /// [RadialGradient]. abstract class Shader { - /// Allows subclasses to be const, and specifies the human-readable type name - /// for this shader. - const Shader(this.typeName); - - /// A human readable name for this shader, i.e. 'linearGradient'. - final String typeName; + /// Allows subclasses to be const. + const Shader(); } /// A [Shader] that describes a linear gradient from [from] to [to]. @@ -112,7 +108,7 @@ class LinearGradient extends Shader { required this.tileMode, this.unitMode = GradientUnitMode.objectBoundingBox, this.transform, - }) : super('linearGradient'); + }); /// The start point of the gradient, as specified by [tileMode]. final Point from; @@ -155,7 +151,7 @@ class LinearGradient extends Shader { @override String toString() { return ''' -final $typeName$hashCode = Gradient.linear( +Gradient.linear( const Offset(${from.x}, ${from.y}), const Offset(${to.x}, ${to.y}), $colors, @@ -215,7 +211,7 @@ class RadialGradient extends Shader { this.transform, this.focalPoint, this.unitMode = GradientUnitMode.objectBoundingBox, - }) : super('radialGradient'); + }); /// The central point of the gradient. final Point center; @@ -271,7 +267,7 @@ class RadialGradient extends Shader { @override String toString() { return ''' -final $typeName$hashCode = Gradient.radial( +Gradient.radial( const Offset(${center.x}, ${center.y}), $radius, $colors, @@ -454,12 +450,12 @@ class Stroke { /// Creates a string with the dart:ui code to represent this stroke and any /// shaders it contains as a ui.Paint. - String toFlutterPaintString([BlendMode? blendMode]) { + String toFlutterPaintString(String shaderName, String paintName, [BlendMode? blendMode]) { final StringBuffer buffer = StringBuffer(); if (shader != null) { - buffer.writeln(shader?.toString()); + buffer.writeln('final $shaderName = $shader'); } - buffer.write('Paint()'); + buffer.write('final $paintName = Paint()'); if ((blendMode ?? BlendMode.srcOver) != BlendMode.srcOver) { buffer.write('\n ..blendMode = $blendMode'); } @@ -467,7 +463,7 @@ class Stroke { buffer.write('\n ..color = $color'); } if (shader != null) { - buffer.write('\n ..shader = shader${shader.hashCode}}'); + buffer.write('\n ..shader = $shaderName'); } if ((cap ?? StrokeCap.butt) != StrokeCap.butt) { buffer.write('\n ..strokeCap = $cap'); @@ -574,12 +570,12 @@ class Fill { /// Creates a string with the dart:ui code to represent this fill and any /// shaders it contains as a ui.Paint. - String toFlutterPaintString([BlendMode? blendMode]) { + String toFlutterPaintString(String shaderName, String paintName, [BlendMode? blendMode]) { final StringBuffer buffer = StringBuffer(); if (shader != null) { - buffer.writeln(shader?.toString()); + buffer.writeln('final $shaderName = $shader'); } - buffer.write('Paint()'); + buffer.write('final $paintName = Paint()'); if ((blendMode ?? BlendMode.srcOver) != BlendMode.srcOver) { buffer.write('\n ..blendMode = $blendMode'); } @@ -587,7 +583,7 @@ class Fill { buffer.write('\n ..color = $color'); } if (shader != null) { - buffer.write('\n ..shader = shader${shader.hashCode}}'); + buffer.write('\n ..shader = $shaderName'); } buffer.writeln(';'); return buffer.toString(); diff --git a/packages/vector_graphics_compiler/lib/src/svg/parsers.dart b/packages/vector_graphics_compiler/lib/src/svg/parsers.dart index 3bf07ad5..784ef27e 100644 --- a/packages/vector_graphics_compiler/lib/src/svg/parsers.dart +++ b/packages/vector_graphics_compiler/lib/src/svg/parsers.dart @@ -4,21 +4,6 @@ import '../geometry/matrix.dart'; import '../geometry/path.dart'; import 'numbers.dart'; -// /// Parses a `text-anchor` attribute. -// DrawableTextAnchorPosition? parseTextAnchor(String? raw) { -// switch (raw) { -// case 'inherit': -// return null; -// case 'middle': -// return DrawableTextAnchorPosition.middle; -// case 'end': -// return DrawableTextAnchorPosition.end; -// case 'start': -// default: -// return DrawableTextAnchorPosition.start; -// } -// } - const String _transformCommandAtom = ' *,?([^(]+)\\(([^)]*)\\)'; final RegExp _transformValidator = RegExp('^($_transformCommandAtom)*\$'); final RegExp _transformCommand = RegExp(_transformCommandAtom); @@ -115,8 +100,7 @@ AffineMatrix _parseSvgRotate(String? paramsStr, AffineMatrix current) { assert(params.length <= 3); final double a = radians(parseDouble(params[0])!); - final AffineMatrix rotate = - AffineMatrix(cos(a), sin(a), -sin(a), cos(a), 0.0, 0.0); + final AffineMatrix rotate = AffineMatrix.identity.rotated(a); if (params.length > 1) { final double x = parseDouble(params[1])!; diff --git a/packages/vector_graphics_compiler/lib/src/svg/theme.dart b/packages/vector_graphics_compiler/lib/src/svg/theme.dart index 660b4b3b..2e308c65 100644 --- a/packages/vector_graphics_compiler/lib/src/svg/theme.dart +++ b/packages/vector_graphics_compiler/lib/src/svg/theme.dart @@ -44,7 +44,6 @@ class SvgTheme { int get hashCode => Object.hash(currentColor, fontSize, xHeight); @override - String toString() { - return 'SvgTheme(currentColor: $currentColor, fontSize: $fontSize, xHeight: $xHeight)'; - } + String toString() => + 'SvgTheme(currentColor: $currentColor, fontSize: $fontSize, xHeight: $xHeight)'; } diff --git a/packages/vector_graphics_compiler/test/matrix_test.dart b/packages/vector_graphics_compiler/test/matrix_test.dart index 7448d05f..03dbe356 100644 --- a/packages/vector_graphics_compiler/test/matrix_test.dart +++ b/packages/vector_graphics_compiler/test/matrix_test.dart @@ -49,6 +49,13 @@ void main() { ); }); + test('Multiply handles the extra matrix4 scale value', () { + final AffineMatrix matrix1 = AffineMatrix.identity.scaled(2, 3); + final AffineMatrix matrix2 = AffineMatrix.identity.multiplied(matrix1); + + expect(matrix1, matrix2); + }); + test('Translate', () { const AffineMatrix matrix1 = AffineMatrix(2, 2, 3, 4, 5, 6); diff --git a/packages/vector_graphics_compiler/test/paint_test.dart b/packages/vector_graphics_compiler/test/paint_test.dart index a4c17fe9..539dba11 100644 --- a/packages/vector_graphics_compiler/test/paint_test.dart +++ b/packages/vector_graphics_compiler/test/paint_test.dart @@ -80,4 +80,87 @@ void main() { expect(const Paint().isEmpty, true); expect(Paint.empty.isEmpty, true); }); + + test('Paint toFlutterString', () { + const Paint paint = Paint( + blendMode: BlendMode.screen, + stroke: Stroke( + color: Color(0x87654321), + width: 2.0, + cap: StrokeCap.round, + join: StrokeJoin.bevel, + miterLimit: 10.0, + shader: LinearGradient( + from: Point(0, 0), + to: Point(10, 10), + colors: [Color.opaqueBlack, Color(0xFFABCDEF)], + tileMode: TileMode.mirror, + offsets: [0.0, 1.0], + transform: AffineMatrix.identity, + unitMode: GradientUnitMode.userSpaceOnUse, + )), + fill: Fill( + color: Color(0x12345678), + shader: RadialGradient( + center: Point(50, 50), + radius: 10, + colors: [Color(0xFFFFFFAA), Color(0xFFABCDEF)], + tileMode: TileMode.clamp, + transform: AffineMatrix.identity, + focalPoint: Point(5, 50), + offsets: [.1, .9], + ), + ), + pathFillType: PathFillType.evenOdd, + ); + + expect( + paint.fill!.toFlutterPaintString( + 'shader1', + 'fillPaint', + paint.blendMode, + ), + 'final shader1 = Gradient.radial(\n' + ' const Offset(50.0, 50.0),\n' + ' 10.0,\n' + ' [Color(0xffffffaa), Color(0xffabcdef)],\n' + ' [0.1, 0.9],\n' + ' TileMode.clamp,\n' + ' Float64List.fromList([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]),\n' + ' const Offset(5.0, 50.0),\n' + ' 0.0,\n' + ');\n' + '\n' + 'final fillPaint = Paint()\n' + ' ..blendMode = BlendMode.screen\n' + ' ..color = Color(0x12345678)\n' + ' ..shader = shader1;\n', + ); + + expect( + paint.stroke!.toFlutterPaintString( + 'shader2', + 'strokePaint', + paint.blendMode, + ), + 'final shader2 = Gradient.linear(\n' + ' const Offset(0.0, 0.0),\n' + ' const Offset(10.0, 10.0),\n' + ' [Color(0xff000000), Color(0xffabcdef)],\n' + ' [0.0, 1.0],\n' + ' TileMode.mirror,\n' + ' Float64List.fromList([1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]),\n' + ');\n' + '\n' + 'final strokePaint = Paint()\n' + ' ..blendMode = BlendMode.screen\n' + ' ..color = Color(0x87654321)\n' + ' ..shader = shader2\n' + ' ..strokeCap = StrokeCap.round\n' + ' ..strokeJoin = StrokeJoin.bevel\n' + ' ..strokeMiterLimit = 10.0\n' + ' ..strokeWidth = 2.0\n' + ' ..style = PaintingStyle.stroke;\n', + ); + }); } diff --git a/packages/vector_graphics_compiler/test/parsers_test.dart b/packages/vector_graphics_compiler/test/parsers_test.dart new file mode 100644 index 00000000..348f93b2 --- /dev/null +++ b/packages/vector_graphics_compiler/test/parsers_test.dart @@ -0,0 +1,105 @@ +import 'package:vector_graphics_compiler/src/svg/numbers.dart'; +import 'package:vector_graphics_compiler/src/svg/parsers.dart'; +import 'package:vector_graphics_compiler/vector_graphics_compiler.dart'; + +import 'package:test/test.dart'; + +void main() { + test('Multiple matrix translates', () { + final AffineMatrix expected = AffineMatrix.identity + .translated(0.338957, 0.010104) + .translated(-0.5214, 0.125) + .translated(0.987, 0.789); + expect( + parseTransform( + 'translate(0.338957,0.010104), translate(-0.5214,0.125),translate(0.987,0.789)', + null, + ), + expected, + ); + }); + + test('Translate and scale matrix', () { + final AffineMatrix expected = AffineMatrix.identity + .translated(0.338957, 0.010104) + .scaled(0.869768, 1.000000); + expect( + parseTransform( + 'translate(0.338957,0.010104),scale(0.869768,1.000000)', + null, + ), + expected, + ); + }); + + test('SVG Transform parser tests', () { + expect(() => parseTransform('invalid', null), throwsStateError); + expect(() => parseTransform('transformunsupported(0,0)', null), + throwsStateError); + + expect( + parseTransform('skewX(60)', null), + AffineMatrix.identity.xSkewed(60.0), + ); + expect( + parseTransform('skewY(60)', null), + AffineMatrix.identity.ySkewed(60.0), + ); + expect( + parseTransform('translate(10,0.0)', null), + AffineMatrix.identity.translated(10.0, 0.0), + ); + + expect( + parseTransform('scale(10)', null), + AffineMatrix.identity.scaled(10.0, 10.0), + ); + expect( + parseTransform('scale(10, 15)', null), + AffineMatrix.identity.scaled(10.0, 15.0), + ); + + expect( + parseTransform('rotate(20)', null), + AffineMatrix.identity.rotated(radians(20.0)), + ); + expect( + parseTransform('rotate(20, 30)', null), + AffineMatrix.identity + .translated(30.0, 30.0) + .rotated(radians(20.0)) + .translated(-30.0, -30.0), + ); + expect( + parseTransform('rotate(20, 30, 40)', null), + AffineMatrix.identity + .translated(30.0, 40.0) + .rotated(radians(20.0)) + .translated(-30.0, -40.0), + ); + + expect( + parseTransform('matrix(1.5, 2.0, 3.0, 4.0, 5.0, 6.0)', null), + const AffineMatrix(1.5, 2.0, 3.0, 4.0, 5.0, 6.0), + ); + + expect( + parseTransform('matrix(1.5, 2.0, 3.0, 4.0, 5.0, 6.0 )', null), + const AffineMatrix(1.5, 2.0, 3.0, 4.0, 5.0, 6.0), + ); + + expect( + parseTransform('rotate(20)\n\tscale(10)', null), + AffineMatrix.identity.rotated(radians(20.0)).scaled(10.0, 10.0), + ); + }); + + test('FillRule tests', () { + expect(parseRawFillRule(''), PathFillType.nonZero); + expect(parseRawFillRule(null), isNull); + expect(parseRawFillRule('inherit'), isNull); + expect(parseRawFillRule('nonzero'), PathFillType.nonZero); + expect(parseRawFillRule('evenodd'), PathFillType.evenOdd); + expect(parseRawFillRule('invalid'), PathFillType.nonZero); + }); +} diff --git a/packages/vector_graphics_compiler/test/theme_test.dart b/packages/vector_graphics_compiler/test/theme_test.dart new file mode 100644 index 00000000..715962f0 --- /dev/null +++ b/packages/vector_graphics_compiler/test/theme_test.dart @@ -0,0 +1,85 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:vector_graphics_compiler/src/paint.dart'; +import 'package:vector_graphics_compiler/src/svg/theme.dart'; + +import 'package:test/test.dart'; + +void main() { + group('SvgTheme', () { + group('constructor', () { + test('sets currentColor', () { + const Color currentColor = Color(0xFFB0E3BE); + + expect( + SvgTheme( + currentColor: currentColor, + ).currentColor, + equals(currentColor), + ); + }); + + test('sets fontSize', () { + const double fontSize = 14.0; + + expect( + SvgTheme( + currentColor: Color(0xFFB0E3BE), + ).fontSize, + equals(fontSize), + ); + }); + + test( + 'sets fontSize to 14 ' + 'by default', () { + expect( + SvgTheme(), + equals( + SvgTheme(), + ), + ); + }); + + test('sets xHeight', () { + const double xHeight = 8.0; + + expect( + SvgTheme( + fontSize: 26.0, + xHeight: xHeight, + ).xHeight, + equals(xHeight), + ); + }); + + test( + 'sets xHeight as fontSize divided by 2 ' + 'by default', () { + const double fontSize = 16.0; + + expect( + SvgTheme( + fontSize: fontSize, + ).xHeight, + equals(fontSize / 2), + ); + }); + }); + + test('supports value equality', () { + expect( + SvgTheme( + currentColor: Color(0xFF6F2173), + xHeight: 6.0, + ), + equals( + SvgTheme( + currentColor: Color(0xFF6F2173), + xHeight: 6.0, + ), + ), + ); + }); + }); +} From 727b6fba63c459aeb5d6e2cc5f2b6acf110b078e Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 7 Mar 2022 15:09:26 -0800 Subject: [PATCH 2/2] format --- packages/vector_graphics_compiler/lib/src/paint.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/vector_graphics_compiler/lib/src/paint.dart b/packages/vector_graphics_compiler/lib/src/paint.dart index ac907321..da26bc7a 100644 --- a/packages/vector_graphics_compiler/lib/src/paint.dart +++ b/packages/vector_graphics_compiler/lib/src/paint.dart @@ -450,7 +450,8 @@ class Stroke { /// Creates a string with the dart:ui code to represent this stroke and any /// shaders it contains as a ui.Paint. - String toFlutterPaintString(String shaderName, String paintName, [BlendMode? blendMode]) { + String toFlutterPaintString(String shaderName, String paintName, + [BlendMode? blendMode]) { final StringBuffer buffer = StringBuffer(); if (shader != null) { buffer.writeln('final $shaderName = $shader'); @@ -570,7 +571,8 @@ class Fill { /// Creates a string with the dart:ui code to represent this fill and any /// shaders it contains as a ui.Paint. - String toFlutterPaintString(String shaderName, String paintName, [BlendMode? blendMode]) { + String toFlutterPaintString(String shaderName, String paintName, + [BlendMode? blendMode]) { final StringBuffer buffer = StringBuffer(); if (shader != null) { buffer.writeln('final $shaderName = $shader');