colorimetry 0.0.9

Rust Spectral Colorimetry library with JavaScript/WASM interfaces
Documentation
# Claude Code instructions for colorimetry

## Pre-release / CI checks

Whenever code is modified or tests are run, always execute the full xtask pipeline in this order and report any errors before marking work complete:

```
cargo xtask check   # fmt, clippy, build, rdme
cargo xtask test    # test --all-features and --no-default-features
cargo xtask doc     # rustdoc with --deny warnings
```

These three commands together are the definition of "the build is green" for this project.

## Coding conventions

- Prefer editing existing files over creating new ones.
- Do not add docstrings, comments, or type annotations to code that was not changed.
- Private helper functions (e.g. `rf_from_de`, `N_ANGLE_BIN`) must not be linked
  from public doc comments — rustdoc rejects them with `--deny warnings`. Use plain
  backtick notation instead: `` `rf_from_de` ``.
- Feature-gated code must compile both with and without the feature. The xtask test
  command runs both `--all-features` and `--no-default-features`.

## Reference standards

CIE and IES standard documents are stored locally in `standards/` (gitignored — never committed to
the repo). The PDFs are purchased standards that cannot be redistributed; each developer must
obtain their own copy. When implementing or verifying anything against a standard, read the
relevant document from that directory. Key documents to look for:

- `standards/Colorimetry, 4th Edition (CIE15-2018).pdf` — CIE 15:2018 Colorimetry
  (available for purchase at cie.co.at)
- `standards/CIE 2017 Colour Fidelity Index for accurate scientific use (CIE224-2017).pdf` — CIE 224:2017
  Colour Fidelity Index for accurate scientific use (available for purchase at cie.co.at)
- `standards/TM-30-20-E1.pdf` - ANSI/IES TM-30-20 Technical Memorandum: IES Method for evaluating light source color rendition (available for purchase at <https://store.ies.org/product-category/science>).

Add new documents to `standards/` and list them here (with purchase URL) as they are acquired.

## Domain context

- The library implements CIE and ANSI/IES colorimetry standards.
- The colorimetric calculations should be implemented in accordance with the CIE 15 standard, in particular CIE 15:2004 and CIE 15:2018.
- CFI (Color Fidelity Index) follows **CIE 224:2017** / **ANSI/IES TM-30-20/24**.
  - 99 CES samples, 16 hue bins, CIECAM02-UCS J'a'b' colour space.
  - Bin assignment always uses the **reference-source** hue angle, not the test-source.
  - Per-bin metrics: Rf,hj (fidelity), Rcs,hj (chroma shift fraction), Rhs,hj (hue shift, rad).
- Tolerances for per-bin tests are intentionally wide (±10 Rf, ±0.05 Rcs, ±0.04 rad)
  because a single sample near a bin boundary can shift a bin centroid significantly.
- Reference data for tests: IES TM-30 Spectral Calculator (ies.org) for F12;
  LuxPy (`spd_to_ies_tm30_metrics`) for F1/F2 as a baseline only.

## Release process

This is the complete checklist for cutting a new release. All published artifacts must carry the
same version number: the three Rust crates on crates.io, the `colorimetry` npm package (WASM), and
the GitHub release tag.

### 1. Update CHANGELOG.md

Move every entry under `## [Unreleased]` into a new dated section, e.g.:

```markdown
## [0.0.9] - 2026-04-20
```

Add a diff link at the bottom of the file following the existing pattern. All three changelogs
(`CHANGELOG.md`, `cli/CHANGELOG.md`, `plot/CHANGELOG.md`) should be updated if they contain
unreleased entries.

### 2. Bump version numbers

All of the following must change from the old version to the new version in **one commit**:

| File | What to change |
|---|---|
| `Cargo.toml` | `[workspace.package] version` |
| `Cargo.toml` | `[workspace.dependencies] colorimetry` version pin |
| `pkg/package.json` | `"version"` field (npm / WASM package) |
| `README.md` | any `colorimetry = "x.y.z"` install snippets |
| `cli/README.md` | any version-pinned install snippets |
| `plot/README.md` | any version-pinned install snippets |

The three Rust crates (`colorimetry`, `colorimetry-plot`, `colorimetry-cli`) inherit version via
`version.workspace = true` — only `Cargo.toml` at the workspace root needs to change.

### 3. Run the full xtask pipeline

```sh
cargo xtask check   # fmt, clippy (-D warnings), build, rdme
cargo xtask test    # --all-features and --no-default-features
cargo xtask doc     # rustdoc with --deny warnings
```

All three must pass with zero errors and zero warnings before proceeding.

### 4. Rebuild the WASM package

```sh
cargo xtask wasm    # regenerates pkg/ via wasm-pack + wasm-opt
```

Commit any changes to `pkg/` (`.wasm`, `.js`, `.d.ts` files) together with the version bump commit,
or as a follow-up commit before tagging.

### 5. Commit and tag

```sh
git add -p          # stage version bumps, CHANGELOG, pkg/ changes
git commit -m "chore: release v0.0.9"
git tag v0.0.9
git push origin main --tags
```

The tag triggers the GitHub release. Create a GitHub Release from the tag (via the web UI or
`gh release create v0.0.9 --notes-from-tag`) and paste the relevant CHANGELOG section as the
release notes.

### 6. Publish to crates.io

Publish in dependency order (core library first, then dependents):

```sh
cargo publish -p colorimetry
# wait for crates.io to index it (usually ~30 seconds), then:
cargo publish -p colorimetry-plot
cargo publish -p colorimetry-cli
```

### 7. Publish to npm

```sh
cd pkg
npm publish --access public
cd ..
```

Verify the new version appears on npmjs.com before closing the release.

### Per-PR changelog and deprecation notes

- `CHANGELOG.md` should be updated for any user-facing change, not just at release time.
- The version in `Cargo.toml` follows semver; the library is pre-1.0 so breaking changes are
  allowed but must be noted in the changelog.
- Deprecated items use `#[deprecated(since = "x.y.z", note = "use X instead")]`.

## Tests

- Use `assert!(approx::abs_diff_eq!(got, want, epsilon = X), "bin {j}: got {got}, want {want}")`
  rather than `assert_abs_diff_eq!` with a message argument (the macro does not accept one).
- Tests that depend on optional features must be gated with `#[cfg(feature = "...")]`.
- Do not mock internals. Tests hit real computation paths.