# 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 `<path id="path2">`
  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 `<line>` from `(R0, angle)` to `(R0+length,
angle)` in polar coordinates around the pivot (as `<line>` elements, not
rotated `<rect>`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
  `<path>`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 `<path>` for the dial body + pivot guide
  circle) is emitted as a fixed literal, same pattern as today's fixed
  background `<path>`.
- 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.
