lodim
Fast and pixel accurate fixed-point 2D geometry with minimal approximations.
Optimal for the following use cases:
- 2D game development that uses tile-based or grid-based rendering.
- Graphic rendering of pixel art or other fixed-point rasterized graphics.
- Fixed-point geometry for performance or consistency reasons.
Features
Use lodim
to quickly work with 2D geometry in a fixed-point space:
- Fixed-point: All values and results are always integers.
- Fast: AOT and JIT benchmarked on the Dart VM.
- Pixel accurate: Minimal
^1
ambiguity or hidden rounding errors. - Ergonomics: Familiar API to
dart:ui
(and similar) for ease of use. - Cross-platform: Works on all Dart platforms, including Flutter and web.
^1
: lodim
does provide some algorithms that make approximations, such as
<Pos>.lineTo
, which uses Bresenham's line algorithm.
However, all of these algorithms are cleanly defined and documented, and
allow user-defined overrides.
Design Principles
lodim
is designed to be a low-level, fixed-point geometry library that is not
opionated about how you use it. It is not a game engine, nor does it provide
high-level abstractions for rendering or collections.
For example, top-level functions such as getRect
or fillRect
operate on
either Rect
or Pos
types and either callback methods or a generic linear
buffer. For a higher-level APIs, consider using lodim
in conjunction with
another library, such as package:sector
.
Usage
Just add a dependency in your pubspec.yaml
or run the following command:
dart pub add lodim
If you've used another 2D geometry library, such as dart:ui
from
Flutter, you will find lodim
to be very similar. Work with similar types such
as Pos
(i.e. Offset
) and Rect
:
// Creates a (x, y) position.
final pos = Pos(10, 20);
// Creates a rectangle.
final rect = Rect.fromLTWH(0, 0, 100, 100);
Take advantage of tested and benchmarked common operations:
// Rotate a position by 135 degrees counter-clockwise.
final rotated = pos.rotate45(-3);
// Get the intersection of two rectangles.
final intersection = rect.intersect(a, b);
Use built-in algorithms for common tasks, or define your own:
// Determine the distance between two positions.
final distance = pos1.distanceTo(pos2);
// Use another algorithm to determine the distance or define your own.
final manhattan = pos1.distanceTo(pos2, using: manhattan);
// Draw a line from one position to another.
final line = pos1.lineTo(pos2);
// Use your own algorithm to draw a line.
final custom = pos1.lineTo(pos2, using: someOtherAlgorithm);
Benchmarks
To run the benchmarks, run:
dart run benchmark/benchmark.dart
# Or, to use AOT:
dart compile exe benchmark/benchmark.dart
./benchmark/benchmark.exe
# Or, to profile using devtools:
dart run --pause-isolates-on-start --observe benchmark/benchmark.dart
In local benchmarks on a M2 Macbook Pro, compared to ^2
code.
^2
: What users might write themselves, using (int, int)
tuples or similar.
JIT:
Benchmark | lodim |
Baseline | Delta |
---|---|---|---|
10k allocations positions | 479.8 us | 362.9 us | -25% |
10k euclidian distance | 138.3 us | 153.5 us | +11% |
10k rotations in 45° steps | 6875.8 us | 13658.7 us | +98% |
10k lines drawn | 1340049.5 | - | - |
AOT:
Benchmark | lodim |
Baseline | Delta |
---|---|---|---|
10k allocations positions | 342.7 us | 463.9 us | +35% |
10k euclidian distance | 139.7 us | 132.7 us | -5% |
10k rotations in 45° steps | 5858.2 us | 11708.0 us | +100% |
10k lines drawn | 1333651.0 us | - | - |
tl;dr: lodim
is faster than baseline code, using both JIT and AOT.
Specializing based on fixed-point geometry allows for optimizations that are
not possible with general-purpose code, such as jump-table based rotations.
Contributing
To run the tests, run:
dart test
To check code coverage locally, run:
dart tool/coverage.dart
To preview dartdoc
output locally, run:
dart tool/dartdoc.dart
Libraries
- lodim
- Fast pixel accurate fixed-point 2D geometry with minimum approximations.