summaryrefslogtreecommitdiff
path: root/AGENTS.md
diff options
context:
space:
mode:
Diffstat (limited to 'AGENTS.md')
-rw-r--r--AGENTS.md134
1 files changed, 134 insertions, 0 deletions
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..f8992c7
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,134 @@
+# AGENTS.md — gipfel-rs
+
+Guidance for AI coding agents working in this repository.
+
+## What this is
+
+`gipfel-rs` is a Rust port of **gipfel** (a mountain-photo annotator: you load a
+photo, mark known peaks, and it fits the camera viewpoint + lens distortion from
+the hill database). The original is C++/FLTK (`https://git.flpsed.org/gipfel`,
+cloned at `~/gipfel`). This port targets **Cosmic DE** (libcosmic) on Linux.
+
+The port is organized as milestones (M1 core → M5 polish); see `~/gipfel/plan.md`
+for the full plan and `~/gipfel-spikes/MEMO.md` for early UI-spike decisions.
+
+## Workspace layout
+
+```
+crates/gipfel-core/ headless computational core (no GUI, no native C deps)
+ hill.rs hill DB: CSV load (Latin-1), duplicate marking
+ panorama.rs viewpoint, geodesy, refraction, occlusion, visibility
+ projection/ rectilinear + cylindrical projections
+ generated/ maxima-generated symbolic functions (DO NOT hand-edit)
+ lsq.rs pure-Rust Levenberg-Marquardt fit (was GSL; GSL removed)
+ metadata.rs `gipfel:` JPEG comment + EXIF read/write
+ profile.rs distortion profiles (FLTK prefs format, XDG data dir)
+ stitch.rs cylindrical panorama resampling (ScanImage/Stitch port) + TIFF/JPEG out
+ track.rs GPX track parser (hardened: no XXE, size cap)
+crates/gipfel-cli/ CLI: export-position, export-hills, info, stitch, profiles
+crates/gipfel-app/ libcosmic GUI app (binary name: `gipfel`)
+ main.rs app state + Elm-style update/view + sidebar
+ viewport.rs custom canvas Widget (image + markers + labels)
+ compass.rs compass-strip orientation widget
+ dump.rs off-screen image renderer (screen dump)
+codegen/ maxima scripts + regen.sh (regenerates projection/generated/)
+tests/ C++ reference harnesses (.cxx) + golden files
+```
+
+## Build & test
+
+```sh
+cargo build --release # app binary: target/release/gipfel
+cargo test --lib --test golden # unit + golden tests (the main gate)
+cargo test --workspace # everything incl. app-crate tests
+cargo clippy --workspace --all-targets
+```
+
+- **Rust edition 2021.** No pinned toolchain file.
+- **No native C dependencies** since GSL was replaced by a pure-Rust LM. The only
+ system libs are what libcosmic/wgpu need (Vulkan/GL, fontconfig).
+- The dev environment has **no reachable display server** — the GUI app compiles
+ but cannot be run here. Runtime UI verification is done by the user on a real
+ Wayland desktop. Do not assume you can launch `gipfel` to test it.
+
+## Golden tests — the critical invariant
+
+`crates/gipfel-core/tests/golden.rs` compares output against files in
+`tests/golden/` produced by the **original C++ build**. This is the contract that
+keeps the math faithful.
+
+- Fit tests (`fit_rectilinear_matches_c`, `fit_cylindrical_matches_c`): fitted
+ params within **1e-6 relative**, final projected coords within **1e-9 relative**.
+- Function-grid tests: ~1e-15 agreement on the maxima-generated derivatives.
+- Several golden tests **skip silently** if the hill DB is missing. They look for
+ the data file via `$GIPFEL_DATA`, then `$HOME/gipfel/gipfel.dat`. Set
+ `GIPFEL_DATA=/path/to/gipfel.dat` so they actually run (a skipped golden test
+ is a green test that verified nothing).
+- **Never edit `tests/golden/*` to make a failing test pass.** They are ground
+ truth from C++. If a golden test fails, your change altered behavior — fix the
+ code or understand the deviation.
+
+Regenerating goldens (only when the C++ reference changes):
+`tests/gen_golden.sh` — needs the built C++ tree (`$GIPFEL_SRC`, default
+`~/gipfel/src`), `$GIPFEL_DATA`, g++, GSL, maxima. The `.cxx` harnesses still use
+C++ GSL; that is intentional (they are the reference, not part of the Rust build).
+
+## Code generation (maxima)
+
+`crates/gipfel-core/src/projection/generated/*_funcs.rs` are **generated** from
+`codegen/lsq_{rectilinear,cylindrical}.mac` via `codegen/regen.sh` (needs maxima;
+optional rustfmt). The mathematical models (refraction, cone-visibility,
+distortion) and their symbolic derivatives are **frozen** — do not change the
+models. If you regenerate, re-run the golden tests immediately.
+
+## Hard compatibility constraints
+
+- **`gipfel.dat` CSV format** and the **`gipfel:` JPEG comment parameter string**
+ must stay byte-compatible with the C++ version (keys `nick`/`tilt` are kept in
+ the file even though the code renamed them to pitch/roll).
+- **Hill indices are stable handles.** `Panorama::hills` keeps insertion order;
+ `mark_duplicates` sorts an index permutation, never the vec. Track lists and
+ known-hill sets index into `hills`. Do not reorder `hills` in place.
+- **Terminology:** UI says "Pitch"/"Roll"; internal fields are `a_pitch`/`a_roll`;
+ hill elevation is `a_elev` (distinct from camera pitch). JPEG keys stay
+ `nick`/`tilt`.
+- Distortion profiles live in `$XDG_DATA_HOME/gipfel/distortion-profiles.prefs`
+ (FLTK prefs format, legacy auto-import). Keep the `%g`-style float formatting
+ (`profile::fmt_g`) matching C.
+
+## Code conventions
+
+- **Elm architecture** in the app: `update(&mut self, Message) -> Task` is one
+ flat, **exhaustive** `match` (no catch-all) — keep it exhaustive so new
+ `Message` variants are forced to be handled. Prefer extracting small helper
+ methods over a catch-all arm.
+- Shared helpers to reuse (don't duplicate): `slider_item()`, `pick_open`/
+ `pick_save` (file dialogs), `compass::newton_inverse` (scalar inverse
+ projection), `viewport::layout_label_y` (label layout shared by screen + dump).
+- Sidebar rows use `settings::item_row` with fixed-width labels when fields must
+ align to equal widths (see the Distortion section).
+- libcosmic is **pinned to git rev `430f3d4`** (see `crates/gipfel-app/Cargo.toml`)
+ and vendors iced 0.14.2. The correct nav-bar return type is
+ `cosmic::Action<Message>` (crate root), not `cosmic::app::Action`.
+- Rendering gotchas: the wgpu renderer draws in fixed layer order (use
+ `start_layer`/`end_layer` to force overlays above the image); keep strong
+ `Plain<P>` paragraph refs alive across the frame in widget `Tree` state.
+
+## Platform notes
+
+- **Linux / Cosmic first.** No Windows/macOS support yet.
+- A standalone Windows build is an open experiment. The main blocker (GSL) is
+ already removed. Remaining work: swap libcosmic's `xdg-portal` feature for
+ `rfd` (file dialogs), port the hill-DB search path (`/usr/share`, `$HOME` →
+ `dirs` + exe-relative) and font fallback, then get libcosmic itself to compile
+ for `x86_64-pc-windows-gnu` (upstream does not test Windows).
+
+## Do / Don't
+
+- ✅ Run `cargo test --lib --test golden` (with `GIPFEL_DATA` set) after any
+ change to the core math, projection, fit, metadata, or stitch code.
+- ✅ Keep the build warning-free (rustc `dead_code`/`unused_*` lints are the
+ signal; clippy may not be installed in every environment).
+- ❌ Don't hand-edit `projection/generated/` or `tests/golden/`.
+- ❌ Don't change the mathematical models or file formats.
+- ❌ Don't assume the GUI can be launched in this environment.