From 3924c1e11543bea7ad123ac04b38971a9148a5b2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 25 Jan 2024 00:27:08 +0100 Subject: [PATCH 1/5] Respect device pixel ratio when (un)transforming pixels into projected space. --- lib/src/layer/polygon_layer/polygon_layer.dart | 7 +++++-- lib/src/layer/polyline_layer/polyline_layer.dart | 7 +++++-- lib/src/misc/simplify.dart | 10 ++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 0bc3bff8b..867ed8840 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -95,9 +95,10 @@ class _PolygonLayerState extends State { ? projected : _cachedSimplifiedPolygons[camera.zoom.floor()] ??= _computeZoomLevelSimplification( + camera: camera, polygons: projected, pixelTolerance: widget.simplificationTolerance, - camera: camera, + devicePixelRatio: MediaQuery.of(context).devicePixelRatio, ); final culled = !widget.polygonCulling @@ -122,14 +123,16 @@ class _PolygonLayerState extends State { } static List<_ProjectedPolygon> _computeZoomLevelSimplification({ + required MapCamera camera, required List<_ProjectedPolygon> polygons, required double pixelTolerance, - required MapCamera camera, + required double devicePixelRatio, }) { final tolerance = getEffectiveSimplificationTolerance( crs: camera.crs, zoom: camera.zoom.floor(), pixelTolerance: pixelTolerance, + devicePixelRatio: devicePixelRatio, ); return List<_ProjectedPolygon>.generate( diff --git a/lib/src/layer/polyline_layer/polyline_layer.dart b/lib/src/layer/polyline_layer/polyline_layer.dart index cc89a2baf..0858cc396 100644 --- a/lib/src/layer/polyline_layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer/polyline_layer.dart @@ -109,9 +109,10 @@ class _PolylineLayerState extends State> { ? projected : _cachedSimplifiedPolylines[camera.zoom.floor()] ??= _computeZoomLevelSimplification( + camera: camera, polylines: projected, pixelTolerance: widget.simplificationTolerance, - camera: camera, + devicePixelRatio: MediaQuery.of(context).devicePixelRatio, ); final culled = widget.cullingMargin == null @@ -221,14 +222,16 @@ class _PolylineLayerState extends State> { } static List<_ProjectedPolyline> _computeZoomLevelSimplification({ + required MapCamera camera, required List<_ProjectedPolyline> polylines, required double pixelTolerance, - required MapCamera camera, + required double devicePixelRatio, }) { final tolerance = getEffectiveSimplificationTolerance( crs: camera.crs, zoom: camera.zoom.floor(), pixelTolerance: pixelTolerance, + devicePixelRatio: devicePixelRatio, ); return List<_ProjectedPolyline>.generate( diff --git a/lib/src/misc/simplify.dart b/lib/src/misc/simplify.dart index ea0828ec6..35c63cf3e 100644 --- a/lib/src/misc/simplify.dart +++ b/lib/src/misc/simplify.dart @@ -142,14 +142,16 @@ double getEffectiveSimplificationTolerance({ required Crs crs, required int zoom, required double pixelTolerance, + required double devicePixelRatio, }) { if (pixelTolerance <= 0) return 0; - final (x0, y0) = crs.untransform(0, 0, crs.scale(zoom.toDouble())); + final scale = crs.scale(zoom.toDouble()); + final (x0, y0) = crs.untransform(0, 0, scale); final (x1, y1) = crs.untransform( - pixelTolerance, - pixelTolerance, - crs.scale(zoom.toDouble()), + pixelTolerance * devicePixelRatio, + pixelTolerance * devicePixelRatio, + scale, ); final dx = x1 - x0; From bf465271035bec3dccad62ffbafd4bba3970dc3d Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Thu, 25 Jan 2024 12:54:41 +0000 Subject: [PATCH 2/5] Fixed bug where changing device pixel ratio would not invalidate cached simplifications Refactored internal projection & simplification caching logic into internal `PSCachingLayerState` mixin --- .../layer/general/ps_caching_layer_state.dart | 60 +++++++++++++++++++ .../layer/polygon_layer/polygon_layer.dart | 32 ++++------ .../layer/polyline_layer/polyline_layer.dart | 55 +++++++---------- 3 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 lib/src/layer/general/ps_caching_layer_state.dart diff --git a/lib/src/layer/general/ps_caching_layer_state.dart b/lib/src/layer/general/ps_caching_layer_state.dart new file mode 100644 index 000000000..38a4735f7 --- /dev/null +++ b/lib/src/layer/general/ps_caching_layer_state.dart @@ -0,0 +1,60 @@ +import 'dart:ui'; + +import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; + +/// Projecton & Simplification Caching State +/// +/// [P] is a Projected feature. +@internal +mixin PSCachingLayerState

+ on State { + /// Default/last [PlatformDispatcher.onMetricsChanged] callback + /// + /// Monitoring the current device pixel ratio is necessary, as a change in this + /// will invalidate the cache + /// + /// Will be called inside overriden callback, and restored during [dispose]. + void Function()? _onMetricsChangedDefault; + double? _devicePixelRatio; + + List

? cachedProjected; + final cachedSimplified = >{}; + + @override + @mustCallSuper + void initState() { + super.initState(); + + _onMetricsChangedDefault = PlatformDispatcher.instance.onMetricsChanged; + PlatformDispatcher.instance.onMetricsChanged = () { + _onMetricsChangedDefault?.call(); + + final newDPR = MediaQuery.devicePixelRatioOf(context); + if (_devicePixelRatio != newDPR) { + _devicePixelRatio = newDPR; + cachedSimplified.clear(); + } + }; + } + + @override + @mustCallSuper + void dispose() { + PlatformDispatcher.instance.onMetricsChanged = _onMetricsChangedDefault; + super.dispose(); + } + + @override + @mustCallSuper + void didUpdateWidget(W oldWidget) { + super.didUpdateWidget(oldWidget); + + if (canReuseCache(oldWidget)) return; + cachedSimplified.clear(); + cachedProjected = null; + } + + /// Whether the currently cached projections and simplifications can be reused + bool canReuseCache(W oldWidget); +} diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 867ed8840..15dc74133 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/layer/general/ps_caching_layer_state.dart'; import 'package:flutter_map/src/misc/offsets.dart'; import 'package:flutter_map/src/misc/simplify.dart'; import 'package:latlong2/latlong.dart' hide Path; @@ -61,28 +62,13 @@ class PolygonLayer extends StatefulWidget { State createState() => _PolygonLayerState(); } -class _PolygonLayerState extends State { - List<_ProjectedPolygon>? _cachedProjectedPolygons; - final _cachedSimplifiedPolygons = >{}; - - @override - void didUpdateWidget(PolygonLayer oldWidget) { - super.didUpdateWidget(oldWidget); - - // Reuse cache - if (widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance && - listEquals(oldWidget.polygons, widget.polygons)) return; - - _cachedSimplifiedPolygons.clear(); - _cachedProjectedPolygons = null; - } - +class _PolygonLayerState extends State + with PSCachingLayerState<_ProjectedPolygon, PolygonLayer> { @override Widget build(BuildContext context) { final camera = MapCamera.of(context); - final projected = _cachedProjectedPolygons ??= List.generate( + final projected = cachedProjected ??= List.generate( widget.polygons.length, (i) => _ProjectedPolygon._fromPolygon( camera.crs.projection, @@ -93,12 +79,12 @@ class _PolygonLayerState extends State { final simplified = widget.simplificationTolerance <= 0 ? projected - : _cachedSimplifiedPolygons[camera.zoom.floor()] ??= + : cachedSimplified[camera.zoom.floor()] ??= _computeZoomLevelSimplification( camera: camera, polygons: projected, pixelTolerance: widget.simplificationTolerance, - devicePixelRatio: MediaQuery.of(context).devicePixelRatio, + devicePixelRatio: MediaQuery.devicePixelRatioOf(context), ); final culled = !widget.polygonCulling @@ -122,6 +108,12 @@ class _PolygonLayerState extends State { ); } + @override + bool canReuseCache(PolygonLayer oldWidget) => + widget.simplificationTolerance != 0 && + oldWidget.simplificationTolerance == widget.simplificationTolerance && + listEquals(oldWidget.polygons, widget.polygons); + static List<_ProjectedPolygon> _computeZoomLevelSimplification({ required MapCamera camera, required List<_ProjectedPolygon> polygons, diff --git a/lib/src/layer/polyline_layer/polyline_layer.dart b/lib/src/layer/polyline_layer/polyline_layer.dart index 0858cc396..3d0e69d48 100644 --- a/lib/src/layer/polyline_layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer/polyline_layer.dart @@ -5,6 +5,7 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map/src/layer/general/ps_caching_layer_state.dart'; import 'package:flutter_map/src/misc/offsets.dart'; import 'package:flutter_map/src/misc/simplify.dart'; import 'package:latlong2/latlong.dart'; @@ -72,31 +73,13 @@ class PolylineLayer extends StatefulWidget { State> createState() => _PolylineLayerState(); } -class _PolylineLayerState extends State> { - List<_ProjectedPolyline>? _cachedProjectedPolylines; - final _cachedSimplifiedPolylines = >{}; - - final _culledPolylines = - <_ProjectedPolyline>[]; // Avoids repetitive memory reallocation - - @override - void didUpdateWidget(PolylineLayer oldWidget) { - super.didUpdateWidget(oldWidget); - - // Reuse cache - if (widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance && - listEquals(oldWidget.polylines, widget.polylines)) return; - - _cachedSimplifiedPolylines.clear(); - _cachedProjectedPolylines = null; - } - +class _PolylineLayerState extends State> + with PSCachingLayerState<_ProjectedPolyline, PolylineLayer> { @override Widget build(BuildContext context) { final camera = MapCamera.of(context); - final projected = _cachedProjectedPolylines ??= List.generate( + final projected = cachedProjected ??= List.generate( widget.polylines.length, (i) => _ProjectedPolyline._fromPolyline( camera.crs.projection, @@ -107,12 +90,12 @@ class _PolylineLayerState extends State> { final simplified = widget.simplificationTolerance == 0 ? projected - : _cachedSimplifiedPolylines[camera.zoom.floor()] ??= + : cachedSimplified[camera.zoom.floor()] ??= _computeZoomLevelSimplification( camera: camera, polylines: projected, pixelTolerance: widget.simplificationTolerance, - devicePixelRatio: MediaQuery.of(context).devicePixelRatio, + devicePixelRatio: MediaQuery.devicePixelRatioOf(context), ); final culled = widget.cullingMargin == null @@ -137,13 +120,19 @@ class _PolylineLayerState extends State> { ); } - List<_ProjectedPolyline> _aggressivelyCullPolylines({ + @override + bool canReuseCache(PolylineLayer oldWidget) => + widget.simplificationTolerance != 0 && + oldWidget.simplificationTolerance == widget.simplificationTolerance && + listEquals(oldWidget.polylines, widget.polylines); + + static List<_ProjectedPolyline> _aggressivelyCullPolylines({ required Projection projection, required List<_ProjectedPolyline> polylines, required MapCamera camera, required double cullingMargin, }) { - _culledPolylines.clear(); + final culledPolylines = <_ProjectedPolyline>[]; final bounds = camera.visibleBounds; final margin = cullingMargin / math.pow(2, camera.zoom); @@ -172,7 +161,7 @@ class _PolylineLayerState extends State> { // Gradient poylines cannot be easily segmented if (polyline.gradientColors != null) { - _culledPolylines.add(projectedPolyline); + culledPolylines.add(projectedPolyline); continue; } @@ -192,10 +181,12 @@ class _PolylineLayerState extends State> { } else { // If we cannot see this segment but have seen previous ones, flush the last polyline fragment. if (start != -1) { - _culledPolylines.add(_ProjectedPolyline._( - polyline: polyline, - points: projectedPolyline.points.sublist(start, i + 1), - )); + culledPolylines.add( + _ProjectedPolyline._( + polyline: polyline, + points: projectedPolyline.points.sublist(start, i + 1), + ), + ); // Reset start. start = -1; @@ -206,7 +197,7 @@ class _PolylineLayerState extends State> { // If the last segment was visible push that last visible polyline // fragment, which may also be the entire polyline if `start == 0`. if (containsSegment) { - _culledPolylines.add( + culledPolylines.add( start == 0 ? projectedPolyline : _ProjectedPolyline._( @@ -218,7 +209,7 @@ class _PolylineLayerState extends State> { } } - return _culledPolylines; + return culledPolylines; } static List<_ProjectedPolyline> _computeZoomLevelSimplification({ From 28978d6759f9d68fa29a6d490dbb65257d6f69dd Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Thu, 25 Jan 2024 13:03:24 +0000 Subject: [PATCH 3/5] Improved perfomance by splitting cache reuse logic between projection and simplification --- .../layer/general/ps_caching_layer_state.dart | 25 +++++++++++++++---- .../layer/polygon_layer/polygon_layer.dart | 7 ++++-- .../layer/polyline_layer/polyline_layer.dart | 7 ++++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/src/layer/general/ps_caching_layer_state.dart b/lib/src/layer/general/ps_caching_layer_state.dart index 38a4735f7..00e0bdf75 100644 --- a/lib/src/layer/general/ps_caching_layer_state.dart +++ b/lib/src/layer/general/ps_caching_layer_state.dart @@ -50,11 +50,26 @@ mixin PSCachingLayerState

void didUpdateWidget(W oldWidget) { super.didUpdateWidget(oldWidget); - if (canReuseCache(oldWidget)) return; - cachedSimplified.clear(); - cachedProjected = null; + if (!canReuseProjectionCache(oldWidget)) { + cachedProjected = null; + cachedSimplified.clear(); + } else if (!canReuseSimplificationCache(oldWidget)) { + cachedSimplified.clear(); + } } - /// Whether the currently cached projections and simplifications can be reused - bool canReuseCache(W oldWidget); + /// Whether the currently cached projections can be reused + /// + /// Ignore device pixel ratio changes, this is handled automatically + /// internally. + /// + /// If this is `false`, [canReuseSimplificationCache] will be treated as + /// `false` without execution. + bool canReuseProjectionCache(W oldWidget); + + /// Whether the currently cached simplifications can be reused + /// + /// Ignore device pixel ratio changes, this is handled automatically + /// internally. + bool canReuseSimplificationCache(W oldWidget); } diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 15dc74133..18d86c6d9 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -109,9 +109,12 @@ class _PolygonLayerState extends State } @override - bool canReuseCache(PolygonLayer oldWidget) => + bool canReuseSimplificationCache(PolygonLayer oldWidget) => widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance && + oldWidget.simplificationTolerance == widget.simplificationTolerance; + + @override + bool canReuseProjectionCache(PolygonLayer oldWidget) => listEquals(oldWidget.polygons, widget.polygons); static List<_ProjectedPolygon> _computeZoomLevelSimplification({ diff --git a/lib/src/layer/polyline_layer/polyline_layer.dart b/lib/src/layer/polyline_layer/polyline_layer.dart index 3d0e69d48..c2da860d1 100644 --- a/lib/src/layer/polyline_layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer/polyline_layer.dart @@ -121,9 +121,12 @@ class _PolylineLayerState extends State> } @override - bool canReuseCache(PolylineLayer oldWidget) => + bool canReuseSimplificationCache(PolylineLayer oldWidget) => widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance && + oldWidget.simplificationTolerance == widget.simplificationTolerance; + + @override + bool canReuseProjectionCache(PolylineLayer oldWidget) => listEquals(oldWidget.polylines, widget.polylines); static List<_ProjectedPolyline> _aggressivelyCullPolylines({ From faad770d5473d5d5e71d32cd8c127ee48f10b539 Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Thu, 25 Jan 2024 17:33:57 +0000 Subject: [PATCH 4/5] Removed `PSCachingLayerState` Reworked `devicePixelRatio` cache invalidator --- .../layer/general/ps_caching_layer_state.dart | 75 ------------------- .../layer/polygon_layer/polygon_layer.dart | 60 +++++++++------ .../layer/polyline_layer/polyline_layer.dart | 67 ++++++++++------- 3 files changed, 80 insertions(+), 122 deletions(-) delete mode 100644 lib/src/layer/general/ps_caching_layer_state.dart diff --git a/lib/src/layer/general/ps_caching_layer_state.dart b/lib/src/layer/general/ps_caching_layer_state.dart deleted file mode 100644 index 00e0bdf75..000000000 --- a/lib/src/layer/general/ps_caching_layer_state.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/widgets.dart'; -import 'package:meta/meta.dart'; - -/// Projecton & Simplification Caching State -/// -/// [P] is a Projected feature. -@internal -mixin PSCachingLayerState

- on State { - /// Default/last [PlatformDispatcher.onMetricsChanged] callback - /// - /// Monitoring the current device pixel ratio is necessary, as a change in this - /// will invalidate the cache - /// - /// Will be called inside overriden callback, and restored during [dispose]. - void Function()? _onMetricsChangedDefault; - double? _devicePixelRatio; - - List

? cachedProjected; - final cachedSimplified = >{}; - - @override - @mustCallSuper - void initState() { - super.initState(); - - _onMetricsChangedDefault = PlatformDispatcher.instance.onMetricsChanged; - PlatformDispatcher.instance.onMetricsChanged = () { - _onMetricsChangedDefault?.call(); - - final newDPR = MediaQuery.devicePixelRatioOf(context); - if (_devicePixelRatio != newDPR) { - _devicePixelRatio = newDPR; - cachedSimplified.clear(); - } - }; - } - - @override - @mustCallSuper - void dispose() { - PlatformDispatcher.instance.onMetricsChanged = _onMetricsChangedDefault; - super.dispose(); - } - - @override - @mustCallSuper - void didUpdateWidget(W oldWidget) { - super.didUpdateWidget(oldWidget); - - if (!canReuseProjectionCache(oldWidget)) { - cachedProjected = null; - cachedSimplified.clear(); - } else if (!canReuseSimplificationCache(oldWidget)) { - cachedSimplified.clear(); - } - } - - /// Whether the currently cached projections can be reused - /// - /// Ignore device pixel ratio changes, this is handled automatically - /// internally. - /// - /// If this is `false`, [canReuseSimplificationCache] will be treated as - /// `false` without execution. - bool canReuseProjectionCache(W oldWidget); - - /// Whether the currently cached simplifications can be reused - /// - /// Ignore device pixel ratio changes, this is handled automatically - /// internally. - bool canReuseSimplificationCache(W oldWidget); -} diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 18d86c6d9..8b3ecf2f0 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -5,7 +5,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map/src/layer/general/ps_caching_layer_state.dart'; import 'package:flutter_map/src/misc/offsets.dart'; import 'package:flutter_map/src/misc/simplify.dart'; import 'package:latlong2/latlong.dart' hide Path; @@ -62,13 +61,30 @@ class PolygonLayer extends StatefulWidget { State createState() => _PolygonLayerState(); } -class _PolygonLayerState extends State - with PSCachingLayerState<_ProjectedPolygon, PolygonLayer> { +class _PolygonLayerState extends State { + List<_ProjectedPolygon>? _cachedProjectedPolygons; + final _cachedSimplifiedPolygons = >{}; + + double? _devicePixelRatio; + + @override + void didUpdateWidget(PolygonLayer oldWidget) { + super.didUpdateWidget(oldWidget); + + if (!listEquals(oldWidget.polygons, widget.polygons)) { + _cachedProjectedPolygons = null; + _cachedSimplifiedPolygons.clear(); + } else if (!(widget.simplificationTolerance != 0 && + oldWidget.simplificationTolerance == widget.simplificationTolerance)) { + _cachedSimplifiedPolygons.clear(); + } + } + @override Widget build(BuildContext context) { final camera = MapCamera.of(context); - final projected = cachedProjected ??= List.generate( + final projected = _cachedProjectedPolygons ??= List.generate( widget.polygons.length, (i) => _ProjectedPolygon._fromPolygon( camera.crs.projection, @@ -77,15 +93,24 @@ class _PolygonLayerState extends State growable: false, ); - final simplified = widget.simplificationTolerance <= 0 - ? projected - : cachedSimplified[camera.zoom.floor()] ??= - _computeZoomLevelSimplification( - camera: camera, - polygons: projected, - pixelTolerance: widget.simplificationTolerance, - devicePixelRatio: MediaQuery.devicePixelRatioOf(context), - ); + late final List<_ProjectedPolygon> simplified; + if (widget.simplificationTolerance == 0) { + simplified = projected; + } else { + final newDPR = MediaQuery.devicePixelRatioOf(context); + if (newDPR != _devicePixelRatio) { + _devicePixelRatio = newDPR; + _cachedSimplifiedPolygons.clear(); + } + + simplified = _cachedSimplifiedPolygons[camera.zoom.floor()] ??= + _computeZoomLevelSimplification( + camera: camera, + polygons: projected, + pixelTolerance: widget.simplificationTolerance, + devicePixelRatio: newDPR, + ); + } final culled = !widget.polygonCulling ? simplified @@ -108,15 +133,6 @@ class _PolygonLayerState extends State ); } - @override - bool canReuseSimplificationCache(PolygonLayer oldWidget) => - widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance; - - @override - bool canReuseProjectionCache(PolygonLayer oldWidget) => - listEquals(oldWidget.polygons, widget.polygons); - static List<_ProjectedPolygon> _computeZoomLevelSimplification({ required MapCamera camera, required List<_ProjectedPolygon> polygons, diff --git a/lib/src/layer/polyline_layer/polyline_layer.dart b/lib/src/layer/polyline_layer/polyline_layer.dart index c2da860d1..e8317ffd9 100644 --- a/lib/src/layer/polyline_layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer/polyline_layer.dart @@ -5,7 +5,6 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map/src/layer/general/ps_caching_layer_state.dart'; import 'package:flutter_map/src/misc/offsets.dart'; import 'package:flutter_map/src/misc/simplify.dart'; import 'package:latlong2/latlong.dart'; @@ -73,13 +72,30 @@ class PolylineLayer extends StatefulWidget { State> createState() => _PolylineLayerState(); } -class _PolylineLayerState extends State> - with PSCachingLayerState<_ProjectedPolyline, PolylineLayer> { +class _PolylineLayerState extends State> { + List<_ProjectedPolyline>? _cachedProjectedPolylines; + final _cachedSimplifiedPolylines = >>{}; + + double? _devicePixelRatio; + + @override + void didUpdateWidget(PolylineLayer oldWidget) { + super.didUpdateWidget(oldWidget); + + if (!listEquals(oldWidget.polylines, widget.polylines)) { + _cachedProjectedPolylines = null; + _cachedSimplifiedPolylines.clear(); + } else if (!(widget.simplificationTolerance != 0 && + oldWidget.simplificationTolerance == widget.simplificationTolerance)) { + _cachedSimplifiedPolylines.clear(); + } + } + @override Widget build(BuildContext context) { final camera = MapCamera.of(context); - final projected = cachedProjected ??= List.generate( + final projected = _cachedProjectedPolylines ??= List.generate( widget.polylines.length, (i) => _ProjectedPolyline._fromPolyline( camera.crs.projection, @@ -88,15 +104,24 @@ class _PolylineLayerState extends State> growable: false, ); - final simplified = widget.simplificationTolerance == 0 - ? projected - : cachedSimplified[camera.zoom.floor()] ??= - _computeZoomLevelSimplification( - camera: camera, - polylines: projected, - pixelTolerance: widget.simplificationTolerance, - devicePixelRatio: MediaQuery.devicePixelRatioOf(context), - ); + late final List<_ProjectedPolyline> simplified; + if (widget.simplificationTolerance == 0) { + simplified = projected; + } else { + final newDPR = MediaQuery.devicePixelRatioOf(context); + if (newDPR != _devicePixelRatio) { + _devicePixelRatio = newDPR; + _cachedSimplifiedPolylines.clear(); + } + + simplified = _cachedSimplifiedPolylines[camera.zoom.floor()] ??= + _computeZoomLevelSimplification( + camera: camera, + polylines: projected, + pixelTolerance: widget.simplificationTolerance, + devicePixelRatio: newDPR, + ); + } final culled = widget.cullingMargin == null ? simplified @@ -120,15 +145,6 @@ class _PolylineLayerState extends State> ); } - @override - bool canReuseSimplificationCache(PolylineLayer oldWidget) => - widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance; - - @override - bool canReuseProjectionCache(PolylineLayer oldWidget) => - listEquals(oldWidget.polylines, widget.polylines); - static List<_ProjectedPolyline> _aggressivelyCullPolylines({ required Projection projection, required List<_ProjectedPolyline> polylines, @@ -215,9 +231,10 @@ class _PolylineLayerState extends State> return culledPolylines; } - static List<_ProjectedPolyline> _computeZoomLevelSimplification({ + static List<_ProjectedPolyline> + _computeZoomLevelSimplification({ required MapCamera camera, - required List<_ProjectedPolyline> polylines, + required List<_ProjectedPolyline> polylines, required double pixelTolerance, required double devicePixelRatio, }) { @@ -228,7 +245,7 @@ class _PolylineLayerState extends State> devicePixelRatio: devicePixelRatio, ); - return List<_ProjectedPolyline>.generate( + return List<_ProjectedPolyline>.generate( polylines.length, (i) { final polyline = polylines[i]; From a4fa7c901b79f699b5545bd6e236a44861117a52 Mon Sep 17 00:00:00 2001 From: JaffaKetchup Date: Sat, 3 Feb 2024 12:21:17 +0000 Subject: [PATCH 5/5] Commented projection & simplification cache invalidating algorithms --- lib/src/layer/polygon_layer/polygon_layer.dart | 10 ++++++++-- lib/src/layer/polyline_layer/polyline_layer.dart | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/src/layer/polygon_layer/polygon_layer.dart b/lib/src/layer/polygon_layer/polygon_layer.dart index 8b3ecf2f0..3e99ca3a3 100644 --- a/lib/src/layer/polygon_layer/polygon_layer.dart +++ b/lib/src/layer/polygon_layer/polygon_layer.dart @@ -72,10 +72,15 @@ class _PolygonLayerState extends State { super.didUpdateWidget(oldWidget); if (!listEquals(oldWidget.polygons, widget.polygons)) { + // If the polylines have changed, then both the projections and the + // projection-dependendent simplifications must be invalidated _cachedProjectedPolygons = null; _cachedSimplifiedPolygons.clear(); - } else if (!(widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance)) { + } else if (oldWidget.simplificationTolerance != + widget.simplificationTolerance) { + // If only the simplification tolerance has changed, this does not affect + // the projections (as that is done before simplification), so only + // invalidate the simplifications _cachedSimplifiedPolygons.clear(); } } @@ -97,6 +102,7 @@ class _PolygonLayerState extends State { if (widget.simplificationTolerance == 0) { simplified = projected; } else { + // If the DPR has changed, invalidate the simplification cache final newDPR = MediaQuery.devicePixelRatioOf(context); if (newDPR != _devicePixelRatio) { _devicePixelRatio = newDPR; diff --git a/lib/src/layer/polyline_layer/polyline_layer.dart b/lib/src/layer/polyline_layer/polyline_layer.dart index e8317ffd9..6be75f675 100644 --- a/lib/src/layer/polyline_layer/polyline_layer.dart +++ b/lib/src/layer/polyline_layer/polyline_layer.dart @@ -83,10 +83,15 @@ class _PolylineLayerState extends State> { super.didUpdateWidget(oldWidget); if (!listEquals(oldWidget.polylines, widget.polylines)) { + // If the polylines have changed, then both the projections and the + // projection-dependendent simplifications must be invalidated _cachedProjectedPolylines = null; _cachedSimplifiedPolylines.clear(); - } else if (!(widget.simplificationTolerance != 0 && - oldWidget.simplificationTolerance == widget.simplificationTolerance)) { + } else if (oldWidget.simplificationTolerance != + widget.simplificationTolerance) { + // If only the simplification tolerance has changed, this does not affect + // the projections (as that is done before simplification), so only + // invalidate the simplifications _cachedSimplifiedPolylines.clear(); } } @@ -108,6 +113,7 @@ class _PolylineLayerState extends State> { if (widget.simplificationTolerance == 0) { simplified = projected; } else { + // If the DPR has changed, invalidate the simplification cache final newDPR = MediaQuery.devicePixelRatioOf(context); if (newDPR != _devicePixelRatio) { _devicePixelRatio = newDPR;