https://github.com/o-murphy/bcrg
Simple lua-based engine to generate dynamic ballistics reticles
https://github.com/o-murphy/bcrg
ballistics framebuf lua luascript python python3 reticles
Last synced: 3 months ago
JSON representation
Simple lua-based engine to generate dynamic ballistics reticles
- Host: GitHub
- URL: https://github.com/o-murphy/bcrg
- Owner: o-murphy
- License: lgpl-3.0
- Created: 2024-05-29T12:54:21.000Z (almost 2 years ago)
- Default Branch: master
- Last Pushed: 2025-09-16T19:43:58.000Z (6 months ago)
- Last Synced: 2025-09-16T22:12:12.504Z (6 months ago)
- Topics: ballistics, framebuf, lua, luascript, python, python3, reticles
- Language: Lua
- Homepage: https://pypi.org/project/bcrg
- Size: 91.8 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# BCRG - Ballistic reticle generator
### Simple tool to generate dynamic ballistics reticles by .lua templates
[
**Українською**](./README.ua.md)
-----
## Installation 🚀
Install the **BCRG** ballistic reticle generator using `pip` (the standard method) or the newer **`uv`** tool (recommended for speed and efficiency).
### Using uv (Recommended)
If you have the **uv** tool installed, you can manage the package directly:
**Installation:**
```bash
uv tool install bcrg
```
**Upgrade:**
```bash
uv tool upgrade bcrg
```
### Using pip (Standard Method)
If you are using the standard Python package manager:
**Installation:**
```bash
pip install bcrg
```
**Upgrade:**
```bash
pip install --upgrade bcrg
```
-----
## Usage
### As CLI tool (Via Command Line)
Use `bcrg` or `python -m bcrg` to generate reticle images (BMP) from Lua templates:
```bash
usage: bcr [-h] [-o OUTPUT] [-f] [-W ] [-H ] [-cx ] [-cy ] [-z [ ...]] [-T | -Z] file
positional arguments:
file Reticle template file in .lua format
options:
-h, --help show this help message and exit
-o OUTPUT, --output OUTPUT
Output directory path, defaults to ./
-f, --force Force overwrite existing files without prompt
-W , --width
Canvas width (px)
-H , --height
Canvas height (px)
-cx , --click-x
Horizontal click size (cm/100m)
-cy , --click-y
Vertical click size (cm/100m)
-z [ ...], --zoom [ ...]
Zoom value (int)
-V, --version show program\'s version number and exit
archiving options:
-T, --tar Store as .tar.gz (overrides --zip)
-Z, --zip Store as .zip
```
### As Imported module
You can integrate the generator directly into your Python code:
```python
from bcrg import LuaReticleLoader
loader = LuaReticleLoader('my_reticle_template.lua')
# Create 1bit-depth .bmp bytearray
# Parameters: width, height, click_x, click_y, zoom, adjustment
byte_stream = loader.make_bmp(640, 480, 2.27, 2.27, 4, None)
with open("myreticle.bmp", 'wb') as f:
f.write(byte_stream)
```
### References
* A reticle template has to implement `make_reticle` function, that gets required arguments and has to return `self:to_bmp` or `self:to_bmp_1bit`.
* Examples in `./templates` dir.
-----
## 📐 Reticle Template API (Lua)
This section details how to create Lua templates using the **ReticleDraw** library, which extends **`FrameBuffer`**.
### 🛠️ Reticle File Structure and `make_reticle`
Every reticle file must declare a dependency on `reticledraw`
```lua
-- Load the framebuffer module
require("reticledraw") -- 👈 THIS LINE IS CRUCIAL!
```
Every reticle file must contain a single `make_reticle` function.
```lua
function make_reticle(width, height, click_x, click_y, zoom, adjustment)
-- ... Your drawing code ...
end
```
| Parameter | Type | Description |
| :--- | :--- | :--- |
| **`width`** | `number` | Framebuffer (display) width in pixels. |
| **`height`** | `number` | Framebuffer (display) height in pixels. |
| **`click_x`** | `number` | **Click Value (Correction Value)** on the X-axis (e.g., MILs/MOAs per pixel at *minimum* zoom). |
| **`click_y`** | `number` | **Click Value (Correction Value)** on the Y-axis. |
| **`zoom`** | `number` | Current scope **magnification** (zoom value). |
| **`adjustment`** | `table` | Optional additional reticle parameters/settings. |
### 🚀 General Code Template and Scaling Logic (With Examples)
The core logic converts **reticle units** (MILs/MOAs) into **screen pixels** using the scaling coefficients `ax` and `ay`.
```lua
require("reticledraw")
local BLACK = 0
local WHITE = 1
-- Rounding function (ensures the pixel value is an integer)
local function round(v)
if v < 0 then
return math.ceil(v)
elseif v > 0 then
return math.floor(v)
else
return 0
end
end
function make_reticle(width, height, click_x, click_y, zoom, adjustment)
-- 1. Calculate scaling coefficients (AX, AY)
-- AX/AY: how many reticle units correspond to 1 pixel on the screen at the current ZOOM.
local ax = click_x / zoom
local ay = click_y / zoom
-- 2. Helper functions: convert reticle units (v) to pixels
local function _x(v)
return round(v / ax)
end
local function _y(v)
return round(v / ay)
end
local fb = make_canvas(width, height, 1)
fb:fill(WHITE)
-- 3. Examples of drawing reticle elements:
-- Center dot (3x3 pixels)
fb:c_fill_rect(0, 0, 3, 3, BLACK)
-- Main horizontal line (from -100 to +100 units)
fb:c_line(_x(-100), 0, _x(100), 0, BLACK)
-- Dynamic markers: drawing lines every 10 units
local marker_length = 5 -- marker length in pixels
local marker_step = 10 -- step in reticle units (MILs/MOAs)
for i = marker_step, 50, marker_step do
-- Line up
fb:c_vline(_x(0), _y(i), marker_length, BLACK)
-- Line down
fb:c_vline(_x(0), _y(-i), marker_length, BLACK)
-- Numerical markers (using 6x6 font)
fb:c_text6(tostring(i), _x(5), _y(i), BLACK)
end
-- Dynamic detail adaptation
-- If zoom is high (ax < 0.5), draw additional markers every 5 units
if ax < 0.5 then
for i = 5, 50, 5 do
-- Small pixel dot at 5 units to the right
fb:c_pixel(_x(i), _y(-5), BLACK)
end
end
return fb
end
```
### 🎨 ReticleDraw Methods (Centered Coordinates)
These methods automatically draw relative to the display center (`0,0`). **Always use `_x()` and `_y()`** for all coordinates measured in reticle units.
| Method | Description | Example |
| :--- | :--- | :--- |
| **`fb:c_pixel(x, y, color)`** | Draws a single pixel. | `fb:c_pixel(_x(10), _y(10), BLACK)` |
| **`fb:c_line(x0, y0, x1, y1, color)`** | Draws a line. | `fb:c_line(0, 0, _x(50), 0, BLACK)` |
| **`fb:c_hline(x, y, width, color)`** | Draws a horizontal line. | `fb:c_hline(_x(-50), _y(20), _x(100), BLACK)` |
| **`fb:c_vline(x, y, height, color)`** | Draws a vertical line. | `fb:c_vline(_x(30), _y(-50), _y(100), BLACK)` |
| **`fb:c_rect(x, y, w, h, color)`** | Draws a rectangle outline (center `x, y`). | `fb:c_rect(_x(10), _y(10), 20, 20, BLACK)` |
| **`fb:c_fill_rect(x, y, w, h, color)`**| Draws a filled rectangle. | `fb:c_fill_rect(0, 0, 3, 3, BLACK)` |
| **`fb:c_circle(x, y, r, color)`** | Draws a circle outline. `r` is the radius in **pixels**. | `fb:c_circle(0, _y(30), 5, BLACK)` |
| **`fb:c_fill_circle(x, y, r, color)`** | Draws a filled circle. | `fb:c_fill_circle(0, 0, 2, BLACK)` |
| **`fb:c_text6(s, x, y, color)`** | Draws text using the **6x6** pixel font. | `fb:c_text6("10", _x(10), _y(-10), BLACK)` |
| **`fb:c_arc(x, y, rx, ry, start_angle, end_angle, color)`** | Draws an arc from `start_angle` to `end_angle` (degrees, 0° = 12 o'clock). | `fb:c_arc(0, 0, 20, 20, 0, 90, BLACK)` |
### 📐 Inherited FrameBuffer Methods (Absolute Coordinates)
These methods require **absolute pixel coordinates** (`0,0` is the top-left corner).
| Method | Coordinate Requirement | Description |
| :--- | :--- | :--- |
| **`fb:pixel(x, y, color)`** | Absolute | Sets the color of a pixel. |
| **`fb:fill(color)`** | Absolute | Fills the entire buffer. |
| **`fb:fill_rect(x, y, w, h, c)`** | Absolute | Fills a rectangle. |
| **`fb:rect(x, y, w, h, c)`** | Absolute | Draws a rectangle outline. |
| **`fb:line(x0, y0, x1, y1, color)`**| Absolute | Draws an arbitrary line. |
| **`fb:hline(x, y, w, color)`** | Absolute | Draws a horizontal line. |
| **`fb:vline(x, y, h, color)`** | Absolute | Draws a vertical line. |
| **`fb:circle(x, y, r, color)`** | Absolute | Draws a circle outline. |
| **`fb:fill_circle(x, y, r, color)`** | Absolute | Draws a filled circle. |
| **`fb:ellipse(x, y, rx, ry, color)`** | Absolute | Draws an ellipse outline. |
| **`fb:polygon(points, color)`** | Absolute | Draws a filled polygon. |
| **`fb:text(s, x0, y0, col)`** | Absolute | Draws text using the standard **8x8** pixel font. |
| **`fb:arc(cx, cy, rx, ry, start_angle, end_angle, color)`** | Absolute | Draws an arc. Angles in degrees, 0° = 12 o'clock. |