Skip to content

Commit

Permalink
Fix polygon draw batching (#1599)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
ignatz authored Jul 26, 2023
1 parent 61bc775 commit 991f53c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 14 deletions.
37 changes: 27 additions & 10 deletions lib/src/layer/polygon_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ enum PolygonLabelPlacement {
polylabel,
}

bool isClockwise(List<LatLng> points) {
double sum = 0;
for (int i = 0; i < points.length; ++i) {
final a = points[i];
final b = points[(i + 1) % points.length];

sum += (b.longitude - a.longitude) * (b.latitude + a.latitude);
}
return sum >= 0;
}

class Polygon {
final List<LatLng> points;
final List<List<LatLng>>? holePointsList;
Expand All @@ -27,6 +38,10 @@ class Polygon {
final TextStyle labelStyle;
final PolygonLabelPlacement labelPlacement;
final bool rotateLabel;
// Designates whether the given polygon points follow a clock or anti-clockwise direction.
// This is respected during draw call batching for filled polygons. Otherwise, batched polygons
// of opposing clock-directions cut holes into each other leading to a leaky optimization.
final bool _filledAndClockwise;

LatLngBounds? _boundingBox;
LatLngBounds get boundingBox {
Expand All @@ -49,19 +64,21 @@ class Polygon {
this.labelStyle = const TextStyle(),
this.labelPlacement = PolygonLabelPlacement.centroid,
this.rotateLabel = false,
});
}) : _filledAndClockwise = isFilled && isClockwise(points);

/// Used to batch draw calls to the canvas.
int get renderHashCode => Object.hash(
holePointsList,
color,
borderStrokeWidth,
borderColor,
isDotted,
isFilled,
strokeCap,
strokeJoin,
labelStyle);
holePointsList,
color,
borderStrokeWidth,
borderColor,
isDotted,
isFilled,
strokeCap,
strokeJoin,
labelStyle,
_filledAndClockwise,
);
}

class PolygonLayer extends StatelessWidget {
Expand Down
19 changes: 15 additions & 4 deletions test/layer/polygon_layer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ void main() {
borderColor: Colors.purple,
borderStrokeWidth: 4,
label: '$i',
points: <LatLng>[
const LatLng(55.5, -0.09),
const LatLng(54.3498, -6.2603),
const LatLng(52.8566, 2.3522),
points: const [
LatLng(55.5, -0.09),
LatLng(54.3498, -6.2603),
LatLng(52.8566, 2.3522),
],
),
];
Expand All @@ -35,4 +35,15 @@ void main() {
of: find.byType(PolygonLayer), matching: find.byType(CustomPaint)),
findsOneWidget);
});

test('polygon normal/rotation', () {
const clockwise = [
LatLng(30, 20),
LatLng(30, 30),
LatLng(20, 30),
LatLng(20, 20),
];
expect(isClockwise(clockwise), isTrue);
expect(isClockwise(clockwise.reversed.toList()), isFalse);
});
}

0 comments on commit 991f53c

Please sign in to comment.