Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: pre-project polylines, and improve simplification & culling #1805

Merged
merged 9 commits into from
Jan 23, 2024
20 changes: 9 additions & 11 deletions lib/src/layer/polygon_layer/polygon_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class _PolygonLayerState extends State<PolygonLayer> {

final projected = _cachedProjectedPolygons ??= List.generate(
widget.polygons.length,
(i) => _ProjectedPolygon.fromPolygon(
(i) => _ProjectedPolygon._fromPolygon(
camera.crs.projection,
widget.polygons[i],
),
Expand Down Expand Up @@ -135,17 +135,16 @@ class _PolygonLayerState extends State<PolygonLayer> {
return List<_ProjectedPolygon>.generate(
polygons.length,
(i) {
final polygon = polygons[i];
final holes = polygon.holePoints;
final projectedPolygon = polygons[i];
final holes = projectedPolygon.holePoints;

return _ProjectedPolygon._(
polygon: polygon.polygon,
points: simplifyPoints(
points: polygon.points,
return projectedPolygon
..points = simplifyPoints(
JaffaKetchup marked this conversation as resolved.
Show resolved Hide resolved
points: projectedPolygon.points,
tolerance: tolerance,
highQuality: true,
),
holePoints: holes == null
)
..holePoints = holes == null
? null
: List<List<DoublePoint>>.generate(
holes.length,
Expand All @@ -155,8 +154,7 @@ class _PolygonLayerState extends State<PolygonLayer> {
highQuality: true,
),
growable: false,
),
);
);
},
growable: false,
);
Expand Down
66 changes: 29 additions & 37 deletions lib/src/layer/polygon_layer/projected_polygon.dart
Original file line number Diff line number Diff line change
@@ -1,47 +1,39 @@
part of 'polygon_layer.dart';

@immutable
class _ProjectedPolygon {
final Polygon polygon;
final List<DoublePoint> points;
final List<List<DoublePoint>>? holePoints;

const _ProjectedPolygon._({
required this.polygon,
required this.points,
this.holePoints,
});
// Mutable to reduce GC stress from repetitive allocation
List<DoublePoint> points;
List<List<DoublePoint>>? holePoints;

_ProjectedPolygon.fromPolygon(Projection projection, Polygon polygon)
: this._(
polygon: polygon,
points: List<DoublePoint>.generate(
polygon.points.length,
_ProjectedPolygon._fromPolygon(Projection projection, this.polygon)
: points = List<DoublePoint>.generate(
polygon.points.length,
(j) {
final (x, y) = projection.projectXY(polygon.points[j]);
return DoublePoint(x, y);
},
growable: false,
),
holePoints = (() {
final holes = polygon.holePointsList;
if (holes == null) return null;

return List<List<DoublePoint>>.generate(
holes.length,
(j) {
final (x, y) = projection.projectXY(polygon.points[j]);
return DoublePoint(x, y);
final points = holes[j];
return List<DoublePoint>.generate(
points.length,
(k) {
final (x, y) = projection.projectXY(points[k]);
return DoublePoint(x, y);
},
growable: false,
);
},
growable: false,
),
holePoints: () {
final holes = polygon.holePointsList;
if (holes == null) return null;

return List<List<DoublePoint>>.generate(
holes.length,
(j) {
final points = holes[j];
return List<DoublePoint>.generate(
points.length,
(k) {
final (x, y) = projection.projectXY(points[k]);
return DoublePoint(x, y);
},
growable: false,
);
},
growable: false,
);
}(),
);
);
}());
}
39 changes: 16 additions & 23 deletions lib/src/layer/polyline_layer/painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ part of 'polyline_layer.dart';
/// [CustomPainter] for [Polygon]s.
class _PolylinePainter<R extends Object> extends CustomPainter {
/// Reference to the list of [Polyline]s.
final List<Polyline<R>> polylines;
final List<_ProjectedPolyline> polylines;

/// Reference to the [MapCamera].
final MapCamera camera;
Expand All @@ -20,18 +20,6 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
required this.minimumHitbox,
});

List<Offset> getOffsets(Offset origin, List<LatLng> points) => List.generate(
points.length,
(index) => getOffset(origin, points[index]),
growable: false,
);

Offset getOffset(Offset origin, LatLng point) {
// Critically create as little garbage as possible. This is called on every frame.
final projected = camera.project(point);
return Offset(projected.x - origin.dx, projected.y - origin.dy);
}

@override
bool? hitTest(Offset position) {
if (hitNotifier == null) return null;
Expand All @@ -41,7 +29,9 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
final origin =
camera.project(camera.center).toOffset() - camera.size.toOffset() / 2;

for (final polyline in polylines.reversed) {
for (final projectedPolyline in polylines.reversed) {
final polyline = projectedPolyline.polyline as Polyline<R>;

if (polyline.hitValue == null) continue;
JaffaKetchup marked this conversation as resolved.
Show resolved Hide resolved

// TODO: For efficiency we'd ideally filter by bounding box here. However
Expand All @@ -51,11 +41,11 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
// continue;
// }

final offsets = getOffsets(origin, polyline.points);
final offsets = getOffsetsXY(camera, origin, projectedPolyline.points);
final strokeWidth = polyline.useStrokeWidthInMeter
? _metersToStrokeWidth(
origin,
polyline.points.first,
_unproject(projectedPolyline.points.first),
offsets.first,
polyline.strokeWidth,
)
Expand Down Expand Up @@ -141,11 +131,11 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
final origin =
camera.project(camera.center).toOffset() - camera.size.toOffset() / 2;

for (final polyline in polylines) {
final offsets = getOffsets(origin, polyline.points);
if (offsets.isEmpty) {
continue;
}
for (final projectedPolyline in polylines) {
final polyline = projectedPolyline.polyline;

final offsets = getOffsetsXY(camera, origin, projectedPolyline.points);
if (offsets.isEmpty) continue;

final hash = polyline.renderHashCode;
if (needsLayerSaving || (lastHash != null && lastHash != hash)) {
Expand All @@ -159,7 +149,7 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
if (polyline.useStrokeWidthInMeter) {
strokeWidth = _metersToStrokeWidth(
origin,
polyline.points.first,
_unproject(projectedPolyline.points.first),
offsets.first,
polyline.strokeWidth,
);
Expand Down Expand Up @@ -281,10 +271,13 @@ class _PolylinePainter<R extends Object> extends CustomPainter {
double strokeWidthInMeters,
) {
final r = _distance.offset(p0, strokeWidthInMeters, 180);
final delta = o0 - getOffset(origin, r);
final delta = o0 - getOffset(camera, origin, r);
return delta.distance;
}

LatLng _unproject(DoublePoint p0) =>
camera.crs.projection.unprojectXY(p0.x, p0.y);

@override
bool shouldRepaint(_PolylinePainter<R> oldDelegate) => false;
}
Expand Down
15 changes: 0 additions & 15 deletions lib/src/layer/polyline_layer/polyline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,6 @@ class Polyline<R extends Object> {
this.hitValue,
});

Polyline<R> copyWithNewPoints(List<LatLng> points) => Polyline<R>(
points: points,
strokeWidth: strokeWidth,
color: color,
borderStrokeWidth: borderStrokeWidth,
borderColor: borderColor,
gradientColors: gradientColors,
colorsStop: colorsStop,
isDotted: isDotted,
strokeCap: strokeCap,
strokeJoin: strokeJoin,
useStrokeWidthInMeter: useStrokeWidthInMeter,
hitValue: hitValue,
);

@override
bool operator ==(Object other) =>
identical(this, other) ||
Expand Down
Loading
Loading