The 2x2's identifying mark was a struck ring in bare paper, which fought every screen it was drawn over -- a hard outline across a Ben-Day field or a hatch reads as damage. It is now a tone: a disc laid down before the threshold and put through the same screen in the same ink, so what shows is a change in the density of the dots already there. Denser rather than thinner. Thinning it vanished, because at a coarse ruling the disc is barely wider than one cell of the screen and there is no room in it for a density change to show; going the other way needs none. It still fades at the coarsest rulings and is absent under SOLID, which discards tone by definition. RECORD and SETTINGS become a bar chart and a pair of sliders in hard-ruled squares -- inline SVG on integer coordinates in currentColor, so they invert with the box when pressed.
- commit
- 56a7690cd4471d62b9658cc13687127694ac89bd
- parent
- 846d74086e74399ff970a2254a72e71cc0d27200
- author
- Dami <dami.etoile@gmail.com>
| M | README.md | 25 | ++++++++++++++ |
| M | mockup/ | 25 | +++++++++++++- |
| M | mockup/ | 55 | ++++++++++++++++++++---------- |
| M | mockup/ | 20 | +++++++++++ |
@@ -100,6 +100,31 @@ was originally built for.
100100 Nothing small is ever set on the desktop pattern — including the title screen, which is a single101101 centred window holding the title, the specimen board and the prompt. An About box, essentially.102102 103+**The goal block's mark is made of the shading, not stamped on it.** It used to be a struck ring104+in bare paper, and a struck ring is an opinion about colour: stamping paper through the middle of a105+shaded block fought whatever screen the rung drew, and a hard outline across a Ben-Day field or a106+hatch reads as damage rather than as a mark. Under almost every screen, it did.107+108+So it is a *tone* instead — a disc laid down before the threshold and put through the same screen109+in the same ink, so what shows is a change in the density of the dots already there. An inlay110+rather than an inscription, and it cannot clash with a pattern it is made of. Under a line screen111+it appears as the rules themselves bulging; under diffusion, as a solid roundel in a dotted field.112+113+Denser rather than thinner, which took a look to settle: thinning it was the first instinct and it114+vanished, because at a coarse ruling the disc is barely wider than one cell of the screen and there115+is no room inside it for a change in density to show. Going the other way needs no room — the disc116+simply runs out of paper before the field around it does. It still fades at the coarsest rulings,117+which is what a coarse screen does to any fine detail, and is a far better failure than fighting118+the pattern. Under SOLID it is absent, honestly: that screen discards tone by definition, so a mark119+made of tone has nothing to be made of.120+121+**Toolbar icons carry their boxes.** RECORD and SETTINGS are a bar chart and a pair of sliders in122+hard-ruled squares rather than words — the way a system of this vintage drew a toolbar, and the123+reason it drew the rectangle every time: an unboxed glyph on a menu bar reads as a picture of124+something rather than as a thing to press. They are inline SVG on integer coordinates with125+`crispEdges`, in `currentColor` so they invert with the box when pressed. The canvas still has no126+SVG anywhere and should not; this is DOM chrome, which is where a vector glyph belongs.127+103128 **The dev-logo** sits at the far left of the menu bar on the puzzle list, which is where a104129 Macintosh put the Apple and the reason that position reads as a signature rather than as an icon105130 someone left lying there. It is constant in shape and position; the one thing that varies is which@@ -58,8 +58,29 @@
5858 <button id="btnBack" class="ghost">BACK</button>5959 <span class="spacer"></span>6060 <button id="btnInstall">INSTALL</button>61- <button id="btnStats" class="ghost">RECORD</button>62- <button id="btnSet" class="ghost">SETTINGS</button>61+ <!-- Boxed icons, the way a system of this vintage drew a toolbar: a hard62+ rectangle with a glyph struck inside it, rather than a word. Drawn as63+ inline SVG on integer coordinates with crispEdges, so the strokes64+ land on whole pixels like everything else, and in currentColor so65+ they invert with the box when it is pressed.66+67+ The canvas has no SVG anywhere and should not; this is DOM chrome,68+ which is exactly where a vector glyph belongs. -->69+ <button id="btnStats" class="iconbtn" aria-label="Record" title="RECORD">70+ <svg viewBox="0 0 9 9" aria-hidden="true" focusable="false">71+ <rect x="0" y="6" width="2" height="3"/>72+ <rect x="3.5" y="3" width="2" height="6"/>73+ <rect x="7" y="0" width="2" height="9"/>74+ </svg>75+ </button>76+ <button id="btnSet" class="iconbtn" aria-label="Settings" title="SETTINGS">77+ <svg viewBox="0 0 9 9" aria-hidden="true" focusable="false">78+ <rect x="0" y="2" width="9" height="1"/>79+ <rect x="2" y="0" width="2" height="5"/>80+ <rect x="0" y="6" width="9" height="1"/>81+ <rect x="5" y="4" width="2" height="5"/>82+ </svg>83+ </button>6384 </div>6485 6586 <div id="gauge" class="zone">@@ -462,28 +462,47 @@ function drawBlock(c, x, y, w, h, cls, opt = {}) {
462462 c.fillRect(bx + 1, by + 1, bw - 2, 1); c.fillRect(bx + 1, by + bh - 2, bw - 2, 1);463463 c.fillRect(bx + 1, by + 1, 1, bh - 2); c.fillRect(bx + bw - 2, by + 1, 1, bh - 2);464464 }465- // A struck ring on the goal block: the one piece whose identity matters.465+ // The mark on the goal block: the one piece whose identity matters.466466 //467- // Plotted a pixel at a time rather than stroked. A stroked arc is468- // anti-aliased, and a half-covered pixel comes out somewhere in the middle of469- // the range -- which error diffusion carries happily and an ordered screen470- // does not: under a flat threshold every partial pixel fell on the ink side471- // and the ring dropped to four stray dashes. A plotted circle is either on or472- // off, so it survives whichever style the rung drew.467+ // It used to be a struck ring in bare paper, and a struck ring is an opinion468+ // about colour. Stamping paper through the middle of a shaded block fights469+ // whatever screen the rung drew -- a hard outline across a Ben-Day field or a470+ // hatch reads as damage rather than as a mark, and it did so under almost471+ // every one of them.472+ //473+ // So it is not drawn on top of the shading at all. It is a *tone*: a disc474+ // laid down before the threshold and put through the same screen, in the same475+ // ink, so what shows is a change in the density of the dots already there.476+ // An inlay rather than an inscription, and it cannot clash with a pattern it477+ // is made of.478+ //479+ // Denser rather than thinner, which took a look to settle. Thinning it was480+ // the first instinct and it disappeared: at a coarse ruling the disc is481+ // barely wider than one cell of the screen, so there is no room inside it for482+ // a change in density to show. Going the other way needs no room at all --483+ // the disc simply runs out of paper before the field around it does, and a484+ // solid roundel inside a dotted one reads at every ruling because it is not485+ // competing with the screen, it is the absence of it.486+ //487+ // Plotted span by span rather than filled as an arc: a filled path is488+ // anti-aliased, and every half-covered pixel would land on some in-between489+ // tone and become its own group in the area pass.490+ //491+ // Under SOLID it does not appear, and that is the honest outcome rather than492+ // a gap: that screen discards tone by definition, so a mark made of tone has493+ // nothing to be made of. The block is still the only 2x2 and the only red.473494 if (opt.device) {474495 const cx = Math.round(bx + bw / 2), cy = Math.round(by + bh / 2);475- let px = Math.max(3, Math.round(bw * 0.19)), py = 0, err = 1 - px;476- c.fillStyle = grey(255);477- while (px >= py) {478- for (const [ox, oy] of [[px, py], [py, px], [-py, px], [-px, py],479- [-px, -py], [-py, -px], [py, -px], [px, -py]]) {480- c.fillRect(cx + ox, cy + oy, 1, 1);481- }482- py++;483- if (err < 0) err += 2 * py + 1;484- else { px--; err += 2 * (py - px) + 1; }496+ // Generous, because the disc has to be wider than the screen's own period497+ // before it can read as a shape at all. It still fades away at the coarsest498+ // rulings, which is what a coarse screen does to any fine detail and is a499+ // far better failure than fighting the pattern.500+ const r = Math.max(3, Math.round(Math.min(bw, bh) * 0.26));501+ c.fillStyle = grey(Math.round(tone * 0.3));502+ for (let dy = -r; dy <= r; dy++) {503+ const half = Math.floor(Math.sqrt(r * r - dy * dy));504+ c.fillRect(cx - half, cy + dy, half * 2 + 1, 1);485505 }486- c.fillRect(cx - 1, cy - 1, 2, 2);487506 }488507 if (cls && PIECE32[cls]) {489508 const p = PATTERNS[opt.shading];@@ -66,6 +66,26 @@ body {
6666 #top { justify-content: flex-start; gap: calc(5px * var(--s)); padding: 0 calc(8px * var(--s)); }6767 #top button { background: none; padding: calc(2px * var(--s)) calc(4px * var(--s)); }6868 #top .spacer { flex: 1; }69+70+/* A boxed icon: square, hard-ruled, with the glyph struck inside it. The box is71+ the point -- an unboxed glyph on a menu bar reads as a picture of something72+ rather than as a thing to press, which is why systems of this vintage drew73+ the rectangle every time. */74+.iconbtn {75+ display: flex; align-items: center; justify-content: center;76+ padding: calc(2.5px * var(--s)) ;77+ border-color: var(--ink) ;78+}79+.iconbtn svg {80+ display: block;81+ width: calc(9px * var(--s)); height: calc(9px * var(--s));82+ fill: currentColor;83+ shape-rendering: crispEdges; /* whole pixels, like the rest of the picture */84+}85+/* Inverts on press. The plain menu-bar buttons deliberately do not take a86+ background, but these already carry a box, so filling it is the QuickDraw87+ answer rather than a hole in the rule. */88+#top .iconbtn:active { background: var(--ink); color: var(--paper); }6989 #acts { gap: calc(6px * var(--s)); }7090 7191 .stat { display: flex; flex-direction: column; align-items: center; gap: calc(2px * var(--s)); }