dart_bclibc 0.1.2-beta.2
dart_bclibc: ^0.1.2-beta.2 copied to clipboard
Dart FFI bindings for the bclibc ballistics engine.
dart_bclibc #
Dart FFI bindings for the bclibc ballistics engine.
A thin, zero-copy Dart wrapper around libbclibc_ffi — a high-performance 3-DOF + spin drift ballistic solver engine with RK4/Euler integration. Ships with the bclibc C++ source as a git submodule; no pre-built binaries required.
Table of Contents #
- dart_bclibc
Quick start #
import 'package:dart_bclibc/bclibc.dart';
final bc = BcLibC.open(); // loads the native library once
final shot = BcShot(
bc: 0.295,
weightGrain: 168.0,
diameterInch: 0.308,
lengthInch: 1.22,
muzzleVelocityFps: 2750.0,
sightHeightFt: 0.1148,
twistInch: 10.0,
tempC: 15.0,
pressureHpa: 1013.25,
altitudeFt: 0.0,
dragTable: [BcDragPoint(0.0, 0.0), /* … Mach / CD pairs … */],
lookAngleRad: 0.0,
barrelElevationRad: 0.0,
);
final hit = bc.integrateShot(
shot,
BcTrajectoryRequest(rangeLimitFt: 3000.0, rangeStepFt: 100.0),
);
for (final pt in hit.trajectory) {
print('${pt.distanceFt} ft ${pt.velocityFps} fps ${pt.heightFt} ft');
}
API #
Input types #
| Dart type | Description |
|---|---|
BcShot |
Preferred shot input (natural units). All physics conversion — atmosphere density, Coriolis trig, PCHIP drag curve, cant — is performed inside C++ via BCLIBCFFI_Shot::to_shot_props(). |
BcShotProps |
Legacy shot input (pre-computed BcAtmosphere/BcCoriolis structs). |
BcTrajectoryRequest |
Step size, range limit, and BCLIBCFFI_TrajFlag filter bitmask. |
BcConfig |
Solver knobs (step multiplier, accuracy, gravity constant, etc.). |
BcDragPoint |
One Mach / CD entry for the drag table. |
BcWind |
One wind segment (velocity, direction, distance bounds). |
Result types #
| Dart type | Description |
|---|---|
BcTrajectoryData |
One filtered trajectory record. |
BcHitResult |
Full trajectory list + BCLIBCFFI_TerminationReason. |
BcInterception |
Single interpolated point from integrateAtShot. |
BcMaxRangeResult |
Max range (ft) + angle (rad) from findMaxRangeShot. |
BcLibC methods #
| Method | Description |
|---|---|
BcLibC.open() |
Load the native library (call once at startup). |
integrateShot(BcShot, BcTrajectoryRequest) |
Full filtered trajectory. |
integrateAtShot(BcShot, key, value) |
Single interpolated point at a trajectory key. |
findZeroAngleShot(BcShot, distanceFt) |
Barrel elevation to zero at distance. |
findApexShot(BcShot) |
Highest point of the trajectory arc. |
findMaxRangeShot(BcShot) |
Maximum range and corresponding angle. |
getCorrection(distanceFt, offsetFt) |
Angular correction (rad) for a linear offset. |
calculateEnergy(grains, fps) |
Kinetic energy (ft-lb). |
calculateOgw(grains, fps) |
Optimal Game Weight. |
Legacy BcShotProps-based overloads (findApex, findMaxRange, etc.) are retained for backwards compatibility.
Enums #
| Dart enum | Description |
|---|---|
BCLIBCFFI_TrajFlag |
Trajectory filter flags (BCLIBCFFI_TRAJ_FLAG_RANGE, BCLIBCFFI_TRAJ_FLAG_APEX, …) |
BcTerminationReason |
Why integration stopped (BcHitResult.reason). |
BcBaseTrajInterpKey |
Key field selector for integrateAtShot (.posX, .time, .mach, …). |
BcIntegrationMethod |
BcIntegrationMethod.rk4 (default) or .euler (BcShot.method). |
BcTerminationReason, BcBaseTrajInterpKey, and BcIntegrationMethod (from
lib/ffi/bclibc_types.dart) are platform-agnostic — the same types work with
both BcLibC (native) and BcLibCWeb (web). They replace the raw
ffigen-generated BCLIBCFFI_* enum classes in the public API; those still
exist in bclibc_bindings.g.dart but are native-internal implementation
detail now.
Async API #
AsyncCalculator mirrors Calculator's three methods
(barrelElevationForTarget, setWeaponZero, fire) but returns Futures,
and works on both native and web:
import 'package:dart_bclibc/bclibc.dart';
final calc = AsyncCalculator();
final result = await calc.fire(
shot: shot,
trajectoryRange: Distance.meter(1000),
);
- On native, each call runs on a fresh isolate via
Isolate.run, so heavy trajectory integration doesn't block the caller's isolate (e.g. the UI isolate in a Flutter app). - On web, the wasm engine is loaded once (cached) and every call after that runs synchronously through it — there's no isolate to offload to on web, and once the module is loaded, wasm calls are plain synchronous JS calls.
Calculator itself (the synchronous class) stays native-only — dart:ffi
doesn't exist on web, so there's no web equivalent of it. Web consumers use
AsyncCalculator.
Web / WebAssembly #
bclibc's C ABI compiles to WebAssembly via bclibc/build_wasm.sh
(Emscripten), and is consumed on web through BcLibCWeb
(lib/ffi/bclibc_ffi_web.dart) using dart:js_interop directly against the
same flat BCLIBCFFI_* exports the native binding uses — no Embind, no
third-party FFI-on-web shim. Struct field offsets are never hardcoded on the
Dart side: BCLIBCFFI_get_layout() computes them via offsetof()/sizeof()
in whichever compiler built the wasm module, so the binding can't silently
drift from the C struct layout if it changes.
The compiled artifact (assets/wasm/bclibc_ffi.js + .wasm) ships with the
package via flutter.assets in pubspec.yaml — flutter build web picks
it up automatically, no extra setup needed in the consuming app.
import 'package:dart_bclibc/bclibc.dart';
// Works unmodified on web — AsyncCalculator picks the wasm engine
// automatically when compiled for web.
final calc = AsyncCalculator();
final elev = await calc.barrelElevationForTarget(shot, Distance.meter(500));
package:dart_bclibc/bclibc.dart conditionally excludes the native-only
Calculator / BcLibC / generated dart:ffi bindings when compiling for
web, so importing the package barrel compiles cleanly on both platforms.
To rebuild the wasm artifact from source (only needed if you're modifying
bclibc itself):
bclibc/build_wasm.sh # self-installs a pinned Emscripten SDK on first run
cp bclibc/build/web/bclibc_ffi.{js,wasm} assets/wasm/
Atmosphere and Coriolis #
When using BcShot:
- Pass
pressureHpa: 0for vacuum (zero drag). - Pass
latitudeDeg: double.nanto disable Coriolis (defaultBcShotvalue). - Pass
azimuthDeg: double.nanfor flat-fire drift only.
Unit system #
lib/unit.dart provides typed unit wrappers with an in_() / toDouble() API:
import 'package:dart_bclibc/unit.dart';
final t = Temperature.celsius(15.0);
print(t.in_(TemperatureUnit.fahrenheit)); // 59.0
final d = Distance.meters(100.0);
print(d.in_(DistanceUnit.feet)); // 328.084...
Available types: Distance, Velocity, Temperature, Pressure, Angular, Weight, Energy.
Building #
Prerequisites #
- Dart SDK ≥ 3.11.4 (or Flutter ≥ 3.3.0)
- CMake ≥ 3.13
- C++17 compiler (GCC / Clang on Linux/macOS, MSVC 2022 on Windows)
- LLVM/Clang dev headers (only for regenerating FFI bindings)
Clone #
git clone --recurse-submodules https://github.com/ballistics-lab/dart-bclibc.git
cd dart-bclibc
If you cloned without --recurse-submodules:
git submodule update --init
Build native library #
make build
Or manually:
cmake -S bclibc -B build/bclibc -DCMAKE_BUILD_TYPE=Release
cmake --build build/bclibc --parallel
For Flutter apps the native library is built and bundled automatically by
flutter build. No changes to the app's platformCMakeLists.txtare required. On Linux the plugin registersinstall(TARGETS bclibc_ffi LIBRARY)rules that produce the standard versioned-file + soname + namelink layout in the bundle. On Windows the DLL is declared viaPLUGIN_BUNDLED_LIBRARIESand installed by the app's existing loop;add_dependencies(flutter_assemble bclibc_ffi)ensures correct build order. Android is handled by Gradle/AGP without anyinstall()rules.
Consuming apps: flutter test / dart test
flutter build/flutter run bundle the native library automatically, but
flutter test/dart test never run a platform build, so apps that depend on
dart_bclibc need to build it explicitly before testing:
dart run dart_bclibc:build_native
This builds libbclibc_ffi into build/bclibc/ relative to your project's
working directory — one of the paths BcLibC.open() checks automatically.
Wire it into your test target, e.g. in a Makefile:
test: build-bclibc
flutter test
build-bclibc:
dart run dart_bclibc:build_native
Regenerate FFI bindings #
# Install LLVM first:
# Linux: sudo apt install libclang-dev clang
# macOS: brew install llvm
# Windows: winget install LLVM
make ffigen
The generated file is lib/ffi/bclibc_bindings.g.dart.
Run tests #
make test
Native library #
| Platform | Library name |
|---|---|
| Linux | libbclibc_ffi.so |
| Android | libbclibc_ffi.so |
| macOS | libbclibc_ffi.dylib |
| iOS | libbclibc_ffi.dylib |
| Windows | bclibc_ffi.dll |
| WASM | bclibc_ffi.wasm |
During development the path can be overridden via the BCLIBC_FFI_PATH environment variable.
Dependencies #
| Dependency | Role |
|---|---|
| bclibc v1.1.6 | C++ ballistic solver engine (3-DOF + spin drift, RK4) — LGPL-3.0, bundled as a git submodule |
| ffi | Dart ↔ C FFI bindings |
| plugin_platform_interface | Flutter plugin platform interface |
| web | dart:js_interop-based DOM bindings, used by BcLibCWeb to load the wasm module |
License #
Copyright (C) 2026 Yaroshenko Dmytro (o-murphy)
This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License v3.0 as published by the Free Software Foundation.
See LICENSE for the full text. See CHANGELOG for release history.
Note
bclibc (the ballistic solver engine, located in bclibc/) is licensed separately under the GNU Lesser General Public License v3.0. See bclibc/LICENSE.
Warning
Risk notice. This package performs approximate simulations of complex physical processes. Calculation results must not be considered as completely or reliably reflecting actual projectile behaviour. Results may be used for educational purposes only and must not be relied upon in any context where an incorrect calculation could cause financial harm or put a human life at risk.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.