Fixed-point pixel accurate 2D geometry with minimal approximations.
✅ Health | 🚀 Release | 📝 Docs | ♻️ Maintenance |
---|---|---|---|
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.
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: Minimal1 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.
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
.
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);
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 [baseline]2 code.
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.
We welcome contributions to this package!
Please file an issue before contributing larger changes.
This package uses repository specific tooling to enforce formatting, static analysis, and testing. Please run the following commands locally before submitting a pull request:
./dev.sh --packages packages/lodim check
./dev.sh --packages packages/lodim test
Footnotes
-
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. ↩ -
What users might write themselves, using
(int, int)
tuples or similar. ↩