-
-
Notifications
You must be signed in to change notification settings - Fork 860
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make isPointInPolygon 40% faster (at least in JIT mode).
I mostly looked at the function because I wanted to use it myself but it was private. So I thought, if I expose it then I could at least put a ribbon on it by adding a benchmark. Before: (duration: 0:00:05.998949, name: In circle) (duration: 0:00:06.866919, name: Not in circle) After: (duration: 0:00:03.649496, name: In circle) (duration: 0:00:04.611599, name: Not in circle) Note, I opportunistically touched crs to remove the dart:ui dependency. This way it can be compiled with dart (w/o flutter) rendering the instructions in the benchmark correct again. Unfortunately, pointInPolygon is not so fortunate and needs flutter due to the dependency on ui.Offset. That's why I could only run it with "flutter test" in JIT mode.
- Loading branch information
Showing
5 changed files
with
104 additions
and
23 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import 'dart:async'; | ||
import 'dart:math' as math; | ||
import 'dart:ui'; | ||
|
||
import 'package:flutter_map/src/misc/point_in_polygon.dart'; | ||
import 'package:logger/logger.dart'; | ||
|
||
class NoFilter extends LogFilter { | ||
@override | ||
bool shouldLog(LogEvent event) => true; | ||
} | ||
|
||
typedef Result = ({ | ||
String name, | ||
Duration duration, | ||
}); | ||
|
||
Future<Result> timedRun(String name, dynamic Function() body) async { | ||
Logger().i('running $name...'); | ||
final watch = Stopwatch()..start(); | ||
await body(); | ||
watch.stop(); | ||
|
||
return (name: name, duration: watch.elapsed); | ||
} | ||
|
||
// NOTE: to have a more prod like comparison, run with: | ||
// $ dart compile exe benchmark/crs.dart && ./benchmark/crs.exe | ||
// | ||
// If you run in JIT mode, the resulting execution times will be a lot more similar. | ||
Future<void> main() async { | ||
Logger.level = Level.all; | ||
Logger.defaultFilter = NoFilter.new; | ||
Logger.defaultPrinter = SimplePrinter.new; | ||
|
||
final results = <Result>[]; | ||
const N = 3000000; | ||
const points = 1000; | ||
|
||
results.add(await timedRun('In circle', () { | ||
final polygon = List.generate(points, (i) { | ||
final angle = math.pi * 2 / points * i; | ||
return Offset(math.cos(angle), math.sin(angle)); | ||
}); | ||
|
||
const point = math.Point(0, 0); | ||
|
||
bool yesPlease = true; | ||
for (int i = 0; i < N; ++i) { | ||
yesPlease = yesPlease && pointInPolygon(point, polygon); | ||
} | ||
|
||
assert(yesPlease, 'should be in circle'); | ||
return yesPlease; | ||
})); | ||
|
||
results.add(await timedRun('Not in circle', () { | ||
final polygon = List.generate(points, (i) { | ||
final angle = math.pi * 2 / points * i; | ||
return Offset(math.cos(angle), math.sin(angle)); | ||
}); | ||
|
||
const point = math.Point(4, 4); | ||
|
||
bool noSir = false; | ||
for (int i = 0; i < N; ++i) { | ||
noSir = noSir || pointInPolygon(point, polygon); | ||
} | ||
|
||
assert(!noSir, 'should not be in circle'); | ||
return noSir; | ||
})); | ||
|
||
Logger().i('Results:\n${results.map((r) => r.toString()).join('\n')}'); | ||
} |
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
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 |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import 'dart:math' as math; | ||
import 'dart:ui'; | ||
|
||
/// Checks whether point [p] is within the specified closed [polygon] | ||
/// | ||
/// Uses the even-odd algorithm. | ||
bool pointInPolygon(math.Point p, List<Offset> polygon) { | ||
final double px = p.x.toDouble(); | ||
final double py = p.y.toDouble(); | ||
|
||
bool isInPolygon = false; | ||
for (int i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { | ||
final double poIx = polygon[i].dx; | ||
final double poIy = polygon[i].dy; | ||
|
||
final double poJx = polygon[j].dx; | ||
final double poJy = polygon[j].dy; | ||
|
||
if ((((poIy <= py) && (py < poJy)) || ((poJy <= py) && (py < poIy))) && | ||
(px < (poJx - poIx) * (py - poIy) / (poJy - poIy) + poIx)) { | ||
isInPolygon = !isInPolygon; | ||
} | ||
} | ||
return isInPolygon; | ||
} |