Skip to content

Commit 0167891

Browse files
authored
fix: overlapping Polygon cutting & color/translucency mixing (#1901)
1 parent c321865 commit 0167891

File tree

3 files changed

+118
-9
lines changed

3 files changed

+118
-9
lines changed

example/lib/pages/polygon.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,71 @@ class _PolygonPageState extends State<PolygonPage> {
188188
subtitle: 'This one still works with performant rendering',
189189
),
190190
),
191+
Polygon(
192+
points: const [
193+
LatLng(61.861042, 0.946502),
194+
LatLng(61.861458, 0.949468),
195+
LatLng(61.861427, 0.949626),
196+
LatLng(61.859015, 0.951513),
197+
LatLng(61.858129, 0.952652)
198+
],
199+
holePointsList: [],
200+
color: Colors.lightGreen.withOpacity(0.5),
201+
borderColor: Colors.lightGreen.withOpacity(0.5),
202+
borderStrokeWidth: 10,
203+
hitValue: (
204+
title: 'Testing opacity treatment (small)',
205+
subtitle:
206+
"Holes shouldn't be cut, and colors should be mixed correctly",
207+
),
208+
),
209+
Polygon(
210+
points: const [
211+
LatLng(61.861042, 0.946502),
212+
LatLng(61.861458, 0.949468),
213+
LatLng(61.861427, 0.949626),
214+
LatLng(61.859015, 0.951513),
215+
LatLng(61.858129, 0.952652),
216+
LatLng(61.857633, 0.953214),
217+
LatLng(61.855842, 0.954683),
218+
LatLng(61.855769, 0.954692),
219+
LatLng(61.855679, 0.954565),
220+
LatLng(61.855417, 0.953926),
221+
LatLng(61.855268, 0.953431),
222+
LatLng(61.855173, 0.952443),
223+
LatLng(61.855161, 0.951147),
224+
LatLng(61.855222, 0.950822),
225+
LatLng(61.855928, 0.948422),
226+
LatLng(61.856365, 0.946638),
227+
LatLng(61.856456, 0.946586),
228+
LatLng(61.856787, 0.946656),
229+
LatLng(61.857578, 0.946675),
230+
LatLng(61.859338, 0.946453),
231+
LatLng(61.861042, 0.946502)
232+
],
233+
holePointsList: const [
234+
[
235+
LatLng(61.858881, 0.947234),
236+
LatLng(61.858728, 0.947126),
237+
LatLng(61.858562, 0.947132),
238+
LatLng(61.858458, 0.947192),
239+
LatLng(61.85844, 0.947716),
240+
LatLng(61.858488, 0.947819),
241+
LatLng(61.858766, 0.947818),
242+
LatLng(61.858893, 0.947779),
243+
LatLng(61.858975, 0.947542),
244+
LatLng(61.858881, 0.947234)
245+
]
246+
],
247+
color: Colors.lightGreen.withOpacity(0.5),
248+
borderColor: Colors.lightGreen.withOpacity(0.5),
249+
borderStrokeWidth: 10,
250+
hitValue: (
251+
title: 'Testing opacity treatment (large)',
252+
subtitle:
253+
"Holes shouldn't be cut, and colors should be mixed correctly",
254+
),
255+
),
191256
];
192257
late final _polygons =
193258
Map.fromEntries(_polygonsRaw.map((e) => MapEntry(e.hitValue, e)));

lib/src/layer/polygon_layer/painter.dart

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,20 @@ base class _PolygonPainter<R extends Object>
1616
/// Reference to the bounding box of the [Polygon].
1717
final LatLngBounds bounds;
1818

19-
/// Whether to draw per-polygon labels
19+
/// Whether to draw per-polygon labels ([Polygon.label])
20+
///
21+
/// Note that drawing labels will reduce performance, as the internal
22+
/// canvas must be drawn to and 'saved' more frequently to ensure the proper
23+
/// stacking order is maintained. This can be avoided, potentially at the
24+
/// expense of appearance, by setting [PolygonLayer.drawLabelsLast].
25+
///
26+
/// It is safe to ignore this property, and the performance pitfalls described
27+
/// above, if no [Polygon]s have labels specified.
2028
final bool polygonLabels;
2129

2230
/// Whether to draw labels last and thus over all the polygons
31+
///
32+
/// This may improve performance: see [polygonLabels] for more information.
2333
final bool drawLabelsLast;
2434

2535
/// Create a new [_PolygonPainter] instance.
@@ -85,6 +95,8 @@ base class _PolygonPainter<R extends Object>
8595

8696
@override
8797
void paint(Canvas canvas, Size size) {
98+
const checkOpacity = true; // for debugging purposes only, should be true
99+
88100
final trianglePoints = <Offset>[];
89101

90102
final filledPath = Path();
@@ -154,8 +166,12 @@ base class _PolygonPainter<R extends Object>
154166
// The hash is based on the polygons visual properties. If the hash from
155167
// the current and the previous polygon no longer match, we need to flush
156168
// the batch previous polygons.
169+
// We also need to flush if the opacity is not 1 or 0, so that they get
170+
// mixed properly. Otherwise, holes get cut, or colors aren't mixed,
171+
// depending on the holes handler.
157172
final hash = polygon.renderHashCode;
158-
if (lastHash != hash) {
173+
final opacity = polygon.color?.opacity ?? 0;
174+
if (lastHash != hash || (checkOpacity && opacity > 0 && opacity < 1)) {
159175
drawPaths();
160176
}
161177
lastPolygon = polygon;
@@ -193,13 +209,11 @@ base class _PolygonPainter<R extends Object>
193209
}
194210

195211
// Afterwards deal with more complicated holes.
212+
// Improper handling of opacity and fill methods may result in normal
213+
// polygons cutting holes into other polygons, when they should be mixing:
214+
// https://github.com/fleaflet/flutter_map/issues/1898.
196215
final holePointsList = polygon.holePointsList;
197216
if (holePointsList != null && holePointsList.isNotEmpty) {
198-
// Ideally we'd use `Path.combine(PathOperation.difference, ...)`
199-
// instead of evenOdd fill-type, however it creates visual artifacts
200-
// using the web renderer.
201-
filledPath.fillType = PathFillType.evenOdd;
202-
203217
final holeOffsetsList = List<List<Offset>>.generate(
204218
holePointsList.length,
205219
(i) => getOffsets(camera, origin, holePointsList[i]),
@@ -208,11 +222,27 @@ base class _PolygonPainter<R extends Object>
208222

209223
for (final holeOffsets in holeOffsetsList) {
210224
filledPath.addPolygon(holeOffsets, true);
225+
226+
// TODO: Potentially more efficient and may change the need to do
227+
// opacity checking - needs testing. However,
228+
// https://github.com/flutter/flutter/issues/44572 prevents this.
229+
/*filledPath = Path.combine(
230+
PathOperation.difference,
231+
filledPath,
232+
Path()..addPolygon(holeOffsets, true),
233+
);*/
211234
}
212235

213236
if (!polygon.disableHolesBorder && polygon.borderStrokeWidth > 0.0) {
214-
_addHoleBordersToPath(borderPath, polygon, holeOffsetsList, size,
215-
canvas, _getBorderPaint(polygon), polygon.borderStrokeWidth);
237+
_addHoleBordersToPath(
238+
borderPath,
239+
polygon,
240+
holeOffsetsList,
241+
size,
242+
canvas,
243+
_getBorderPaint(polygon),
244+
polygon.borderStrokeWidth,
245+
);
216246
}
217247
}
218248

lib/src/layer/polygon_layer/polygon.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ class Polygon<R extends Object> {
1818
final List<List<LatLng>>? holePointsList;
1919

2020
/// The fill color of the [Polygon].
21+
///
22+
/// Note that translucent (opacity is not 1 or 0) colors will reduce
23+
/// performance, as the internal canvas must be drawn to and 'saved' more
24+
/// frequently to ensure the colors of overlapping polygons are mixed
25+
/// correctly.
2126
final Color? color;
2227

2328
/// The stroke width of the [Polygon] outline.
@@ -69,6 +74,11 @@ class Polygon<R extends Object> {
6974
final StrokeJoin strokeJoin;
7075

7176
/// The optional label of the [Polygon].
77+
///
78+
/// Note that specifying a label will reduce performance, as the internal
79+
/// canvas must be drawn to and 'saved' more frequently to ensure the proper
80+
/// stacking order is maintained. This can be avoided, potentially at the
81+
/// expense of appearance, by setting [PolygonLayer.drawLabelsLast].
7282
final String? label;
7383

7484
/// The [TextStyle] of the [Polygon.label].
@@ -78,6 +88,8 @@ class Polygon<R extends Object> {
7888
///
7989
/// [PolygonLabelPlacement.polylabel] can be expensive for some polygons. If
8090
/// there is a large lag spike, try using [PolygonLabelPlacement.centroid].
91+
///
92+
/// Labels will not be drawn if there is not enough space.
8193
final PolygonLabelPlacement labelPlacement;
8294

8395
/// Whether to rotate the label counter to the camera's rotation, to ensure
@@ -187,6 +199,8 @@ class Polygon<R extends Object> {
187199
int? _renderHashCode;
188200

189201
/// An optimized hash code dedicated to be used inside the [_PolygonPainter].
202+
///
203+
/// Note that opacity is handled in the painter.
190204
int get renderHashCode => _renderHashCode ??= Object.hash(
191205
color,
192206
borderStrokeWidth,

0 commit comments

Comments
 (0)