Files
44-Scale/docs/superpowers/specs/2026-07-28-e440-arc-dial-design.md

7.5 KiB
Raw Permalink Blame History

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 050, 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-scalee440-scale.
  • k8s/ingress.yaml route works.baumann.gr/M1730-generatorworks.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, 050, 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.