From 3b1e244d55b9971c5c788ad364ed2a3b6f0a07bd Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Wed, 29 Jul 2026 00:48:54 +0200 Subject: [PATCH] Fix final-review findings: unit-label baseline, sqrt minor-tick math, dead code, edge cases - Unit label was vertical-centered on a box derived from a baseline coordinate, shifting it 3.34mm too low and making the generated deliverables inconsistent with E440-Scale_template.svg. UNIT_Y is now used directly as the baseline; regenerated E440-Scale_paths.svg and E440-Scale_text.svg accordingly. - sqrt scale's minor-tick angle formula double-applied the square-root mapping, badly misplacing minor ticks. Removed the special case so sqrt falls through to the already-correct generic value-interpolation branch. Corrected README's sqrt row (crowds toward the high end, not low). - Removed dead code: unused _has_markup(), identity-transform wrapper and _UNIT_SCALE_X in _markup_unit_svg, unreachable max_val<=0 check, redundant math.isinf() in the minor-tick condition. - Linear scale with min==max now raises ValueError like the other three mappings, instead of silently producing a degenerate dial. - Form numeric parsing moved inside the try/except so bad input returns 400 instead of crashing with 500. - left_label/right_label now split on splitlines() to handle CRLF from browser textareas correctly. - Formatting consistency: PIVOT_CIRCLE_R/R0 use :.5f like other constants; font_face indentation matches its sibling elements. - LICENSE and CLAUDE.md: last M1730-Scale references and a doc inaccuracy about which labels are live text vs. outlined paths. Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 2 +- E440-Scale_paths.svg | 10 ++++---- E440-Scale_text.svg | 12 ++++------ LICENSE | 4 ++-- README.md | 2 +- web/app.py | 56 +++++++++++++++++--------------------------- 6 files changed, 35 insertions(+), 51 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ab00805..c96af76 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,7 +14,7 @@ The three SVG files represent layers of the same design, intended to be combined - **`E440-Scale_template.svg`** — Inkscape working template. Contains the case body path (with mounting ears and rim cutout), the pivot guide circle, tick marks, numeric labels `0`–`50`, the `uA` unit label, and `Text`/`Left` and `Text`/`Right` corner label placeholders. - **`E440-Scale_paths.svg`** — Paths-only version: all tick marks and text as ``/`` elements, no live text. Used for cutting/printing workflows where fonts must not be embedded as live text. -- **`E440-Scale_text.svg`** — Text-overlay version: tick mark lines plus live `` elements for numeric/unit/corner labels, font embedded as base64. +- **`E440-Scale_text.svg`** — Text-overlay version: tick mark lines plus live `` elements for numeric/corner labels (the unit label is always emitted as outline paths since it supports markup), font embedded as base64. ## Key Design Parameters diff --git a/E440-Scale_paths.svg b/E440-Scale_paths.svg index 4b05ff1..89b035e 100644 --- a/E440-Scale_paths.svg +++ b/E440-Scale_paths.svg @@ -2,8 +2,8 @@ - - + + @@ -66,10 +66,8 @@ - - - - + + diff --git a/E440-Scale_text.svg b/E440-Scale_text.svg index 577244d..e7647f8 100644 --- a/E440-Scale_text.svg +++ b/E440-Scale_text.svg @@ -1,10 +1,10 @@ - + - - + + @@ -62,10 +62,8 @@ 30 40 50 - - - - + + Text Left Text diff --git a/LICENSE b/LICENSE index 387efe8..b133688 100644 --- a/LICENSE +++ b/LICENSE @@ -208,7 +208,7 @@ If you develop a new program, and you want it to be of the greatest possible use To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. - M1730-Scale + E440-Scale Copyright (C) 2026 adebaumann This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. @@ -221,7 +221,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - M1730-Scale Copyright (C) 2026 adebaumann + E440-Scale Copyright (C) 2026 adebaumann This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/README.md b/README.md index 8a3c05d..250b063 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Fonts are loaded at startup from the `fonts/` directory in the repo root. | Mapping | Major tick values | Minor tick placement | Constraints | |---|---|---|---| | Linear | Evenly spaced | Uniform | None | -| √ | Quadratic (0, 1, 4, 9 … × max) — compresses low values | Crowded toward the low end of each interval | Min ≥ 0 | +| √ | Quadratic (0, 1, 4, 9 … × max) — compresses low values | Crowded toward the high end of each interval | Min ≥ 0 | | Log | Geometric (min × r⁰, r¹, r² …) — each interval spans the same ratio | Crowded toward the high end of each interval | Min and Max > 0 | | 1/x | Harmonic (values decrease left → right, e.g. ∞, 10, 5, 2, 1) — compresses high values | Uniform (in angle space, i.e. proportional to 1/v) | Min and Max > 0; Max may be ∞ | diff --git a/web/app.py b/web/app.py index b60a95d..676860d 100644 --- a/web/app.py +++ b/web/app.py @@ -99,7 +99,6 @@ _FRAC_SIDE = 0.12 _UNIT_BOX_X0, _UNIT_BOX_X1 = UNIT_X - 15.0, UNIT_X + 15.0 _UNIT_BOX_Y0, _UNIT_BOX_Y1 = UNIT_Y - 6.0, UNIT_Y + 6.0 _UNIT_MAX_FS = 9.46938021 -_UNIT_SCALE_X = 1.0 def _point_at(radius: float, angle_deg: float): @@ -271,30 +270,23 @@ def _draw_nodes(nodes, x: float, y: float, fs: float, fill: str, fd: _FontData): return parts, cx - x -def _has_markup(s: str) -> bool: - return any(c in s for c in '^_') or ('{' in s and '}' in s) - - def _markup_unit_svg(unit: str, fd: _FontData, fill='#1a1a1a') -> str: nodes = _parse_markup(unit) x0, x1 = _UNIT_BOX_X0, _UNIT_BOX_X1 y0, y1 = _UNIT_BOX_Y0, _UNIT_BOX_Y1 - xc, yc = (x0 + x1) / 2, (y0 + y1) / 2 + xc = (x0 + x1) / 2 w1 = _measure(nodes, 1.0, fd) top1, bot1 = _vbounds(nodes, 1.0, fd) fs = _UNIT_MAX_FS if w1 > 0: - fs = min(fs, (x1 - x0) / (w1 * _UNIT_SCALE_X)) + fs = min(fs, (x1 - x0) / w1) if bot1 - top1 > 0: fs = min(fs, (y1 - y0) / (bot1 - top1)) w = _measure(nodes, fs, fd) - top, bot = _vbounds(nodes, fs, fd) - y_base = yc - (top + bot) / 2 - parts, _ = _draw_nodes(nodes, xc - w / 2, y_base, fs, fill, fd) - cond = f'translate({xc:.5f},0) scale({_UNIT_SCALE_X},1) translate({-xc:.5f},0)' - return f'\n{chr(10).join(parts)}\n' + parts, _ = _draw_nodes(nodes, xc - w / 2, UNIT_Y, fs, fill, fd) + return '\n'.join(parts) def _text_to_paths(text, x, y, font_size, fd: _FontData, anchor='start', fill='#1a1a1a', wrap_transform=None): @@ -387,8 +379,6 @@ def generate_svg(unit, min_val, max_val, left_label, right_label, big_ticks, sma return math.inf return min_val * big_ticks / i else: - if max_val <= 0: - raise ValueError("Reciprocal scale requires min and max values greater than 0.") k = 1.0 / min_val - 1.0 / max_val def value_to_angle(v): @@ -399,10 +389,10 @@ def generate_svg(unit, min_val, max_val, left_label, right_label, big_ticks, sma else: span = max_val - min_val + if span == 0: + raise ValueError("Linear scale requires min and max values to differ.") def value_to_angle(v): - if span == 0: - return ANGLE_MIN return ANGLE_MIN + (ANGLE_MAX - ANGLE_MIN) * (v - min_val) / span def major_value(i): @@ -434,7 +424,7 @@ def generate_svg(unit, min_val, max_val, left_label, right_label, big_ticks, sma ) font_face = ( - '