# Geodesic Flow -- Live Desktop Wallpaper

[![CI](https://github.com/Mattbusel/geodesic-wallpaper/actions/workflows/ci.yml/badge.svg)](https://github.com/Mattbusel/geodesic-wallpaper/actions/workflows/ci.yml)
[![Release](https://github.com/Mattbusel/geodesic-wallpaper/actions/workflows/release.yml/badge.svg)](https://github.com/Mattbusel/geodesic-wallpaper/actions/workflows/release.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Rust 1.75+](https://img.shields.io/badge/rust-1.75%2B-orange.svg)](https://www.rust-lang.org)

A real-time animated desktop wallpaper for Windows that renders families of
geodesics flowing across parameterized surfaces. Geodesics are integrated with
a fourth-order Runge-Kutta (RK4) scheme using analytically computed Christoffel
symbols. The window sits below all application windows via a Win32
WM_WINDOWPOSCHANGING hook, leaving desktop icons fully accessible.

Built with Rust, wgpu (DirectX 12 / Vulkan / Metal), and the windows crate for
Win32 integration.

---

## Download

Head to the [Releases](../../releases/latest) page and download
`geodesic-wallpaper-windows.zip`. Extract it alongside `config.toml` and
run `geodesic-wallpaper.exe`. No installer required.

**Requirements:** Windows 10 or 11, any GPU with DirectX 12 or Vulkan support.

---

## Architecture

| Module | File | Responsibility |
|--------|------|----------------|
| `config` | `src/config.rs` | Load and hot-reload `config.toml`; parse CSS hex colours |
| `error` | `src/error.rs` | Typed error enum covering all subsystems |
| `surface` | `src/surface/` | `Surface` trait + `Torus`, `Sphere`, `Saddle` implementations |
| `geodesic` | `src/geodesic.rs` | RK4 integrator for the geodesic ODE using Christoffel symbols |
| `trail` | `src/trail.rs` | Fixed-capacity ring buffer with quadratic alpha fade |
| `renderer` | `src/renderer/` | wgpu render pipelines (surface wireframe + trail lines) |
| `renderer::camera` | `src/renderer/camera.rs` | Orbiting perspective camera |
| `wallpaper` | `src/wallpaper.rs` | Win32 borderless window pinned below all app windows |
| `main` | `src/main.rs` | Application entry point, message loop, hot-reload watcher |

```
config.toml
    |
    v
+--------+     hot-reload      +------------------+
| Config | -----------------> | SharedConfig      |
+--------+   (notify watcher) | Arc<RwLock<...>>  |
                               +------------------+
                                        |
                                        v
                        +-----------------------------+
                        | GeodesicEngine              |
                        | - Surface trait             |
                        |   (Torus / Sphere / Saddle) |
                        | - Geodesic: RK4 integrator  |
                        | - Christoffel symbols       |
                        +-----------------------------+
                                        |
                                        v
                        +-----------------------------+
                        | TrailRenderer               |
                        | - TrailBuffer (ring buffer) |
                        | - quadratic alpha fade      |
                        +-----------------------------+
                                        |
                                        v
                              +-----------------+
                              | wgpu pipelines  |
                              | (surface mesh + |
                              |  trail lines)   |
                              +-----------------+
                                        |
                                        v
                        +-------------------------------+
                        | Win32 WorkerW window          |
                        | (HWND_BOTTOM, WS_POPUP,       |
                        |  WM_WINDOWPOSCHANGING locked) |
                        +-------------------------------+
```

---

## Building from source

**Requirements:** Rust stable (1.75 or later), Windows 10 or 11, a GPU with
DirectX 12, Vulkan, or Metal support.

```powershell
git clone https://github.com/Mattbusel/geodesic-wallpaper.git
cd geodesic-wallpaper
cargo build --release
.\target\release\geodesic-wallpaper.exe
```

Place `config.toml` in the same directory as the executable. The application
reloads the file automatically whenever it changes on disk. Press Ctrl+C in the
terminal or close the process to stop.

To run tests (no GPU required):

```powershell
cargo test --lib
```

---

## Configuration reference

All fields are optional. Missing fields revert to the defaults listed below.

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `surface` | string | `"torus"` | Surface to render: `"torus"`, `"sphere"`, or `"saddle"` |
| `num_geodesics` | integer | `30` | Number of simultaneous geodesic curves |
| `trail_length` | integer | `300` | Frames a trail persists before respawning |
| `rotation_speed` | float | `0.001047` | Camera orbit speed in radians per second |
| `color_palette` | string[] | 5 entries | CSS hex colour strings cycled across geodesics |
| `torus_R` | float | `2.0` | Torus major radius (centre to tube centre) |
| `torus_r` | float | `0.7` | Torus minor radius (tube radius) |
| `time_step` | float | `0.016` | RK4 integration timestep in seconds per frame |

```toml
# Surface to render.
# Accepted values: "torus", "sphere", "saddle".
# Any other value falls back to "torus".
# Default: "torus"
surface = "torus"

# Number of simultaneous geodesic curves.
# Default: 30
num_geodesics = 30

# Number of frames a trail persists before being respawned.
# Default: 300
trail_length = 300

# Camera orbit angular speed in radians per second.
# 0.001047 is approximately one full revolution every 100 minutes.
# Default: 0.001047
rotation_speed = 0.001047

# Trail color palette as CSS hex strings (with or without leading '#').
# Geodesics cycle through the list in order.
# Default: five entries from blue to gold to pink.
color_palette = ["#4488FF", "#88DDFF", "#FFD700", "#88FF88", "#FF88CC"]

# Torus major radius: distance from the center of the torus to the center
# of the tube.
# Default: 2.0
torus_R = 2.0

# Torus minor radius: radius of the tube.
# Default: 0.7
torus_r = 0.7

# RK4 integration time step in seconds per frame.
# Values above 0.02 can cause visible trajectory drift on a small torus.
# Default: 0.016
time_step = 0.016
```

---

## How it works

A geodesic on a Riemannian surface is a curve whose acceleration has no
tangential component; equivalently, it satisfies the geodesic equation
d^2x^k/dt^2 + Gamma^k_ij (dx^i/dt)(dx^j/dt) = 0, where Gamma^k_ij are the
Christoffel symbols of the metric. Each symbol is computed analytically from
the first and second fundamental forms of the surface, avoiding finite-difference
approximations and the numerical noise they introduce. For the torus the metric
is diagonal (the parameterization is orthogonal), which collapses the Christoffel
computation to two non-zero components. The sphere and saddle use the full
symmetric formula.

The ODE is advanced with a classical fourth-order Runge-Kutta integrator. At
each frame the integrator evaluates the Christoffel symbols at four intermediate
positions and combines the results with the standard 1/6-2/6-2/6-1/6 weights.
After each step the velocity is renormalized to unit metric speed
(g_ij du^i du^j = 1) to suppress the slow drift caused by floating-point
truncation over hundreds of frames. This is correct because the geodesic
equation preserves metric speed exactly; renormalization merely restores the
invariant that the integrator breaks weakly.

Each geodesic leaves a trail stored in a fixed-capacity ring buffer. When a new
position is pushed, the oldest is silently overwritten. At render time the buffer
is iterated from oldest to newest and each vertex is assigned an alpha value
equal to (i / N)^2, producing a quadratic fade from transparent at the tail to
opaque at the head. The ring buffer never grows or reallocates after
initialization, so the per-frame allocation pressure is zero.

---

## Supported surfaces

| Surface | Curvature | Behavior |
|---------|-----------|----------|
| torus   | Mixed (positive outer rim, negative inner rim) | Geodesics diverge on the inner rim and focus on the outer |
| sphere  | Constant positive | All geodesics are great circles |
| saddle  | Constant negative | Geodesics diverge exponentially |

---

## Contributing

1. Fork the repository.
2. Create a feature branch: `git checkout -b my-feature`.
3. Ensure `cargo fmt`, `cargo clippy -- -D warnings`, and `cargo test` all pass.
4. Open a pull request against `master`.

CI enforces formatting, Clippy warnings-as-errors, the full test suite, and a
release build before merging.

---

## License

MIT -- see [LICENSE](LICENSE) for details.
