forked from lpongetti/flutter_map_marker_cluster
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Perf: use more efficient data representation and significantly reduce…
… virtual function overhead (plus some minor cleanups, e.g. spiderify property to layer). In my tests, this reduces the execution time (i.e. ClusterManager.addLayer) for the "Clustering Many Markers" example experience from ~1s to 700ms, i.e. roughly a 30% reduction.
- Loading branch information
Showing
5 changed files
with
120 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,108 +1,108 @@ | ||
import 'dart:collection'; | ||
import 'dart:math'; | ||
|
||
class CellEntry<T> { | ||
final T obj; | ||
final double x; | ||
final double y; | ||
|
||
const CellEntry(this.x, this.y, this.obj); | ||
} | ||
|
||
class GridKey { | ||
final int row; | ||
final int col; | ||
|
||
const GridKey(this.row, this.col); | ||
|
||
@override | ||
bool operator ==(Object other) => | ||
other is GridKey && other.row == row && other.col == col; | ||
|
||
@override | ||
int get hashCode => (col << 26) ^ row; | ||
} | ||
|
||
class DistanceGrid<T> { | ||
final num cellSize; | ||
final int cellSize; | ||
final double _sqCellSize; | ||
|
||
final num _sqCellSize; | ||
final Map<num, Map<num, List<T>>> _grid = {}; | ||
final Map<T, Point> _objectPoint = {}; | ||
final _grid = HashMap<GridKey, List<CellEntry<T>>>(); | ||
final _objectPoint = HashMap<T, GridKey>(); | ||
|
||
DistanceGrid(this.cellSize) : _sqCellSize = cellSize * cellSize; | ||
DistanceGrid(int cellSize) | ||
: cellSize = cellSize > 0 ? cellSize : 1, | ||
_sqCellSize = (cellSize * cellSize).toDouble(); | ||
|
||
void addObject(T obj, Point point) { | ||
final x = _getCoord(point.x), y = _getCoord(point.y); | ||
final row = _grid[y] ??= {}; | ||
final cell = row[x] ??= []; | ||
void clear() { | ||
_grid.clear(); | ||
_objectPoint.clear(); | ||
} | ||
|
||
_objectPoint[obj] = point; | ||
void addObject(T obj, Point<double> point) { | ||
final key = GridKey(_getCoord(point.y), _getCoord(point.x)); | ||
final cell = _grid[key] ??= []; | ||
|
||
cell.add(obj); | ||
_objectPoint[obj] = key; | ||
cell.add(CellEntry<T>(point.x, point.y, obj)); | ||
} | ||
|
||
void updateObject(T obj, Point point) { | ||
void updateObject(T obj, Point<double> point) { | ||
removeObject(obj); | ||
addObject(obj, point); | ||
} | ||
|
||
//Returns true if the object was found | ||
bool removeObject(T obj) { | ||
final point = _objectPoint[obj]; | ||
if (point == null) return false; | ||
|
||
final x = _getCoord(point.x), y = _getCoord(point.y); | ||
final row = _grid[y] ??= {}; | ||
final cell = row[x] ??= []; | ||
|
||
_objectPoint.remove(obj); | ||
|
||
final len = cell.length; | ||
for (var i = 0; i < len; i++) { | ||
if (cell[i] == obj) { | ||
cell.removeAt(i); | ||
|
||
if (len == 1) { | ||
row.remove(x); | ||
|
||
if (_grid[y]!.isEmpty) { | ||
_grid.remove(y); | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
final key = _objectPoint.remove(obj); | ||
if (key == null) return false; | ||
|
||
// Object existed in the _objectPoint map, thus must exist in the grid. | ||
final cell = _grid[key]!; | ||
cell.removeWhere((e) => e.obj == obj); | ||
if (cell.isEmpty) { | ||
_grid.remove(key); | ||
} | ||
return false; | ||
return true; | ||
} | ||
|
||
void eachObject(Function(T) fn) { | ||
for (final i in _grid.keys) { | ||
final row = _grid[i]!; | ||
|
||
for (final j in row.keys) { | ||
final cell = row[j]!; | ||
|
||
for (var k = 0; k < cell.length; k++) { | ||
fn(cell[k]); | ||
} | ||
for (final cell in _grid.values) { | ||
for (final entry in cell) { | ||
fn(entry.obj); | ||
} | ||
} | ||
} | ||
|
||
T? getNearObject(Point point) { | ||
final x = _getCoord(point.x), y = _getCoord(point.y); | ||
var closestDistSq = _sqCellSize; | ||
T? getNearObject(Point<double> point) { | ||
final px = point.x; | ||
final py = point.y; | ||
|
||
final x = _getCoord(px), y = _getCoord(py); | ||
double closestDistSq = _sqCellSize; | ||
T? closest; | ||
|
||
for (var i = y - 1; i <= y + 1; i++) { | ||
final row = _grid[i]; | ||
if (row != null) { | ||
for (var j = x - 1; j <= x + 1; j++) { | ||
final cell = row[j]; | ||
if (cell != null) { | ||
for (var k = 0; k < cell.length; k++) { | ||
final obj = cell[k]; | ||
final dist = _sqDist(_objectPoint[obj]!, point); | ||
|
||
if (dist < closestDistSq || | ||
dist <= closestDistSq && closest == null) { | ||
closestDistSq = dist; | ||
closest = obj; | ||
} | ||
// Checks rows and columns with index +/- 1. | ||
for (int i = y - 1; i <= y + 1; i++) { | ||
for (int j = x - 1; j <= x + 1; j++) { | ||
final cell = _grid[GridKey(i, j)]; | ||
if (cell != null) { | ||
for (final entry in cell) { | ||
final double dx = px - entry.x; | ||
final double dy = py - entry.y; | ||
final double distSq = dx * dx + dy * dy; | ||
|
||
if (distSq <= closestDistSq) { | ||
closestDistSq = distSq; | ||
closest = entry.obj; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return closest; | ||
} | ||
|
||
num _getCoord(num x) { | ||
final coord = x / cellSize; | ||
return coord.isFinite ? coord.floor() : x; | ||
return closest; | ||
} | ||
|
||
num _sqDist(Point p1, Point p2) { | ||
final dx = p2.x - p1.x, dy = p2.y - p1.y; | ||
return dx * dx + dy * dy; | ||
} | ||
int _getCoord(double x) => x ~/ cellSize; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.