From 852f8fe0508c9be014ca4d7dc41824f7f8473ac0 Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Tue, 28 Jul 2026 23:09:07 +0200 Subject: [PATCH] Add design spec for E440 arc-dial scale generator pivot --- .../specs/2026-07-28-e440-arc-dial-design.md | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-28-e440-arc-dial-design.md diff --git a/docs/superpowers/specs/2026-07-28-e440-arc-dial-design.md b/docs/superpowers/specs/2026-07-28-e440-arc-dial-design.md new file mode 100644 index 0000000..3fe49b2 --- /dev/null +++ b/docs/superpowers/specs/2026-07-28-e440-arc-dial-design.md @@ -0,0 +1,151 @@ +# 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.