Skip to content

Commit 5422149

Browse files
authored
Merge pull request #6 from dnfield/ast
Parsing, a few test cases
2 parents 9b6f415 + 2ec3623 commit 5422149

24 files changed

+10212
-165
lines changed
Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,33 @@
11
include: package:flutter_lints/flutter.yaml
22

3-
# Additional information about this file can be found at
4-
# https://dart.dev/guides/language/analysis-options
3+
analyzer:
4+
language:
5+
strict-raw-types: true
6+
strong-mode:
7+
implicit-casts: false
8+
errors:
9+
missing_required_param: error
10+
todo: ignore
11+
12+
linter:
13+
14+
rules:
15+
always_declare_return_types: true
16+
always_put_control_body_on_new_line: true
17+
always_specify_types: true
18+
avoid_dynamic_calls: true
19+
avoid_redundant_argument_values: true
20+
avoid_print: false # For now, the compiler is printing.
21+
prefer_single_quotes: true
22+
# flutter_style_todos: true TODO(dnfield): enable this eventually
23+
hash_and_equals: true
24+
no_default_cases: true
25+
only_throw_errors: true
26+
prefer_final_fields: true
27+
prefer_final_locals: true
28+
public_member_api_docs: true
29+
recursive_getters: true
30+
sort_constructors_first: true
31+
sort_unnamed_constructors_first: true
32+
test_types_in_equals: true
33+
unnecessary_late: true

packages/vector_graphics_compiler/lib/src/geometry/basic_types.dart

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class Point {
99
/// Creates a point object with x,y coordinates.
1010
const Point(this.x, this.y);
1111

12+
/// The point at the origin of coordinate space.
1213
static const Point zero = Point(0, 0);
1314

1415
/// The offset along the x-axis of this point.
@@ -25,10 +26,16 @@ class Point {
2526
return other is Point && other.x == x && other.y == y;
2627
}
2728

29+
/// Returns a point whose coordinates are the coordinates of the
30+
/// left-hand-side operand (a Point) divided by the scalar right-hand-side
31+
/// operand (a double).
2832
Point operator /(double divisor) {
2933
return Point(x / divisor, y / divisor);
3034
}
3135

36+
/// Returns a point whose coordinates are the coordinates of the
37+
/// left-hand-side operand (a Point) multiplied by the scalar right-hand-side
38+
/// operand (a double).
3239
Point operator *(double multiplicand) {
3340
return Point(x * multiplicand, y * multiplicand);
3441
}
@@ -80,21 +87,6 @@ class Rect {
8087
/// The height of the rectangle.
8188
double get height => bottom - top;
8289

83-
/// The top left corner of the rect.
84-
Point get topLeft => Point(left, top);
85-
86-
/// The top right corner of the rect.
87-
Point get topRight => Point(right, top);
88-
89-
/// The bottom left corner of the rect.
90-
Point get bottomLeft => Point(bottom, left);
91-
92-
/// The bottom right corner of the rect.
93-
Point get bottomRight => Point(bottom, right);
94-
95-
/// The size of the rectangle, expressed as a [Point].
96-
Point get size => Point(width, height);
97-
9890
/// Creates the smallest rectangle that covers the edges of this and `other`.
9991
Rect expanded(Rect other) {
10092
return Rect.fromLTRB(
@@ -105,9 +97,6 @@ class Rect {
10597
);
10698
}
10799

108-
@override
109-
String toString() => 'Rect.fromLTRB($left, $top, $right, $bottom)';
110-
111100
@override
112101
int get hashCode => Object.hash(left, top, right, bottom);
113102

@@ -119,4 +108,7 @@ class Rect {
119108
other.right == right &&
120109
other.bottom == bottom;
121110
}
111+
112+
@override
113+
String toString() => 'Rect.fromLTRB($left, $top, $right, $bottom)';
122114
}

packages/vector_graphics_compiler/lib/src/geometry/path.dart

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,14 @@ abstract class PathCommand {
6868

6969
/// Returns a new path command transformed by `matrix`.
7070
PathCommand transformed(AffineMatrix matrix);
71+
72+
/// A representation of this path command for dart:ui.
73+
String toFlutterString();
7174
}
7275

76+
/// A straight line from the current point to x,y.
7377
class LineToCommand extends PathCommand {
78+
/// Creates a straight line command from the current point to x,y.
7479
const LineToCommand(this.x, this.y) : super._(PathCommandType.line);
7580

7681
/// The absolute offset of the destination point for this path from the x
@@ -96,12 +101,15 @@ class LineToCommand extends PathCommand {
96101
}
97102

98103
@override
99-
String toString() {
100-
return '..lineTo($x, $y)';
101-
}
104+
String toFlutterString() => '..lineTo($x, $y)';
105+
106+
@override
107+
String toString() => 'LineToCommand($x, $y)';
102108
}
103109

110+
/// Moves the current point to x,y as if picking up the pen.
104111
class MoveToCommand extends PathCommand {
112+
/// Creates a new command that moves the current point to x,y without drawing.
105113
const MoveToCommand(this.x, this.y) : super._(PathCommandType.move);
106114

107115
/// The absolute offset of the destination point for this path from the x
@@ -127,12 +135,17 @@ class MoveToCommand extends PathCommand {
127135
}
128136

129137
@override
130-
String toString() {
131-
return '..moveTo($x, $y)';
132-
}
138+
String toFlutterString() => '..moveTo($x, $y)';
139+
140+
@override
141+
String toString() => 'MoveToCommand($x, $y)';
133142
}
134143

144+
/// A command describing a cubic Bezier command from the current point to
145+
/// x3,y3 using control points x1,y1 and x2,y2.
135146
class CubicToCommand extends PathCommand {
147+
/// Creates a new cubic Bezier command from the current point to x3,y3 using
148+
/// control points x1,y1 and x2,y2.
136149
const CubicToCommand(this.x1, this.y1, this.x2, this.y2, this.x3, this.y3)
137150
: super._(PathCommandType.cubic);
138151

@@ -183,12 +196,16 @@ class CubicToCommand extends PathCommand {
183196
}
184197

185198
@override
186-
String toString() {
187-
return '..cubicTo($x1, $y1, $x2, $y2, $x3, $y3)';
188-
}
199+
String toFlutterString() => '..cubicTo($x1, $y1, $x2, $y2, $x3, $y3)';
200+
201+
@override
202+
String toString() => 'CubicToCommand($x1, $y1, $x2, $y2, $x3, $y3)';
189203
}
190204

205+
/// A straight line from the current point to the current contour start point.
191206
class CloseCommand extends PathCommand {
207+
/// Creates a new straight line from the current point to the current contour
208+
/// start point.
192209
const CloseCommand() : super._(PathCommandType.close);
193210

194211
@override
@@ -205,9 +222,9 @@ class CloseCommand extends PathCommand {
205222
}
206223

207224
@override
208-
String toString() {
209-
return '..close()';
210-
}
225+
String toFlutterString() => '..close()';
226+
@override
227+
String toString() => 'CloseCommand()';
211228
}
212229

213230
/// Creates a new builder of [Path] objects.
@@ -311,7 +328,7 @@ class PathBuilder implements PathProxy {
311328
return;
312329
}
313330

314-
final magicRadius = Point(rx, ry) * _kArcApproximationMagic;
331+
final Point magicRadius = Point(rx, ry) * _kArcApproximationMagic;
315332

316333
moveTo(rect.left + rx, rect.top);
317334

@@ -379,13 +396,9 @@ class PathBuilder implements PathProxy {
379396
/// path objects with the same commands. By default, the builder will reset
380397
/// to an initial state.
381398
Path toPath({bool reset = true}) {
382-
// TODO: bounds
383-
Rect bounds = Rect.zero;
384-
385399
final Path path = Path(
386400
commands: _commands,
387401
fillType: fillType,
388-
bounds: bounds,
389402
);
390403

391404
if (reset) {
@@ -402,7 +415,6 @@ class Path {
402415
Path({
403416
List<PathCommand> commands = const <PathCommand>[],
404417
this.fillType = PathFillType.nonZero,
405-
required this.bounds,
406418
}) {
407419
_commands.addAll(commands);
408420
}
@@ -418,9 +430,7 @@ class Path {
418430
/// The fill type of this path, defaulting to [PathFillType.nonZero].
419431
final PathFillType fillType;
420432

421-
/// The bounds of this path object.
422-
final Rect bounds;
423-
433+
/// Creates a new path whose commands and points are transformed by `matrix`.
424434
Path transformed(AffineMatrix matrix) {
425435
final List<PathCommand> commands = <PathCommand>[];
426436
for (final PathCommand command in _commands) {
@@ -429,9 +439,6 @@ class Path {
429439
return Path(
430440
commands: commands,
431441
fillType: fillType,
432-
// TODO: is this safe? What the commands have degenerated? Should probably
433-
// recalculate this.
434-
bounds: matrix.transformRect(bounds),
435442
);
436443
}
437444

@@ -442,28 +449,40 @@ class Path {
442449
bool operator ==(Object other) {
443450
return other is Path &&
444451
listEquals(_commands, other._commands) &&
445-
other.fillType == fillType &&
446-
other.bounds == bounds;
452+
other.fillType == fillType;
447453
}
448454

449-
@override
450-
String toString() {
455+
/// Returns a string that prints the dart:ui code to create this path.
456+
String toFlutterString() {
451457
final StringBuffer buffer = StringBuffer('Path()');
452458
if (fillType != PathFillType.nonZero) {
453459
buffer.write('\n ..fillType = $fillType');
454460
}
455-
for (final command in commands) {
456-
buffer.write('\n $command');
461+
for (final PathCommand command in commands) {
462+
buffer.write('\n ${command.toFlutterString()}');
457463
}
458464
buffer.write(';');
459465
return buffer.toString();
460466
}
467+
468+
@override
469+
String toString() {
470+
final StringBuffer buffer = StringBuffer('Path(');
471+
if (commands.isNotEmpty) {
472+
buffer.write('\n commands: <PathCommand>$commands,');
473+
}
474+
if (fillType != PathFillType.nonZero) {
475+
buffer.write('\n fillType: $fillType,');
476+
}
477+
buffer.write('\n)');
478+
return buffer.toString();
479+
}
461480
}
462481

463482
/// Creates a new [Path] object from an SVG path data string.
464483
Path parseSvgPathData(String svg) {
465484
if (svg == '') {
466-
return Path(bounds: Rect.zero);
485+
return Path();
467486
}
468487

469488
final SvgPathStringSource parser = SvgPathStringSource(svg);

packages/vector_graphics_compiler/lib/src/geometry/vertices.dart

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ import 'basic_types.dart';
44

55
/// A description of vertex points for drawing triangles.
66
class Vertices {
7+
/// Creates a new collection of triangle vertices at the specified points.
78
const Vertices(this.vertexPoints);
89

10+
/// Creates a new collection of triangle vertices from the specified
11+
/// [Float32List], interpreted as x,y pairs.
912
factory Vertices.fromFloat32List(Float32List vertices) {
1013
if (vertices.length.isOdd) {
1114
throw ArgumentError(
1215
'must be an even number of vertex points',
1316
'vertices',
1417
);
1518
}
16-
final List<Point> vertexPoints = [];
19+
final List<Point> vertexPoints = <Point>[];
1720
for (int index = 0; index < vertices.length; index += 2) {
1821
vertexPoints.add(Point(vertices[index], vertices[index + 1]));
1922
}
@@ -28,17 +31,17 @@ class Vertices {
2831
/// Creates an optimized version of [vertexPoints] where the points are
2932
/// deduplicated via an index buffer.
3033
IndexedVertices createIndex() {
31-
final pointMap = <Point, int>{};
34+
final Map<Point, int> pointMap = <Point, int>{};
3235
int index = 0;
33-
final List<int> indices = [];
34-
for (final point in vertexPoints) {
36+
final List<int> indices = <int>[];
37+
for (final Point point in vertexPoints) {
3538
indices.add(pointMap.putIfAbsent(point, () => index++));
3639
}
3740

3841
Float32List pointsToFloat32List(List<Point> points) {
3942
final Float32List vertices = Float32List(points.length * 2);
4043
int vertexIndex = 0;
41-
for (final point in points) {
44+
for (final Point point in points) {
4245
vertices[vertexIndex++] = point.x;
4346
vertices[vertexIndex++] = point.y;
4447
}
@@ -61,6 +64,9 @@ class Vertices {
6164
/// An optimized version of [Vertices] that uses an index buffer to specify
6265
/// reused vertex points.
6366
class IndexedVertices {
67+
/// Creates a indexed set of vertices.
68+
///
69+
/// Consider using [Vertices.createIndex].
6470
const IndexedVertices(this.vertices, this.indices);
6571

6672
/// The raw vertex points.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'paint.dart';
2+
import 'vector_instructions.dart';
3+
4+
/// An optimization pass for a [VectorInstructions] object.
5+
///
6+
/// For example, an optimizer may de-duplicate objects or transform objects
7+
/// into more efficiently drawable objects.
8+
///
9+
/// Optimizers are composable, but may expect certain ordering to reach maximum
10+
/// efficiency.
11+
abstract class Optimizer {
12+
/// Allows inheriting classes to create const instances.
13+
const Optimizer();
14+
15+
/// Takes `original` and produces a new object that is optimized.
16+
VectorInstructions optimize(VectorInstructions original);
17+
}
18+
19+
/// An optimizer that removes duplicate [Paint] objects and rewrites
20+
/// [DrawCommand]s to refer to the updated paint index.
21+
///
22+
/// The resulting [VectorInstructions.paints] is effectively the original paint
23+
/// list converted to a set and then back to a list.
24+
class PaintDeduplicator extends Optimizer {
25+
/// Creates a new paint deduplicator.
26+
const PaintDeduplicator();
27+
28+
@override
29+
VectorInstructions optimize(VectorInstructions original) {
30+
final VectorInstructions result = VectorInstructions(
31+
width: original.width,
32+
height: original.height,
33+
paths: original.paths,
34+
paints: <Paint>[],
35+
commands: <DrawCommand>[],
36+
);
37+
38+
final Map<Paint, int> paints = <Paint, int>{};
39+
for (final DrawCommand command in original.commands) {
40+
final Paint originalPaint = original.paints[command.paintId];
41+
final int paintId = paints.putIfAbsent(
42+
original.paints[command.paintId],
43+
() {
44+
result.paints.add(originalPaint);
45+
return result.paints.length - 1;
46+
},
47+
);
48+
result.commands.add(DrawCommand(
49+
command.objectId,
50+
paintId,
51+
command.type,
52+
command.debugString,
53+
));
54+
}
55+
return result;
56+
}
57+
}

0 commit comments

Comments
 (0)