Add design spec for E440 arc-dial scale generator pivot
This commit is contained in:
@@ -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 `<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.
|
||||||
Reference in New Issue
Block a user