# E440 arc-dial scale generator — design ## Summary Pivot the repository from generating M1730 **linear-strip** meter scales to generating **E440-style round-dial** meter scales, matching the hand-drawn reference `e440.svg`. This is a full replacement, not an added mode: the M1730 linear generator, its three SVG files, and its infra naming are all retired and replaced by E440 equivalents. Feature parity is kept wherever it translates naturally (scale mappings, output modes, custom labels, unit markup) but re-expressed in polar terms (angle/radius) instead of Cartesian (x-position). ## Case geometry (fixed constants) The physical dial body, exactly as drawn in `e440.svg`, does not change between generated scales — only the tick/label/text content does. This mirrors how M1730's background panel rectangle was a fixed constant. - Canvas: 93.824249 × 57.215687 mm, `layer1` transform `translate(-60.717741,-107.14126)` (same convention as M1730: constants are expressed in this pre-translate local frame). - Body outline + two mounting ears + rim cutout: the existing `` in `e440.svg`, carried over unchanged. - Pivot center: local `(107.42845, 162.62819)`. Small gray guide circle (r≈1.73mm) at the pivot is kept as-is. - Rim / tick-base radius `R0 = 43.842751` mm — ticks start here and extend outward. - Angular sweep: `[-45.667°, +45.667°]` measured from vertical (north), negative = left/low end, positive = right/high end, clockwise-positive (matches the reference: 0 at the left end, 50 at the right end). ## Tick generation (parametrized, 3 tiers) Same conceptual parameters as the M1730 generator (`big_ticks`, `small_ticks`, `label_count`), reinterpreted along the arc: - **Major ticks** — one per major interval boundary (`big_ticks + 1` of them), evenly spaced in angle across the sweep. Radial length 4.7839mm, stroke-width 0.7966. These are the ones eligible for numeric labels. - **Medium tick** — appears **only when `small_ticks` is odd**, at the exact midpoint subdivision of each major interval (e.g. `small_ticks=9` → 10 subdivisions → the 5th is the midpoint). Radial length 3.1216mm, stroke-width 0.3799. This matches the reference exactly (values 5, 15, 25, 35, 45 get a medium tick between the 0/10/20/30/40/50 majors). - **Small ticks** — every other subdivision. Radial length 1.8122mm, stroke-width 0.2748. - If `small_ticks` is even, there's no exact midpoint — all minor subdivisions render as small ticks (2-tier fallback), no medium tier. Each tick is emitted as a `` from `(R0, angle)` to `(R0+length, angle)` in polar coordinates around the pivot (as `` elements, not rotated ``s like the hand-drawn file — simpler and consistent with how M1730's code already draws ticks as lines rather than filled rotated boxes). Minor-tick angular placement under each scale mapping (linear/√/log/1/x) carries over directly from the M1730 code, substituting angle for x. ## Labels Numeric labels are positioned at radius ≈49.86mm (just beyond the major tick tips) and **rotated to match their tick's angle** — following the arc, as in the reference (confirmed: "rotated/radial" was the chosen orientation). Default label font size ≈5.11mm (kept configurable, like `label_size` today). Label placement/count logic (`label_count`, `custom_labels`, `_fmt()`) is an unchanged port from the M1730 code. ## Unit label and corner text - **Unit label** (e.g. `uA`): centered above the arc at local `(107.035, 136.646)`, horizontal (not rotated), default size ≈9.47mm. Markup support (`^`, `_`, `{a/b}`) is preserved — same parser as today. - **Corner labels** replace M1730's single `range_label` / colored bar (which E440 doesn't have): two independent fields, **left** and **right**, each up to 2 lines, anchored at the reference's `text58`/`text61` positions (bottom-left start-anchored, bottom-right end-anchored, ≈3.57mm font size, ≈4.46mm line spacing). A field's second line is only drawn if provided; no truncation beyond 2 lines (extra input past line 2 is ignored). ## Fonts Single font selector, unchanged mechanism from today (any `.ttf` dropped in `fonts/` is auto-discovered and offered in the dropdown). Default font becomes `alte-din-1451-mittelschrift.regular` (already present in `fonts/`). The `.gepraegt` (embossed) variant and the existing GOST-family fonts remain selectable but are not auto-paired to any specific element — whatever the user picks in the dropdown is used for unit, labels, and corner text alike, exactly like the current single-selector behavior. ## Scale mappings, output modes, custom labels All four mappings (linear, √, log, 1/x) are kept, each reusing its existing `value_to_x`/`major_value` math from `web/app.py`, renamed conceptually to `value_to_angle`. Dual output mode (path-outlines vs. embedded live text) and custom label override are both kept unchanged in behavior, just applied to the new geometry. ## File deliverables - `e440.svg` → renamed/tidied into `E440-Scale_template.svg` (update `sodipodi:docname`, keep as the Inkscape master with the same reference values: unit `uA`, range 0–50, `Text`/`Left` and `Text`/`Right` corner placeholders). - `E440-Scale_paths.svg` — same content with all text converted to outline ``s via fontTools (mirrors how `M1730-Scale_paths.svg` was derived). - `E440-Scale_text.svg` — same content with the font embedded as base64 `@font-face`, `sodipodi:docname` set to `E440-Scale.svg` (the "final named file", matching the old convention). - Old `M1730-Scale_template.svg`, `M1730-Scale_paths.svg`, `M1730-Scale_text.svg` are deleted. ## `web/app.py` changes - `generate_svg(...)` rewritten around polar geometry: new constants (`PIVOT_X`, `PIVOT_Y`, `R0`, `ANGLE_MIN`, `ANGLE_MAX`, tick lengths/widths per tier, label radius) replace the linear `ZERO_X`/`MAX_X`/`BAR_Y`/tick height constants. - `range_label` parameter replaced by `left_label` / `right_label` (each a string, `\n`-split into up to 2 lines). - Body/case SVG markup (the fixed `` for the dial body + pivot guide circle) is emitted as a fixed literal, same pattern as today's fixed background ``. - Existing helper functions (`_glyph_svg`, `_measure`, `_parse_markup`, `_text_to_paths`, `_fmt`, the fraction/sup/sub renderer) are reused unchanged — they're geometry-agnostic. - Form (`web/templates/index.html`) fields updated: drop "Range label", add "Left label" / "Right label" (2-line text areas), everything else (unit, min/max, mapping, major/minor tick counts, label count/size, custom labels, font, output mode) stays. ## Rebranding Full rename to E440 branding across infra and docs: - Docker image tag `m1730-scale` → `e440-scale`. - `k8s/ingress.yaml` route `works.baumann.gr/M1730-generator` → `works.baumann.gr/E440-generator` (and its stripPrefix middleware). - `README.md` and `CLAUDE.md` rewritten to describe the E440 arc-dial project: new file roles, new coordinate constants, new tick-tier rules, new default font, new form fields. ## Testing / verification No automated test suite exists in this repo (consistent with "no build, lint, or test tooling" in CLAUDE.md). Verification is: run the Flask app locally, generate a scale reproducing the exact reference parameters (unit `uA`, 0–50, 5 major intervals, 9 minor per interval, labels every major, corner texts `Text`/`Left` and `Text`/`Right`), and visually diff the output against `E440-Scale_template.svg` (tick angles/positions, label placement, corner text placement) to confirm the geometry model is correct before considering the port done.