diff --git a/server/pruef-start-ansicht.mjs b/server/pruef-start-ansicht.mjs index 1790b8f3..edf8fd92 100644 --- a/server/pruef-start-ansicht.mjs +++ b/server/pruef-start-ansicht.mjs @@ -750,18 +750,68 @@ console.log("\n=== Das Licht folgt dem Zeiger"); ok(gemessen.punkte > 50 && gemessen.spanne >= 6, `"${name}": auf der Oberkante liegt ueberhaupt Farbe ` + `(${gemessen.punkte} Punkte, Spanne ${gemessen.spanne})`); - /* Die REIHENFOLGE der Kanaele muss uebereinstimmen. Absolute - Werte koennen es nicht: Das Licht ist stark abgedunkelt und - liegt auf einem dunklen Grund, seine Zahlen sind ein Bruchteil - der Tonfarbe. Welcher Kanal der staerkste ist, bleibt davon - unberuehrt -- und genau das beweist, dass es DIESE Farbe ist. */ - const reihe = (v) => [0, 1, 2].sort((a, c) => v[c] - v[a]).join(""); - const sollReihe = reihe(b.soll); - const istReihe = reihe(p); - ok(sollReihe === istReihe, + /* DER FARBWINKEL, NICHT DIE REIHENFOLGE DER KANAELE (08.09.2026). + + Hier stand ein Vergleich der Kanal-REIHENFOLGE: Welcher Kanal + ist der staerkste, welcher der zweite. Der Gedanke war richtig + -- absolute Werte taugen nicht, weil das Licht stark + abgedunkelt auf dunklem Grund liegt. + + Er hat aber eine Voraussetzung, die niemand aufgeschrieben + hatte: Die Kanaele muessen deutlich verschieden sein. Seit die + Kachelfarben am 08.09.2026 neu gerechnet wurden, trifft das + nicht mehr ueberall zu. "Aufgaben" ist jetzt Tuerkis (#18bfa3) + mit G=191 und B=163 -- nach Abdunklung gemessen 63 gegen 65. + ZWEI Stufen von 255. Die Reihenfolge kippt dort durch + Rundung, und die Pruefung meldete einen Fehler, wo keiner war. + + Der Farbwinkel misst dasselbe, nur ohne diese Voraussetzung: + Er sagt, WELCHE Farbe es ist, unabhaengig von Helligkeit und + Saettigung -- genau die Frage. 40 Grad Toleranz, weil das Licht + sich mit dem dunklen Untergrund mischt; verwechselbare Toene + der Palette liegen weiter auseinander (der kleinste Abstand + zweier Kachelfarben betraegt nach der Neuberechnung 0,097 in + OKLab, das sind hier gut 60 Grad). + + Die Gegenprobe unten beweist, dass sie weiterhin anschlaegt. */ + const winkel = (v) => { + const [r, g, bl] = v.map((x) => x / 255); + const max = Math.max(r, g, bl), min = Math.min(r, g, bl), d = max - min; + if (d < 0.004) return null; // grau, kein Ton + let h; + if (max === r) h = ((g - bl) / d) % 6; + else if (max === g) h = (bl - r) / d + 2; + else h = (r - g) / d + 4; + return ((h * 60) % 360 + 360) % 360; + }; + const abstand = (a, c) => { + if (a === null || c === null) return 999; + const d = Math.abs(a - c); + return d > 180 ? 360 - d : d; + }; + const sollWinkel = winkel(b.soll), istWinkel = winkel(p); + const ab = abstand(sollWinkel, istWinkel); + ok(ab <= 40, `das Licht auf "${name}" traegt den Ton der Kachel ` - + `(${b.ton}: ${b.soll.join("/")} → gemessen ${p[0]}/${p[1]}/${p[2]}, ` - + `Reihenfolge ${istReihe} gegen ${sollReihe})`); + + `(Farbwinkel ${sollWinkel === null ? "grau" : Math.round(sollWinkel) + "°"} ` + + `gegen ${istWinkel === null ? "grau" : Math.round(istWinkel) + "°"}, ` + + `Abstand ${Math.round(ab)}° von 40 erlaubt) ` + + `(${b.ton}: ${b.soll.join("/")} → gemessen ${p[0]}/${p[1]}/${p[2]})`); + + /* GEGENPROBE, einmal je Kachel: Eine Pruefung, die immer + bestaetigt, bestaetigt nichts. Der gemessene Ton wird gegen + eine ABSICHTLICH falsche Farbe gehalten -- den Gegenton auf dem + Farbkreis. Schlaegt das nicht an, misst die Pruefung nichts. + + Der Gegenton ist der haerteste Fall: Er ist so weit entfernt, + wie zwei Farben ueberhaupt sein koennen. Wuerde die Toleranz + einmal versehentlich auf 180 Grad hochgesetzt, faellt es hier + sofort auf. */ + const falsch = sollWinkel === null ? 180 : (sollWinkel + 180) % 360; + ok(abstand(falsch, istWinkel) > 40, + ` und sie erkennt den Gegenton als falsch ` + + `(${Math.round(falsch)}° gegen gemessen ` + + `${istWinkel === null ? "grau" : Math.round(istWinkel) + "°"})`); } await seite.mouse.move(2, 2); diff --git a/workspace/assets/css/heim.css b/workspace/assets/css/heim.css index 8be4cd5b..3bc8be49 100644 --- a/workspace/assets/css/heim.css +++ b/workspace/assets/css/heim.css @@ -1815,3 +1815,90 @@ color: #ffd2d0; text-shadow: 0 0 7px rgba(255, 80, 76, .6); } + +/* =================================================================== + DIE UHR, GESCHAERFT (08.09.2026, zweite Runde) + + Filipe: "ich will aber dass die sekunden wie punkte sind, die minuten + breiter und die stunden noch breiter … den silbernen rand weg bitte + den will ich nicht … es soll nichts verschwommen aussehen oder so, + im gegenteil, richtig scharf und perfekt." + + VIER AENDERUNGEN, und jede hat einen Grund ueber den Wunsch hinaus: + + 1. DER SILBERNE RING IST WEG. Er war die breiteste Flaeche der ganzen + Uhr und damit das Erste, was das Auge traf -- ausgerechnet der + Teil, der nichts anzeigt. Jetzt traegt dort dunkles Metall, und + die drei Bahnen sind das Hellste im Bild. Die Skalenstriche + bleiben: Sie geben dem Ring Mass, ohne ihn zu fuellen. + + 2. DIE SEKUNDE IST EINE PUNKTREIHE. Sechzig Punkte, einer je + Sekunde -- der schnellste Wert wird damit zaehlbar statt nur + gewachsen. Das ist auch die ehrlichere Anzeige: Die Sekunde + SPRINGT (die Ziffer daneben tut es auch), ein durchgehender Bogen + behauptet einen fliessenden Wert. + + 3. DIE BREITEN STAFFELN SICH: Sekunde 3, Minute 5, Stunde 7. Vorher + lagen sie bei 3 / 3,4 / 4 -- rechnerisch verschieden, im Bild + nicht zu unterscheiden. Ein Unterschied unter einem Pixel ist + keiner. Jetzt liest man die Ringe an ihrer STAERKE, nicht nur an + der Farbe: je langsamer, desto schwerer. + + 4. NICHTS IST MEHR VERSCHWOMMEN. Die weichen Scheine (`drop-shadow` + mit 7 bis 9 px Radius) lagen ueber den Bahnen wie Nebel. Sie sind + auf 2 bis 3 px zurueckgenommen und liegen jetzt ALS KANTE an, + nicht als Wolke. Der Rest der Tiefe kommt aus dem Versatz der + Lagen -- so, wie es im Rest dieser Uhr ohnehin gemacht ist. */ + +/* ---- 1. Der Ring wird dunkel ------------------------------------- */ +.uhr { + background: + conic-gradient(from 210deg, + #171d27 0deg, #10151d 54deg, #1b222d 116deg, #121820 176deg, + #1d242f 246deg, #10161e 312deg, #171d27 360deg); + box-shadow: + 0 14px 30px -14px rgba(0, 0, 0, .95), + 0 0 0 1px rgba(140, 170, 205, .10), + inset 0 0 0 1px rgba(6, 10, 18, .9); +} + +/* ---- 3. Die Breiten, klar gestaffelt ----------------------------- */ +.uhr__minute { stroke-width: 5; } +.uhr__stunde { stroke-width: 7; } +.uhr__schatten .uhr__minute { stroke-width: 6; } +.uhr__schatten .uhr__stunde { stroke-width: 8; } + +/* ---- 2. Die Sekunde als Punktreihe ------------------------------- + `stroke-linecap: round` plus eine Strichlaenge nahe null ergibt + einen Kreis vom Durchmesser der Strichbreite. Das Muster selbst + setzt start.js -- es muss mit dem Stand wachsen, und dafuer braucht + es die Zahl der Punkte. */ +.uhr__sekunde { + stroke-width: 3.2; + stroke-linecap: round; +} +/* Der Schweif entfaellt bei der Sekunde -- und zwar im HTML, nicht + hier. Eine Punktreihe hat keinen Kometenschweif, sie hat einen + letzten Punkt, und den setzt die Perle. */ + +/* ---- 4. Schaerfe statt Nebel ------------------------------------- */ +.uhr__sekunde { + filter: drop-shadow(0 0 1.5px rgba(255, 92, 108, .9)); +} +.uhr__minute { + filter: drop-shadow(0 0 1.5px rgba(120, 200, 255, .9)); +} +.uhr__stunde { + filter: drop-shadow(0 0 1.5px rgba(255, 205, 145, .85)); +} +.uhr__spur-bahn[data-spur="minute"], +.uhr__spur-bahn[data-spur="stunde"] { + filter: drop-shadow(0 0 1.5px rgba(255, 255, 255, .5)); +} +/* Auch die Perlen: harte Kante, enger Schein. Ein Lichtpunkt ist + scharf -- was leuchtet, hat einen Rand. */ +.uhr__perle::before { + box-shadow: + 0 0 0 1px rgba(255, 255, 255, .75), + 0 0 4px 0 currentColor; +} diff --git a/workspace/assets/js/start.js b/workspace/assets/js/start.js index c2b4e7da..435ac416 100644 --- a/workspace/assets/js/start.js +++ b/workspace/assets/js/start.js @@ -398,8 +398,68 @@ spuren[welcher] = document.querySelector(`[data-spur="${welcher}"]`); } + /* DIE SEKUNDE IST EINE PUNKTREIHE (08.09.2026). + + Filipe: "ich will aber dass die sekunden wie punkte sind". + + Sechzig Punkte auf der Bahn, einer je Sekunde, und es leuchten so + viele, wie vergangen sind. `stroke-linecap: round` macht aus einer + Strichlaenge nahe null einen Kreis vom Durchmesser der + Strichbreite -- so entsteht ein Punkt ohne ein einziges weiteres + Element. + + DAS MUSTER MUSS MITWACHSEN, und genau das kann `dashoffset` + allein nicht: Es verschiebt ein festes Muster, es kuerzt es + nicht. Ein festes Punktmuster plus Offset ergaebe eine Reihe, die + WANDERT statt zu wachsen. Deshalb wird die Liste je Sekunde neu + gebaut: n Paare aus Punkt und Luecke, dahinter eine Luecke ueber + den ganzen Rest. Sechzig Zahlenpaare einmal je Sekunde sind + nichts -- die Alternative waeren sechzig Elemente im Dokument. */ + const SEK_PUNKTE = 60; + const punktMuster = (U, n) => { + const schritt = U / SEK_PUNKTE; + if (n <= 0) return `0 ${U}`; + /* 0.01 statt 0: Manche Browser zeichnen eine Strichlaenge von + exakt null gar nicht, auch mit runder Kappe. */ + const paar = `0.01 ${(schritt - 0.01).toFixed(3)} `; + /* DIE REIHENFOLGE ENTSCHEIDET, OB DER REST EINE LUECKE IST. + + In `dasharray` wechseln sich Strich und Luecke ab, beginnend + mit einem Strich. Nach n Paaren stehen 2n Werte -- der naechste + Wert sitzt damit an UNGERADER Stelle und ist ein STRICH. + + Genau daran bin ich zweimal gescheitert. Erst haengte ich den + Rest direkt an (2n+1 Werte, ungerade -- SVG verdoppelt die + Liste und vertauscht dabei alles), dann setzte ich eine Null + hinterher (`rest 0`): Die Anzahl war gerade, aber der Rest + stand weiterhin an ungerader Stelle und wurde gezeichnet. Im + Bildschirmfoto lief beide Male ein durchgehender roter Bogen + ueber die halbe Uhr, neben den richtigen Punkten. + + Richtig ist die Null ZUERST: Sie belegt die ungerade Stelle als + Strich der Laenge null, und der Rest landet an gerader Stelle -- + als Luecke, wie gemeint. Zwei Zahlen in der richtigen + Reihenfolge, und der Rest verschwindet. */ + return paar.repeat(n) + '0 ' + (U - n * schritt).toFixed(3); + }; + const setze = (welcher, anteil) => { const U = UMFANG[welcher]; + + if (welcher === 'sekunde') { + /* Der erste Punkt gehoert der Sekunde 0 -- deshalb n = sk + 1: + Bei 0 Sekunden leuchtet einer, bei 59 alle sechzig. Ein Ring, + der bei null voellig leer ist, sieht eine Sekunde lang aus wie + ein Ladefehler. */ + const n = Math.round(anteil * SEK_PUNKTE) + 1; + const muster = punktMuster(U, Math.min(n, SEK_PUNKTE)); + for (const el of lagen[welcher]) { + el.style.strokeDasharray = muster; + el.style.strokeDashoffset = '0'; + } + return; + } + const wert = String(U * (1 - anteil)); for (const el of lagen[welcher]) el.style.strokeDashoffset = wert; diff --git a/workspace/start.html b/workspace/start.html index de548011..b2a9abf8 100644 --- a/workspace/start.html +++ b/workspace/start.html @@ -308,8 +308,19 @@ Sie liegen VOR der Oberkante, damit die helle Kante auch auf dem Schweif sitzt und nicht darunter verschwindet. --> + -