From 48ab30f71ee045e29b0dc66278957ae22fad63e8 Mon Sep 17 00:00:00 2001 From: lukas-heiligenbrunner Date: Sun, 26 Jun 2022 19:53:43 +0200 Subject: [PATCH 1/2] add basic support for line-dasharray styling --- lib/src/features/fill_renderer.dart | 2 +- lib/src/features/line_renderer.dart | 6 +++- lib/src/features/symbol_line_renderer.dart | 2 +- lib/src/model/path_utils.dart | 25 ++++++++++++++++ lib/src/model/ring_number_provider.dart | 13 +++++++++ lib/src/model/tile_model.dart | 11 +++++-- lib/src/themes/paint_factory.dart | 34 +++++++++++++++++----- lib/src/themes/style.dart | 9 +++--- lib/src/tileset.dart | 2 +- 9 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 lib/src/model/path_utils.dart create mode 100644 lib/src/model/ring_number_provider.dart diff --git a/lib/src/features/fill_renderer.dart b/lib/src/features/fill_renderer.dart index db55902..518f5e2 100644 --- a/lib/src/features/fill_renderer.dart +++ b/lib/src/features/fill_renderer.dart @@ -31,7 +31,7 @@ class FillRenderer extends FeatureRenderer { final fillPaint = style.fillPaint?.evaluate(evaluationContext); final outlinePaint = style.outlinePaint?.evaluate(evaluationContext); - final polygons = feature.paths; + final polygons = feature.getPaths(); for (final polygon in polygons) { if (!context.optimizations.skipInBoundsChecks && diff --git a/lib/src/features/line_renderer.dart b/lib/src/features/line_renderer.dart index 3d0c81c..b38c441 100644 --- a/lib/src/features/line_renderer.dart +++ b/lib/src/features/line_renderer.dart @@ -47,7 +47,11 @@ class LineRenderer extends FeatureRenderer { effectivePaint.strokeWidth = context.tileSpaceMapper.widthFromPixelToTile(strokeWidth); - final lines = feature.paths; + final dashLengths = effectivePaint.strokeDashPattern + .map((e) => context.tileSpaceMapper.widthFromPixelToTile(e)) + .toList(growable: false); + + final lines = feature.getPaths(dashLengths: dashLengths); for (final line in lines) { if (!context.optimizations.skipInBoundsChecks && diff --git a/lib/src/features/symbol_line_renderer.dart b/lib/src/features/symbol_line_renderer.dart index f9a4fab..7d1f11a 100644 --- a/lib/src/features/symbol_line_renderer.dart +++ b/lib/src/features/symbol_line_renderer.dart @@ -29,7 +29,7 @@ class SymbolLineRenderer extends FeatureRenderer { return; } - final lines = feature.paths; + final lines = feature.getPaths(); if (lines.isEmpty) { return; } diff --git a/lib/src/model/path_utils.dart b/lib/src/model/path_utils.dart new file mode 100644 index 0000000..1e2cfbc --- /dev/null +++ b/lib/src/model/path_utils.dart @@ -0,0 +1,25 @@ +import 'dart:ui'; + +import 'ring_number_provider.dart'; + +extension DashPath on Path { + // convert a path into a dashed path with given intervals + Path dashPath(RingNumberProvider dashArray) { + final Path dest = Path(); + for (final PathMetric metric in this.computeMetrics()) { + double distance = .0; + bool draw = true; + while (distance < metric.length) { + final double len = dashArray.next; + if (draw) { + dest.addPath( + metric.extractPath(distance, distance + len), Offset.zero); + } + distance += len; + draw = !draw; + } + } + + return dest; + } +} diff --git a/lib/src/model/ring_number_provider.dart b/lib/src/model/ring_number_provider.dart new file mode 100644 index 0000000..4ce0f10 --- /dev/null +++ b/lib/src/model/ring_number_provider.dart @@ -0,0 +1,13 @@ +class RingNumberProvider { + RingNumberProvider(this._vals); + + final List _vals; + int _idx = 0; + + double get next { + if (_idx >= _vals.length) { + _idx = 0; + } + return _vals[_idx++]; + } +} diff --git a/lib/src/model/tile_model.dart b/lib/src/model/tile_model.dart index e1a3c25..fb53185 100644 --- a/lib/src/model/tile_model.dart +++ b/lib/src/model/tile_model.dart @@ -1,5 +1,7 @@ import 'dart:ui'; +import 'ring_number_provider.dart'; +import 'path_utils.dart'; import 'geometry_model.dart'; import 'geometry_model_ui.dart'; @@ -56,7 +58,7 @@ class TileFeature { bool get hasPoints => type == TileFeatureType.point; - List get paths { + List getPaths({List dashLengths = const []}) { if (type == TileFeatureType.point) { throw StateError('Cannot get paths from a point feature'); } @@ -78,7 +80,12 @@ class TileFeature { .toList(growable: false); _modelPolygons = null; } - return _paths; + + if (dashLengths.length >= 2) { + return _paths.map((e) => e.dashPath(RingNumberProvider(dashLengths))).toList(); + } else { + return _paths; + } } } diff --git a/lib/src/themes/paint_factory.dart b/lib/src/themes/paint_factory.dart index 8930804..5885be3 100644 --- a/lib/src/themes/paint_factory.dart +++ b/lib/src/themes/paint_factory.dart @@ -6,14 +6,14 @@ import 'expression/expression.dart'; import 'expression/literal_expression.dart'; import 'expression/numeric_expression.dart'; -class PaintExpression extends Expression { +class PaintExpression extends Expression { final PaintStyle _delegate; PaintExpression(this._delegate) : super(_cacheKey(_delegate), _properties(_delegate)); @override - Paint? evaluate(EvaluationContext context) => _delegate.paint(context); + PaintModel? evaluate(EvaluationContext context) => _delegate.paint(context); @override bool get isConstant => false; @@ -28,21 +28,27 @@ class PaintExpression extends Expression { }; } +class PaintModel extends Paint { + List strokeDashPattern = []; +} + class PaintStyle { final String id; final PaintingStyle paintingStyle; final Expression opacity; final Expression strokeWidth; final Expression color; + final List strokeDashPattern; PaintStyle( {required this.id, required this.paintingStyle, required this.opacity, required this.strokeWidth, - required this.color}); + required this.color, + required this.strokeDashPattern}); - Paint? paint(EvaluationContext context) { + PaintModel? paint(EvaluationContext context) { final opacity = this.opacity.evaluate(context); if (opacity != null && opacity <= 0) { return null; @@ -51,7 +57,9 @@ class PaintStyle { if (color == null) { return null; } - final paint = Paint()..style = paintingStyle; + final paint = PaintModel() + ..style = paintingStyle + ..strokeDashPattern = strokeDashPattern; if (opacity != null && opacity < 1.0) { paint.color = color.withOpacity(opacity); } else { @@ -73,7 +81,7 @@ class PaintFactory { final ExpressionParser expressionParser; PaintFactory(this.logger) : expressionParser = ExpressionParser(logger); - Expression? create( + Expression? create( String id, PaintingStyle style, String prefix, paint, {double? defaultStrokeWidth = 1.0}) { if (paint == null) { @@ -87,11 +95,23 @@ class PaintFactory { whenNull: () => LiteralExpression(1.0)); final strokeWidth = expressionParser.parse(paint['$prefix-width'], whenNull: () => LiteralExpression(defaultStrokeWidth)); + + List dashArray = []; + final dashJson = paint['$prefix-dasharray']; + if (dashJson != null && dashJson is List && dashJson.length >= 2) { + if (dashJson.any((element) => element < .0)) { + logger.warn(() => '$prefix-dasharray contains value < 0'); + } else { + dashArray = dashJson.map((e) => e.toDouble()).toList(growable: false); + } + } + return PaintExpression(PaintStyle( id: id, paintingStyle: style, opacity: opacity.asDoubleExpression(), strokeWidth: strokeWidth.asDoubleExpression(), - color: color.asColorExpression())); + color: color.asColorExpression(), + strokeDashPattern: dashArray)); } } diff --git a/lib/src/themes/style.dart b/lib/src/themes/style.dart index ed8b270..fa824d6 100644 --- a/lib/src/themes/style.dart +++ b/lib/src/themes/style.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'dart:ui'; import 'package:flutter/widgets.dart'; +import 'package:vector_tile_renderer/src/themes/paint_factory.dart'; import 'expression/expression.dart'; import '../extensions.dart'; @@ -12,14 +13,14 @@ typedef ColorZoomFunction = Color? Function(double zoom); typedef TextTransformFunction = String? Function(String? text); class Style { - final Expression? fillPaint; + final Expression? fillPaint; final Extrusion? fillExtrusion; - final Expression? linePaint; + final Expression? linePaint; final LineLayout? lineLayout; - final Expression? textPaint; + final Expression? textPaint; final TextLayout? textLayout; final Expression>? textHalo; - final Expression? outlinePaint; + final Expression? outlinePaint; Style( {this.fillPaint, diff --git a/lib/src/tileset.dart b/lib/src/tileset.dart index 043009f..b95f4ee 100644 --- a/lib/src/tileset.dart +++ b/lib/src/tileset.dart @@ -59,7 +59,7 @@ class TilesetPreprocessor { if (_initializeGeometry) { for (final feature in features) { if (feature.feature.hasPaths) { - feature.feature.paths; + feature.feature.getPaths(); } else if (feature.feature.hasPoints) { feature.feature.points; } From e2b08f4cfd7e25e46eb98e1412dcdac8fcca46ad Mon Sep 17 00:00:00 2001 From: David Green Date: Thu, 14 Jul 2022 17:35:27 -0700 Subject: [PATCH 2/2] restructure dashed line functionality --- lib/src/features/fill_renderer.dart | 2 +- lib/src/features/line_renderer.dart | 12 ++++++++---- lib/src/features/symbol_line_renderer.dart | 2 +- lib/src/model/tile_model.dart | 11 ++--------- .../path_utils.dart => path/path_transform.dart} | 4 ++-- lib/src/{model => path}/ring_number_provider.dart | 0 lib/src/themes/paint_factory.dart | 6 +++--- lib/src/tileset.dart | 2 +- 8 files changed, 18 insertions(+), 21 deletions(-) rename lib/src/{model/path_utils.dart => path/path_transform.dart} (80%) rename lib/src/{model => path}/ring_number_provider.dart (100%) diff --git a/lib/src/features/fill_renderer.dart b/lib/src/features/fill_renderer.dart index 518f5e2..db55902 100644 --- a/lib/src/features/fill_renderer.dart +++ b/lib/src/features/fill_renderer.dart @@ -31,7 +31,7 @@ class FillRenderer extends FeatureRenderer { final fillPaint = style.fillPaint?.evaluate(evaluationContext); final outlinePaint = style.outlinePaint?.evaluate(evaluationContext); - final polygons = feature.getPaths(); + final polygons = feature.paths; for (final polygon in polygons) { if (!context.optimizations.skipInBoundsChecks && diff --git a/lib/src/features/line_renderer.dart b/lib/src/features/line_renderer.dart index b38c441..8cf20d7 100644 --- a/lib/src/features/line_renderer.dart +++ b/lib/src/features/line_renderer.dart @@ -1,5 +1,7 @@ import '../../vector_tile_renderer.dart'; import '../context.dart'; +import '../path/path_transform.dart'; +import '../path/ring_number_provider.dart'; import '../themes/expression/expression.dart'; import '../themes/style.dart'; import 'feature_renderer.dart'; @@ -48,16 +50,18 @@ class LineRenderer extends FeatureRenderer { context.tileSpaceMapper.widthFromPixelToTile(strokeWidth); final dashLengths = effectivePaint.strokeDashPattern - .map((e) => context.tileSpaceMapper.widthFromPixelToTile(e)) + ?.map((e) => context.tileSpaceMapper.widthFromPixelToTile(e)) .toList(growable: false); - final lines = feature.getPaths(dashLengths: dashLengths); - - for (final line in lines) { + final lines = feature.paths; + for (var line in lines) { if (!context.optimizations.skipInBoundsChecks && !context.tileSpaceMapper.isPathWithinTileClip(line)) { continue; } + if (dashLengths != null) { + line = line.dashPath(RingNumberProvider(dashLengths)); + } context.canvas.drawPath(line, effectivePaint); } } diff --git a/lib/src/features/symbol_line_renderer.dart b/lib/src/features/symbol_line_renderer.dart index 7d1f11a..f9a4fab 100644 --- a/lib/src/features/symbol_line_renderer.dart +++ b/lib/src/features/symbol_line_renderer.dart @@ -29,7 +29,7 @@ class SymbolLineRenderer extends FeatureRenderer { return; } - final lines = feature.getPaths(); + final lines = feature.paths; if (lines.isEmpty) { return; } diff --git a/lib/src/model/tile_model.dart b/lib/src/model/tile_model.dart index fb53185..e1a3c25 100644 --- a/lib/src/model/tile_model.dart +++ b/lib/src/model/tile_model.dart @@ -1,7 +1,5 @@ import 'dart:ui'; -import 'ring_number_provider.dart'; -import 'path_utils.dart'; import 'geometry_model.dart'; import 'geometry_model_ui.dart'; @@ -58,7 +56,7 @@ class TileFeature { bool get hasPoints => type == TileFeatureType.point; - List getPaths({List dashLengths = const []}) { + List get paths { if (type == TileFeatureType.point) { throw StateError('Cannot get paths from a point feature'); } @@ -80,12 +78,7 @@ class TileFeature { .toList(growable: false); _modelPolygons = null; } - - if (dashLengths.length >= 2) { - return _paths.map((e) => e.dashPath(RingNumberProvider(dashLengths))).toList(); - } else { - return _paths; - } + return _paths; } } diff --git a/lib/src/model/path_utils.dart b/lib/src/path/path_transform.dart similarity index 80% rename from lib/src/model/path_utils.dart rename to lib/src/path/path_transform.dart index 1e2cfbc..b50adbe 100644 --- a/lib/src/model/path_utils.dart +++ b/lib/src/path/path_transform.dart @@ -3,10 +3,10 @@ import 'dart:ui'; import 'ring_number_provider.dart'; extension DashPath on Path { - // convert a path into a dashed path with given intervals + // creates a new dashed path with the given intervals Path dashPath(RingNumberProvider dashArray) { final Path dest = Path(); - for (final PathMetric metric in this.computeMetrics()) { + for (final PathMetric metric in computeMetrics()) { double distance = .0; bool draw = true; while (distance < metric.length) { diff --git a/lib/src/model/ring_number_provider.dart b/lib/src/path/ring_number_provider.dart similarity index 100% rename from lib/src/model/ring_number_provider.dart rename to lib/src/path/ring_number_provider.dart diff --git a/lib/src/themes/paint_factory.dart b/lib/src/themes/paint_factory.dart index 5885be3..ba0ef6b 100644 --- a/lib/src/themes/paint_factory.dart +++ b/lib/src/themes/paint_factory.dart @@ -29,7 +29,7 @@ class PaintExpression extends Expression { } class PaintModel extends Paint { - List strokeDashPattern = []; + List? strokeDashPattern; } class PaintStyle { @@ -38,7 +38,7 @@ class PaintStyle { final Expression opacity; final Expression strokeWidth; final Expression color; - final List strokeDashPattern; + final List? strokeDashPattern; PaintStyle( {required this.id, @@ -96,7 +96,7 @@ class PaintFactory { final strokeWidth = expressionParser.parse(paint['$prefix-width'], whenNull: () => LiteralExpression(defaultStrokeWidth)); - List dashArray = []; + List? dashArray; final dashJson = paint['$prefix-dasharray']; if (dashJson != null && dashJson is List && dashJson.length >= 2) { if (dashJson.any((element) => element < .0)) { diff --git a/lib/src/tileset.dart b/lib/src/tileset.dart index b95f4ee..043009f 100644 --- a/lib/src/tileset.dart +++ b/lib/src/tileset.dart @@ -59,7 +59,7 @@ class TilesetPreprocessor { if (_initializeGeometry) { for (final feature in features) { if (feature.feature.hasPaths) { - feature.feature.getPaths(); + feature.feature.paths; } else if (feature.feature.hasPoints) { feature.feature.points; }