Skip to content

Commit 991f53c

Browse files
authored
Fix polygon draw batching (#1599)
* Fix some polygon edge cases, e.g. double drawing on labels or not correctly setting the border color. I also feel that the code re-organization really helped the separation of concerns and made it much clearer what each part is doing. Specifically: * Fill and border are clearly separated. * The loop is only responsible for batching paths. The Paint is now handled once during drawing. * Address accidental holes when polygons are being batched but their polygons have opposing normals. This is a feature of canvas for cutting holes. This feature might actually be handing for simplifying our hole cutting code, however in this case we don't want it. This is leaky, since this only applies when polygons would be batched, i.e. they have identical properties, they're overlapping, and their points have opposing normals.
1 parent 61bc775 commit 991f53c

File tree

2 files changed

+42
-14
lines changed

2 files changed

+42
-14
lines changed

lib/src/layer/polygon_layer.dart

+27-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ enum PolygonLabelPlacement {
1111
polylabel,
1212
}
1313

14+
bool isClockwise(List<LatLng> points) {
15+
double sum = 0;
16+
for (int i = 0; i < points.length; ++i) {
17+
final a = points[i];
18+
final b = points[(i + 1) % points.length];
19+
20+
sum += (b.longitude - a.longitude) * (b.latitude + a.latitude);
21+
}
22+
return sum >= 0;
23+
}
24+
1425
class Polygon {
1526
final List<LatLng> points;
1627
final List<List<LatLng>>? holePointsList;
@@ -27,6 +38,10 @@ class Polygon {
2738
final TextStyle labelStyle;
2839
final PolygonLabelPlacement labelPlacement;
2940
final bool rotateLabel;
41+
// Designates whether the given polygon points follow a clock or anti-clockwise direction.
42+
// This is respected during draw call batching for filled polygons. Otherwise, batched polygons
43+
// of opposing clock-directions cut holes into each other leading to a leaky optimization.
44+
final bool _filledAndClockwise;
3045

3146
LatLngBounds? _boundingBox;
3247
LatLngBounds get boundingBox {
@@ -49,19 +64,21 @@ class Polygon {
4964
this.labelStyle = const TextStyle(),
5065
this.labelPlacement = PolygonLabelPlacement.centroid,
5166
this.rotateLabel = false,
52-
});
67+
}) : _filledAndClockwise = isFilled && isClockwise(points);
5368

5469
/// Used to batch draw calls to the canvas.
5570
int get renderHashCode => Object.hash(
56-
holePointsList,
57-
color,
58-
borderStrokeWidth,
59-
borderColor,
60-
isDotted,
61-
isFilled,
62-
strokeCap,
63-
strokeJoin,
64-
labelStyle);
71+
holePointsList,
72+
color,
73+
borderStrokeWidth,
74+
borderColor,
75+
isDotted,
76+
isFilled,
77+
strokeCap,
78+
strokeJoin,
79+
labelStyle,
80+
_filledAndClockwise,
81+
);
6582
}
6683

6784
class PolygonLayer extends StatelessWidget {

test/layer/polygon_layer_test.dart

+15-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ void main() {
1616
borderColor: Colors.purple,
1717
borderStrokeWidth: 4,
1818
label: '$i',
19-
points: <LatLng>[
20-
const LatLng(55.5, -0.09),
21-
const LatLng(54.3498, -6.2603),
22-
const LatLng(52.8566, 2.3522),
19+
points: const [
20+
LatLng(55.5, -0.09),
21+
LatLng(54.3498, -6.2603),
22+
LatLng(52.8566, 2.3522),
2323
],
2424
),
2525
];
@@ -35,4 +35,15 @@ void main() {
3535
of: find.byType(PolygonLayer), matching: find.byType(CustomPaint)),
3636
findsOneWidget);
3737
});
38+
39+
test('polygon normal/rotation', () {
40+
const clockwise = [
41+
LatLng(30, 20),
42+
LatLng(30, 30),
43+
LatLng(20, 30),
44+
LatLng(20, 20),
45+
];
46+
expect(isClockwise(clockwise), isTrue);
47+
expect(isClockwise(clockwise.reversed.toList()), isFalse);
48+
});
3849
}

0 commit comments

Comments
 (0)