An open API service indexing awesome lists of open source software.

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

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. |