Skip to content

Commit 1c36e71

Browse files
authored
feat: added PolygonLayer.painterFillMethod (#2081)
1 parent 79609b1 commit 1c36e71

File tree

2 files changed

+57
-20
lines changed

2 files changed

+57
-20
lines changed

lib/src/layer/polygon_layer/painter.dart

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class _PolygonPainter<R extends Object> extends CustomPainter
3838
/// See [PolygonLayer.debugAltRenderer]
3939
final bool debugAltRenderer;
4040

41+
/// See [PolygonLayer.painterFillMethod]
42+
final PolygonPainterFillMethod painterFillMethod;
43+
4144
/// See [PolygonLayer.invertedFill]
4245
final Color? invertedFill;
4346

@@ -55,6 +58,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter
5558
required this.drawLabelsLast,
5659
required this.debugAltRenderer,
5760
required this.camera,
61+
required this.painterFillMethod,
5862
required this.invertedFill,
5963
required this.hitNotifier,
6064
}) : bounds = camera.visibleBounds {
@@ -70,20 +74,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter
7074
/// inverted fill.
7175
static const _minMaxLatitude = [LatLng(90, 0), LatLng(-90, 0)];
7276

73-
/// Whether to use `PathFillType.evenOdd` (true) or `Path.combine` (false).
74-
///
75-
/// * `Path.combine` doesn't work & isn't stable/consistent on web
76-
/// * `evenOdd` gives broken results when polygons intersect when inverted
77-
/// * `Path.combine` has slightly worse performance than `evenOdd`
78-
///
79-
/// The best option is to use `evenOdd` on web, as it at least works
80-
/// sometimes. On native, we use `Path.combine` when inverted filling, or
81-
/// `evenOdd` otherwise.
82-
///
83-
/// See https://github.com/fleaflet/flutter_map/pull/2046.
84-
late final _useEvenOdd = kIsWeb || invertedFill == null;
85-
86-
/// Do we also remove the holes from the inverted map?
77+
/// Do we remove the holes from the inverted map?
8778
/// Should be `true`.
8879
static const _invertedHoles = true;
8980

@@ -284,7 +275,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter
284275
// ignore: dead_code
285276
if (!_fillInvertedHoles) return;
286277

287-
if (_useEvenOdd) {
278+
if (painterFillMethod == PolygonPainterFillMethod.evenOdd) {
288279
invertedHolePaths.addPolygon(offsets, true);
289280
return;
290281
}
@@ -296,7 +287,7 @@ class _PolygonPainter<R extends Object> extends CustomPainter
296287
}
297288

298289
void unfillPolygon(List<Offset> offsets) {
299-
if (_useEvenOdd) {
290+
if (painterFillMethod == PolygonPainterFillMethod.evenOdd) {
300291
filledPath.fillType = PathFillType.evenOdd;
301292
filledPath.addPolygon(offsets, true);
302293
return;
@@ -585,7 +576,9 @@ class _PolygonPainter<R extends Object> extends CustomPainter
585576
triangles != oldDelegate.triangles ||
586577
camera != oldDelegate.camera ||
587578
bounds != oldDelegate.bounds ||
579+
painterFillMethod != oldDelegate.painterFillMethod ||
588580
invertedFill != oldDelegate.invertedFill ||
581+
debugAltRenderer != oldDelegate.debugAltRenderer ||
589582
drawLabelsLast != oldDelegate.drawLabelsLast ||
590583
polygonLabels != oldDelegate.polygonLabels ||
591584
hitNotifier != oldDelegate.hitNotifier;

lib/src/layer/polygon_layer/polygon_layer.dart

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,36 @@ part 'painter.dart';
2323
part 'polygon.dart';
2424
part 'projected_polygon.dart';
2525

26+
/// The method used by the painter to fill polygons and resolve overlaps &
27+
/// intersections
28+
///
29+
/// Each method has its own advantages and disadvantages.
30+
enum PolygonPainterFillMethod {
31+
/// Uses `PathFillType.evenOdd` with `Path().addPolygon`
32+
///
33+
/// This gives the best performance, and works on the web. However, it yields
34+
/// unintended results in certain edge cases when polygons intersect when
35+
/// [PolygonLayer.invertedFill] is used, or when polygon holes intersect with
36+
/// other holes.
37+
evenOdd,
38+
39+
/// Uses `Path.combine`
40+
///
41+
/// This always gives the best results on non-web platforms. However, it
42+
/// always yields unintended results on the web (due to a Flutter issue), and
43+
/// has slightly worse performance.
44+
///
45+
/// The hit to performance is unlikely to be significant or even noticable in
46+
/// many applications, but applications drawing many polygons may see a slow
47+
/// of about 2ms (as tested in the example app's stress test). Profile your
48+
/// project to determine whether switching methods is suitable, especially if
49+
/// there is no visual difference.
50+
///
51+
/// See https://github.com/flutter/flutter/issues/124675 for the Flutter issue
52+
/// preventing this method from working on the web.
53+
pathCombine,
54+
}
55+
2656
/// A polygon layer for [FlutterMap].
2757
@immutable
2858
base class PolygonLayer<R extends Object>
@@ -78,15 +108,25 @@ base class PolygonLayer<R extends Object>
78108
/// Defaults to `false`.
79109
final bool drawInSingleWorld;
80110

111+
/// The method used by the painter to fill polygons and resolve overlaps &
112+
/// intersections
113+
///
114+
/// See documentation on each value in [PolygonPainterFillMethod] for more
115+
/// advantages and disadvantages of each method.
116+
///
117+
/// Defaults to [PolygonPainterFillMethod.evenOdd] on web &
118+
/// [PolygonPainterFillMethod.pathCombine] otherwise.
119+
final PolygonPainterFillMethod painterFillMethod;
120+
81121
/// Color to apply to the map where not covered by a polygon
82122
///
83123
/// > [!WARNING]
84124
/// > On the web, inverted filling may not work as expected in some cases.
85125
/// > It will not match the behaviour seen on native platforms. Avoid allowing
86126
/// > polygons to intersect, and avoid using holes within polygons.
87-
/// > This is due to multiple limitations/bugs within Flutter. See the
88-
/// > [online documentation](docs.fleaflet.dev/layers/polygon-layer#inverted-filling)
89-
/// > for more info.
127+
/// > This is because [PolygonPainterFillMethod.evenOdd] must be used on the
128+
/// > web, which (due to Flutter issues), does not properly support this
129+
/// > functionality.
90130
final Color? invertedFill;
91131

92132
/// {@macro fm.lhn.layerHitNotifier.usage}
@@ -101,9 +141,12 @@ base class PolygonLayer<R extends Object>
101141
this.polygonCulling = true,
102142
this.polygonLabels = true,
103143
this.drawLabelsLast = false,
144+
this.drawInSingleWorld = false,
145+
this.painterFillMethod = kIsWeb
146+
? PolygonPainterFillMethod.evenOdd
147+
: PolygonPainterFillMethod.pathCombine,
104148
this.invertedFill,
105149
this.hitNotifier,
106-
this.drawInSingleWorld = false,
107150
super.simplificationTolerance,
108151
}) : super();
109152

@@ -222,6 +265,7 @@ class _PolygonLayerState<R extends Object> extends State<PolygonLayer<R>>
222265
camera: camera,
223266
polygonLabels: widget.polygonLabels,
224267
drawLabelsLast: widget.drawLabelsLast,
268+
painterFillMethod: widget.painterFillMethod,
225269
invertedFill: widget.invertedFill,
226270
debugAltRenderer: widget.debugAltRenderer,
227271
hitNotifier: widget.hitNotifier,

0 commit comments

Comments
 (0)