From 375d87a9b0ec1cf2fffd705adb870c86144d2400 Mon Sep 17 00:00:00 2001 From: Johannes Hofmann Date: Mon, 26 Oct 2026 20:05:20 +0100 Subject: Replace the C++ implementation with the Rust port The Rust port (formerly gipfel-rs) is now the official successor to the FLTK/GSL C++ code base. This squashes the port into a single commit and removes the C++ sources (they remain in the git history below). Layout: crates/gipfel-core headless core: hills, panorama geometry, projections (maxima-generated derivatives), pure-Rust LM fit, gipfel: JPEG comment + EXIF I/O, profiles, stitching crates/gipfel-cli command-line tools (export, info, stitch, profiles) crates/gipfel-app Cosmic DE (libcosmic) GUI, binary name `gipfel` codegen/ maxima scripts + regen.sh for the projection code tests/ golden files + C++ reference harnesses (historical) The original README is kept for now; it will be updated to describe the Rust build. gipfel.dat, AUTHORS, ChangeLog and NEWS are unchanged. --- AGENTS.md | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 AGENTS.md (limited to 'AGENTS.md') 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` (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

` 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. -- cgit v1.2.3