Metadata-Version: 2.3
Name: lutopia
Version: 0.2.0
Summary: Tool-agnostic LUT and colormap management for Python
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.10
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# lutopia

**Tool-agnostic LUT and colormap management for Python.**

Lutopia treats every palette — smooth gradient or legacy 256-step table — as a single unified continuous function. One API, zero fragmentation across visualization ecosystems.

```python
import lutopia as lt

# Matplotlib
from matplotlib.colors import ListedColormap
cmap = ListedColormap(lt.viridis.plt)

# Seaborn
sns.heatmap(data, cmap=lt.viridis.sns)

# Plotly
fig.update_traces(colorscale=lt.viridis.plotly)
```

---

## Features

- **297 palettes out of the box** — matplotlib's full catalog + the entire colorcet scientific collection
- **Unified engine** — smooth gradients and legacy step-function LUTs share one interface
- **Zero visualization dependencies** — runtime package requires only `numpy` and `scipy`
- **Native consumer outputs** — `.plt`, `.mpl`, `.sns`, `.plotly` return exactly what each library expects, no adapters needed
- **Interactive palette picker** — `uvx lutopia` opens a live GUI browser with one-click code generation
- **`src` layout, `uv_build` backend** — modern packaging, ships a clean wheel

---

## Installation

```bash
pip install lutopia
# or
uv add lutopia
```

---

## Quick Start

```python
import lutopia as lt

# List all available palettes
print(dir(lt))  # ['accent', 'afmhot', 'autumn', ..., 'viridis', ...]

# Sample raw float values (N × 3 array, values in [0, 1])
arr = lt.viridis.sample(256)   # numpy array, shape (256, 3)

# Get hex strings
hexes = lt.plasma.to_hex_list(64)  # ['#0d0887', '#2d0594', ...]
```

---

## Consumer Properties

Every palette exposes three ready-to-use consumer properties. No third-party imports are required by the library itself.

### Matplotlib

```python
from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt
import lutopia as lt

cmap = ListedColormap(lt.inferno.plt)
plt.imshow(data, cmap=cmap)
plt.colorbar()
```

`.plt` / `.mpl` / `.matplotlib` all return the same `(256, 3)` float64 NumPy array that `ListedColormap` and `LinearSegmentedColormap` accept directly.

### Seaborn

```python
import seaborn as sns
import lutopia as lt

sns.heatmap(data, cmap=lt.magma.sns)
```

`.sns` / `.seaborn` return a list of 256 hex strings, which Seaborn accepts for both `palette=` and `cmap=` arguments.

### Plotly

```python
import plotly.graph_objects as go
import lutopia as lt

fig = go.Figure(go.Heatmap(z=data, colorscale=lt.turbo.plotly))
```

`.plotly` returns a native Plotly colorscale — a list of `[fraction, "#rrggbb"]` pairs spanning `[0.0, 1.0]`.

---

## Palette Browser

The interactive palette picker is available as an ephemeral tool — no install needed:

```bash
uvx lutopia
# or, if already installed:
lutopia
```

A tkinter window opens showing all 297 palettes as color swatches. Click any palette to open a code snippet dialog:

- Choose your target ecosystem (Matplotlib, Seaborn, Plotly, or Raw Hex)
- Copy the generated snippet directly to your clipboard

---

## Available Palettes

Lutopia ships with palettes sourced from:

| Source | Count | Examples |
|--------|-------|---------|
| **Matplotlib** | ~90 | `viridis`, `plasma`, `inferno`, `magma`, `cividis`, `jet`, `hot`, `coolwarm`, `spectral`, `tab10` |
| **Colorcet** | ~200 | `fire`, `rainbow4`, `bmy`, `bgyw`, `isolum`, `gouldian`, `kbc`, `dimgray` |

All palettes are available immediately after install — no scraping, no network calls.

---

## How It Works

All palettes are stored as compact JSON anchor files in `lutopia/palettes/`. A continuous `UnifiedPalette` engine evaluates them on demand:

- **Linear interpolation** for smooth gradient maps (e.g. viridis, plasma)
- **Nearest-neighbor / step** for discrete legacy tables (e.g. ImageJ `.lut` files)

```json
{
  "name": "viridis",
  "interpolation": "linear",
  "anchors": [
    [0.0,   [68, 1, 84]],
    [0.5,   [33, 145, 140]],
    [1.0,   [253, 231, 37]]
  ]
}
```

Palettes are loaded lazily on first access and cached for the lifetime of the interpreter session.

---

## Development

### Setup

```bash
git clone https://github.com/bryanbarcelona/lutopia.git
cd lutopia
uv sync
```

### Tooling

```bash
uv run ruff check .          # lint
uv run ruff format .         # format
uv run ty check src/lutopia  # type check
uv run pytest                # tests
```

### Adding Palettes

**From matplotlib / colorcet:**

```bash
uv run python scripts/scrape_ecosystems.py
```

Reads upstream colormap libraries, reduces continuous maps to sparse anchor points (max error < 1/255), and writes JSON assets to `src/lutopia/palettes/`. Run with `--overwrite` to force regeneration.

**From a binary ImageJ / Fiji `.lut` file:**

```bash
uv run python scripts/parse_legacy_lut.py path/to/fire.lut [name]
```

Parses the standard 768-byte RGB block and writes a step-interpolation JSON asset.

After adding palettes, commit the new JSON files and they will be included in the next release.

### Releasing

Releases are fully automated via CI. Every merge to `main` triggers:

1. Commitizen bumps the version and generates a changelog entry
2. `uv build` produces the wheel and sdist
3. `uv publish` pushes to PyPI via trusted publishing (OIDC, no API keys)
4. A GitHub Release is created and tagged

Commit messages must follow [Conventional Commits](https://www.conventionalcommits.org/):

```bash
cz commit
```

---

## License

MIT
