Just Dart
Allocation-free math for Flutter games, shared by the Just engine packages. Every type is mutable and works in place, so code that runs every frame (physics, particles, culling, picking) doesn't add garbage-collection pressure.
Features
Vector2/Vector3: mutable vectors with in-place arithmetic (add,addScaled,normalize,crossInto, ...) andOffsetconversions forCanvas.Quaternion: 3-D rotations with axis-angle, slerp, vector rotation and Euler angles that round-trip the values an inspector shows.Matrix4: the samevector_math64-bit class that Flutter exports, so the name never clashes. TheJustMatrix4extension adds translate-rotate-scale builders, products, point and direction transforms, and y-down orthographic and perspective projections.Aabb3,Ray3,Plane,Frustum: bounds, picking rays and view frustum culling.Quadtree<T>: a 2-D broad phase that answers "what overlaps this rectangle?" queries.
Runs on Android, iOS, macOS, Windows, Linux and the web, compiled with
dart2js or dart2wasm. The package needs Flutter, because it uses Offset
and Rect from dart:ui.
Getting started
Requires Dart ^3.11.0 and Flutter >=3.41.0.
flutter pub add just_dart
import 'package:just_dart/just_dart.dart';
Conventions
- Axes: x right, y down, z into the screen. A positive turn about Z
carries +X towards +Y, which is clockwise on screen, the same as
Canvas.rotate. - Euler angles:
q = qY · qX · qZ, so Z is applied first, then X, then Y (Unity's order). A 2-D rotation is(0, 0, angle). - Clip space: x and y both run −1..1 (y down), and depth runs 0 (near) to 1 (far).
Matrix4storage is column-major: element (row r, column c) isstorage[c * 4 + r].- Output parameters: methods that take an
outparameter write into it instead of allocating, and acceptoutbeing one of the inputs. - Equality compares components exactly. The values are mutable, so
don't change a vector or quaternion while it's a key in a
SetorMap.
Usage
Vectors
final position = Vector2.zero();
final velocity = Vector2(3, 4);
const dt = 1 / 60;
position.addScaled(velocity, dt); // position += velocity * dt, no allocation
final speed = velocity.length; // 5.0
velocity.normalize(); // in place; a zero vector stays zero
final a = Vector3(1, 2, 3);
final normal = Vector3.zero();
a.crossInto(Vector3(0, 1, 0), normal); // a × (0, 1, 0), written into normal
final screen = a.toOffset(); // drops z, for Canvas APIs
Rotations and transforms
final rotation = Quaternion.euler(0, 0, math.pi / 4); // 45° on screen
final world = Matrix4.zero()
..setFromTrs(Vector3(100, 50, 0), rotation, Vector3(2, 2, 1));
final p = Vector3(10, 0, 0);
world.transformPointInto(p, p); // in place
// Show Euler angles in an inspector: pass the last values as a hint, and
// they come back unchanged whenever they still describe the rotation.
final shown = Vector3(0, 0, 2 * math.pi + 0.3); // 377°
final q = Quaternion.euler(shown.x, shown.y, shown.z);
q.eulerInto(shown, hint: shown); // still exactly (0, 0, 2π + 0.3)
Culling and picking
final projection = Matrix4.zero()
..setPerspectiveYDown(math.pi / 3, 16 / 9, 0.1, 1000);
final frustum = Frustum()..setFromViewProjection(projection);
final bounds = Aabb3()..setValues(-1, -1, 10, 1, 1, 12);
if (frustum.intersectsAabb(bounds)) {
// draw it
}
final ray = Ray3()..setValues(0, 0, -10, 0, 0, 1);
final t = ray.intersectAabb(bounds); // distance along the ray, or null
final ground = ray.intersectZ(); // where it crosses the z = 0 plane
Quadtree
final tree = Quadtree<String>(
bounds: const Rect.fromLTWH(-500, -500, 1000, 1000),
maxItems: 8,
maxDepth: 6,
);
tree.insert('player', const Rect.fromLTWH(10, 20, 24, 24));
tree.insert('enemy_1', const Rect.fromLTWH(40, 35, 20, 20));
final nearby = <String>[];
tree.queryRect(const Rect.fromLTWH(0, 0, 80, 80), nearby);
print(nearby); // [player, enemy_1]
The tree stores each item once, so a query never returns an item twice.
Rectangles are captured when they're inserted: to move items, clear()
the tree and insert them again.
Using it alongside vector_math
Matrix4 is vector_math's own class, so the two packages never disagree
about it. However, vector_math also defines Vector2, Vector3,
Quaternion, Aabb3, Plane and Frustum. If a file imports both
packages, prefix one of them:
import 'package:just_dart/just_dart.dart';
import 'package:vector_math/vector_math_64.dart' as vm;
API
| Type | What it is for |
|---|---|
Vector2, Vector3 |
Mutable vectors |
Quaternion |
Rotations and Euler angles |
Matrix4, JustMatrix4 |
4×4 transforms and projections |
Aabb3 |
Axis-aligned bounding boxes |
Ray3 |
Rays for picking (plane, z-plane and box hits) |
Plane |
Signed-distance planes |
Frustum |
View frustum culling |
Quadtree<T> |
2-D rectangle broad phase |
Everything is exported from package:just_dart/just_dart.dart.
Development
From packages/just_dart:
flutter pub get
flutter test
flutter analyze
dart format .
Contributing
- See CONTRIBUTING.md
- See CODE_OF_CONDUCT.md
License
BSD-3-Clause. See LICENSE.
Libraries
- just_dart
- Shared Dart utilities for the Just engine packages.