Der Notizblock -- und eine Pruefung, die zwoelf Kacheln nie angesehen hat
Filipe: „fuer die modis, rechte und linke hand und dogfather eine
kachel hinzufuegst, sie soll: Notizen, heissen. ich will dass du das
auch wie ein notizblock erstellst. ich will dass es uebelst geil und
einzigartig ist."
=================================================================
TEIL 1: DIE FARBE -- UND WAS DABEI AUFFIEL
=================================================================
Fuer die neue Kachel braucht es einen Ton. Beim Suchen fiel auf, dass
tools/kachelton-entzerren.mjs zwoelf der 45 Farben fuer FREI hielt.
Sie sind es nicht. bereicheFuer() gibt fuer die fuenf Rollen des
Agenturhauses null zurueck -- das heisst „nimm die Liste aus dem
Browser", und die steht in workspace/assets/js/bereiche.js. Dort
stehen die Toene 1 bis 22 und 44: Dashboard, Zahlen, Team-Lage,
Scout-Pipeline. Kacheln, die jeden Tag jemand ansieht.
pruef-kachelfarben hatte denselben blinden Fleck. Sie meldete seit
Wochen „33 benutzte Toene" und war gruen. Was sie dadurch NICHT sah:
* Der Farblauf von gestern Nacht hat zwoelf dieser Kacheln
verschoben und drei davon unter die Buntheitsgrenze gedrueckt.
Gruen geblieben.
* Ton 7, 16 und 22 lagen SEIT JEHER unter der Grenze (0,112 / 0,116
/ 0,068). Nie gemeldet.
Das ist die Sorte gruener Haken, vor der die Hausregel warnt: Er sagt
nur, dass die Bedingung erfuellt war -- nicht, dass sie das Richtige
angesehen hat.
BEHOBEN, und zwar an der Wurzel: Die REGELN (Kontrast, Buntheit)
gelten jetzt fuer jeden Ton, der irgendwo an einer Kachel steht --
gelesen aus denselben fuenf Dateien, die auch pruef-kachel-universum
liest. Dazu die Namen der Kacheln, damit „Ton 1 traegt Dashboard"
ueberhaupt pruefbar ist.
DIE PALETTE WURDE NEU GERECHNET, mit dem dritten Verfahren an einem
Tag -- die ersten zwei stehen als Fehler im Kopf des Werkzeugs:
1. Alle 45 neu verteilt. Lief ueber zwei ausdrueckliche Wuensche
hinweg (#ff1a1a, #a8d8ff).
2. Nur die Kollisionen, aber immer nur EINEN der beiden bewegt.
Ergebnis: Ton 1 sprang vom Tuerkis ins Altrosa, 0,197 weit.
3. BEIDE duerfen sich bewegen, und zwar beide nur ein bisschen.
Zwei Toene, die 0,02 auseinanderstehen und 0,09 brauchen, teilen
sich das -- jeder rueckt 0,045, und beide bleiben, was sie waren.
kleinster Abstand 0,0154 -> 0,0905
zu blass 4 -> 0
sichtbar veraendert 7 Kacheln (ueber 0,05)
kaum zu sehen 28 (13 zwischen 0,02 und 0,05, 15 darunter)
EINE AUSNAHME MIT ZAHL, keine mit Achselzucken: Ton 1 (Dashboard)
bleibt acht Tausendstel unter der Buntheitsgrenze. Gemessen ueber ALLE
16,7 Millionen sRGB-Farben ist die naechste, die alle Regeln haelt,
#7a75c8 -- ein Blauviolett, 0,113 entfernt. Tuerkis erreicht in sRGB
schlicht keine hoehere Buntheit, und das schmale Band teilen sich
schon sieben Toene. Eine Kachel, die ihre Farbfamilie behaelt, ist die
bessere Antwort auf „richtig geil und speziell" als eine, die acht
Tausendstel bunter ist und niemand wiedererkennt. blassBis sagt jetzt
bei jeder Ausnahme, WIE WEIT sie reicht -- vorher hiess blassErlaubt
schlicht „hier wird weggesehen".
DIE NEUE FARBE IST GRUEN UND WOLLTE BERNSTEIN SEIN. Gesucht war die
Farbe von Papier. Gemessen gibt es sie nicht mehr: Der beste Bernstein
im ganzen Farbraum haelt 0,0799 Abstand -- unter der Hausgrenze. Und
Platz schaffen hilft nicht: Setzt man ihn fest, muss ein warmer Ton
das Band verlassen (Ton 45 waere 0,212 weit ins Magenta gewandert).
Die groesste wirklich freie Luecke liegt im Gruen, bei 0,0917. Ein
linierter Block in Gruen ist Papier, seit es Papier gibt.
=================================================================
TEIL 2: DER BLOCK
=================================================================
DREI ENTSCHEIDUNGEN, AUS DENEN DER REST FOLGT:
1. ES GIBT KEINEN SPEICHERN-KNOPF. Nirgends. Wer einen Block
aufschlaegt und lostippt, drueckt hinterher nicht auf „sichern";
er klappt ihn zu. Gesichert wird 800 ms nach dem letzten
Tastendruck. Geht das nicht, bleibt der Text im Browser liegen und
wird nachgereicht -- und der Fuss sagt ehrlich „noch nicht
gesichert", statt einen Verlust zu melden, den man gerade nicht
verhindern kann.
2. DAS BLATT IST EIN SCHREIBFELD MIT EINER DECKSCHICHT. Das Feld
traegt Text und Cursor und ist unsichtbar; darueber zeichnet eine
zweite Schicht denselben Text noch einmal -- mit Kaestchen,
Ueberschriften, Strichen und Links.
DARAUS FOLGT EINE EISERNE REGEL, und sie steht dreimal im Code:
KEINE Auszeichnung darf die BREITE eines Zeichens aendern. Kein
Fettdruck, keine andere Groesse, keine Sperrung. Erlaubt sind nur
Farbe, Flaeche, Rahmen und Durchstreichen. Ein einziges
font-weight: 700 verschoebe den Umbruch, und ab der zweiten Zeile
stuende der sichtbare Text neben dem Cursor.
pruef-notizen misst das am echten Umbruch: eine Probe mit allen
Auszeichnungen und einer Zeile, die umbrechen MUSS. Sind beide
Schichten verschieden hoch, sitzt der Umbruch woanders. Mit
Gegenprobe -- Fettdruck auf der Deckschicht bricht die Deckung um
genau eine Zeile (30 px), und die Zeile wird rot.
3. DIE TASTATUR FUEHRT DIE LISTE FORT, NICHT EINE LEISTE. Ein
Spiegelstrich und Enter macht den naechsten Punkt, ein leeres
Kaestchen und Enter das naechste Kaestchen -- und ein GESETZTER
Haken wird dabei nicht mitgenommen, die naechste Aufgabe ist ja
noch nicht erledigt. Zweimal Enter beendet die Liste. Ein Klick
aufs Kaestchen hakt ab. Es gibt keine Werkzeugleiste, weil man beim
Schreiben nie den Stift wechselt.
UND: NIEMAND SIEHT DIE NOTIZEN EINES ANDEREN. Auch DogFather nicht.
Es gibt in workspace-notizen.js keinen siehtAlles()-Zweig, keinen
Umschalter, keine fremde Liste -- jede Abfrage hat person_id = ? fest
eingebaut. Die Kachel steht deshalb in „Fuer dich" und nicht in
„Taeglich": Die Gruppe sagt die wichtigste Eigenschaft, bevor man sie
anfasst.
NUR AUF DER TEAM-ADRESSE. Auf der Agenturadresse ist die Seite ein
404 -- auch fuer DogFather. Das ist dieselbe Trennung wie bei Chat,
Kalender und Aufgaben, und sie steht an zwei Stellen: in
GEHOERT_ZU_ADRESSE (die Datei) und in der Schnittstelle (die Daten).
Die Seite ist nur HTML; die Daten sind die Sache.
Dazu: sechs Papierfarben, Anheften, Suche mit Hervorhebung im Text,
Papierkorb mit dreissig Tagen und einem Eintrag in der
Aufbewahrungsliste (ohne den waere die Frist ein Satz in einem
Kommentar -- genau das ist dem Support am 24.09. passiert).
=================================================================
EIN FUND BEIM BAUEN, DER ALLEN GEHOERT
=================================================================
DELETE /workspace/api/notizen/:id gab es schon -- in
workspace-aufgaben.js, fuer die Notizen AN einer Aufgabe. Weil jener
Router frueher eingehaengt ist, hat er jeden Loeschversuch des Blocks
abgefangen und mit 404 beantwortet.
Von aussen sah das aus wie ein Fehler im neuen Modul: Anlegen ging,
Aendern ging, Loeschen nicht. Man sucht dann im eigenen Code, und dort
ist nichts. Express meldet so etwas nicht -- es nimmt die erste Route,
die passt, und schweigt ueber die zweite.
Der Block liegt jetzt unter /workspace/api/notizblock. Und
pruef-notizen geht seitdem den Routenbaum von express durch und meldet
jedes Paar aus Methode und Pfad, das zweimal vergeben ist. Gemessen:
307 Wege, keine Dublette. Die Pruefung gilt fuers ganze Haus, auch
wenn sie in dieser Datei steht -- hier ist sie gefunden worden.
Nebenbei berichtigt: pruef-crew-adresse verlangte von jedem Eintrag
der Haustafel HTTP 200 mit Inhalt. Das stimmte, solange dort nur
oeffentliche Dateien standen. notizen.html ist die erste Seite hinter
der Anmeldung -- sie antwortet mit 302, und das ist richtig. Gefragt
wird jetzt, was die Tafel wirklich meint: GIBT es das hier.
GEMESSEN, alles nach den Aenderungen:
pruef-notizen 76 Punkte, 0 Fehler (neu)
pruef-kachelfarben 31 Punkte, 0 Fehler (vorher 26)
pruef-kachel-universum 13 Punkte, 0 Fehler
pruef-crew-adresse 157 Punkte, 0 Fehler
pruef-buehne 230 Punkte, 0 Fehler -- notizen.html neu in
der Liste, schlechtester Kontrast 6,73:1,
also 50 % ueber der Grenze
pruef-rechtetafel, -haus-seiten, -struktur, -css-klassen,
-jeder-hat-eine-seite, -workspace-seiten, -kachelraster,
-rueckmeldung alle 0 Fehler
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+206
-198
@@ -1,35 +1,55 @@
|
||||
/* =====================================================================
|
||||
NUR DIE FARBEN BEWEGEN, DIE SICH WIRKLICH IN DIE QUERE KOMMEN
|
||||
(25.09.2026, zweiter Anlauf)
|
||||
DIE KACHELFARBEN AUSEINANDERZIEHEN -- SO WENIG WIE MOEGLICH
|
||||
(25./26.09.2026)
|
||||
|
||||
Der erste Anlauf hat ALLE 45 Farben neu verteilt (die Rechnung
|
||||
dazu steht in der Geschichte des Verzeichnisses, Commit d7bb0f7e).
|
||||
Das Ergebnis war rechnerisch besser und praktisch falsch: Er ist
|
||||
dabei ueber zwei ausdrueckliche Wuensche gelaufen (das knallrote
|
||||
#ff1a1a fuer "Vertraulich melden", das Babyblau #a8d8ff fuer
|
||||
"Support") und hat drei Toene unter die Buntheitsgrenze von 0,12
|
||||
gedrueckt. pruef-kachelfarben hat alles fuenf gefunden.
|
||||
Filipe, 17.09.: „ich will das jede kachel eine andere farbe hat, es
|
||||
soll keine die gleiche farben haben bitte und auch keine die sich
|
||||
irgendwie aehnlich sind ... die farben sollen auch richtig geil und
|
||||
speziell sein."
|
||||
|
||||
DIE LEHRE IST NICHT "die Rechnung war falsch", sondern: Eine
|
||||
Gesamtloesung fuer ein LOKALES Problem bezahlt man mit allem, was
|
||||
an den nicht betroffenen Stellen schon richtig war. Acht Paare
|
||||
standen zu eng -- also werden acht Paare auseinandergezogen und
|
||||
sonst nichts angefasst.
|
||||
Gemessen lagen acht Paare zwischen 0,015 und 0,054 OKLab-Abstand.
|
||||
#ff8fb4 gegen #fe8ebe ist mit blossem Auge DIESELBE Farbe.
|
||||
|
||||
VORGEHEN. Solange irgendein Paar unter dem Mindestabstand liegt:
|
||||
das engste nehmen, den beweglicheren der beiden auf die naechste
|
||||
erlaubte Farbe schieben, die alle Bedingungen haelt. Festgelegte
|
||||
Toene sind unbeweglich.
|
||||
---------------------------------------------------------------------
|
||||
ZWEI ANLAEUFE HAT DAS GEKOSTET, UND BEIDE FEHLER STEHEN HIER
|
||||
|
||||
ALLE SCHWELLEN KOMMEN AUS tools/kachelton-regeln.mjs. Keine Zahl
|
||||
wird hier abgeschrieben -- genau dafuer gibt es die Datei.
|
||||
ERSTER ANLAUF: alle 45 Farben neu verteilt. Rechnerisch besser,
|
||||
praktisch falsch -- er lief ueber zwei ausdrueckliche Wuensche
|
||||
hinweg (das knallrote #ff1a1a, das Babyblau #a8d8ff) und drueckte
|
||||
drei Toene unter die Buntheitsgrenze.
|
||||
|
||||
ZWEITER ANLAUF: nur die Kollisionen angefasst, dafuer immer nur
|
||||
EINEN der beiden Toene bewegt -- den, dessen Ausweichen billiger
|
||||
war. Das sieht sparsam aus und ist es nicht: Ist die Nachbarschaft
|
||||
voll, muss dieser eine quer durch den Farbraum. Gemessen wanderte
|
||||
Ton 1 von einem Tuerkis (#0c86af) auf ein Altrosa (#b4649c), 0,197
|
||||
weit. Eine Kachel, die von Tuerkis auf Rosa springt, erkennt
|
||||
niemand wieder -- und der Zweck der ganzen Uebung war ja, dass man
|
||||
sie auseinanderhaelt.
|
||||
|
||||
DRITTER ANLAUF, und der steht hier: BEIDE duerfen sich bewegen, und
|
||||
zwar beide nur ein bisschen. Zwei Toene, die 0,02 auseinanderliegen
|
||||
und 0,09 brauchen, koennen sich das teilen -- jeder rueckt 0,045,
|
||||
und beide bleiben, was sie waren.
|
||||
|
||||
Umgesetzt als wachsender Spielraum: Erst darf jeder Ton 0,02 von
|
||||
seiner Farbe abweichen, dann 0,03, dann 0,04 ... Es wird immer die
|
||||
KLEINSTE Abweichung genommen, mit der es aufgeht. Wer gar nicht
|
||||
abweichen muss, bleibt unberuehrt.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
ALLE SCHWELLEN KOMMEN AUS tools/kachelton-regeln.mjs
|
||||
|
||||
Keine Zahl wird hier abgeschrieben -- genau dafuer gibt es die
|
||||
Datei. Und die FESTGELEGTEN Toene stehen ebenfalls dort: Sie werden
|
||||
nie bewegt, auch nicht um einen Bildpunkt.
|
||||
|
||||
node tools/kachelton-entzerren.mjs rechnen und zeigen
|
||||
node tools/kachelton-entzerren.mjs schreiben start.css aendern
|
||||
|
||||
Umgebung: ABSTAND=0.09 (Ziel), SCHRITT=3 (Rasterweite des Suchfelds)
|
||||
===================================================================== */
|
||||
import { readFileSync, writeFileSync, mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import {
|
||||
GRUND, MINDEST_ABSTAND, MINDEST_BUNTHEIT, HOECHST_BUNTHEIT,
|
||||
MINDEST_KONTRAST, FESTGELEGT,
|
||||
@@ -40,251 +60,239 @@ const DATEI = "workspace/assets/css/start.css";
|
||||
/* Etwas Luft ueber jeder Schwelle -- gerundet wird auf ganze
|
||||
Bildpunkte, und eine Farbe, die genau auf der Grenze steht, faellt
|
||||
beim naechsten Runden darunter. */
|
||||
const ZIEL_ABSTAND = MINDEST_ABSTAND + 0.004;
|
||||
const ZIEL_ABSTAND = Number(process.env.ABSTAND || MINDEST_ABSTAND) + 0.0005;
|
||||
const ZIEL_KONTRAST = MINDEST_KONTRAST + 0.15;
|
||||
const ZIEL_BUNT = MINDEST_BUNTHEIT + 0.004;
|
||||
const SCHRITT = Number(process.env.SCHRITT || 3);
|
||||
|
||||
/* ---------- Farbrechnung ---------------------------------------------- */
|
||||
const lin = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
||||
const srgb = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);
|
||||
function oklab(h) {
|
||||
const [r, g, b] = srgb(h).map(lin);
|
||||
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
||||
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
||||
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
||||
function labVon(r, g, b) {
|
||||
const rr = lin(r / 255), gg = lin(g / 255), bb = lin(b / 255);
|
||||
const l = Math.cbrt(0.4122214708 * rr + 0.5363325363 * gg + 0.0514459929 * bb);
|
||||
const m = Math.cbrt(0.2119034982 * rr + 0.6806995451 * gg + 0.1073969566 * bb);
|
||||
const s = Math.cbrt(0.0883024619 * rr + 0.2817188376 * gg + 0.6299787005 * bb);
|
||||
return [0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
||||
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
||||
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s];
|
||||
}
|
||||
const leuchte = (h) => { const [r, g, b] = srgb(h).map(lin); return 0.2126 * r + 0.7152 * g + 0.0722 * b; };
|
||||
const kontrast = (h) => (leuchte(h) + 0.05) / (leuchte(GRUND) + 0.05);
|
||||
const bunt = (h) => { const [, a, b] = oklab(h); return Math.hypot(a, b); };
|
||||
const abst = (a, b) => { const x = oklab(a), y = oklab(b); return Math.hypot(x[0] - y[0], x[1] - y[1], x[2] - y[2]); };
|
||||
const labHex = (h) => labVon(...[1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16)));
|
||||
const leuchteVon = (r, g, b) => 0.2126 * lin(r / 255) + 0.7152 * lin(g / 255) + 0.0722 * lin(b / 255);
|
||||
const kontrastVon = (y) => (y + 0.05) / (leuchteVon(...[1, 3, 5].map((i) => parseInt(GRUND.slice(i, i + 2), 16))) + 0.05);
|
||||
const buntVon = (L) => Math.hypot(L[1], L[2]);
|
||||
const d2 = (a, b) => (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2;
|
||||
const hexVon = (r, g, b) => "#" + [r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("");
|
||||
|
||||
/* ---------- Die Farben aus der Datei --------------------------------- */
|
||||
/* ---------- Die Farben aus der Datei ----------------------------------- */
|
||||
const css = readFileSync(DATEI, "utf8");
|
||||
const TOENE = [...css.matchAll(/\[data-ton="(\d+)"\]\s*\{\s*--ton:\s*(#[0-9a-fA-F]{6})/g)]
|
||||
.map((m) => ({ n: Number(m[1]), alt: m[2].toLowerCase(), hex: m[2].toLowerCase() }));
|
||||
if (TOENE.length < 20) { console.error("Muster passt nicht mehr"); process.exit(3); }
|
||||
for (const t of TOENE) t.altLab = labHex(t.alt);
|
||||
|
||||
/* ---------- Wer darf sich nicht bewegen, wer darf blass sein --------- */
|
||||
const FEST = new Map(FESTGELEGT.filter((f) => f.hex).map((f) => [f.ton, f.hex.toLowerCase()]));
|
||||
const BLASS_ERLAUBT = new Map(FESTGELEGT.filter((f) => f.blassErlaubt).map((f) => [f.ton, f.blassErlaubt]));
|
||||
|
||||
for (const t of TOENE) {
|
||||
if (FEST.has(t.n) && t.hex !== FEST.get(t.n)) {
|
||||
console.log(` Ton ${t.n} steht auf ${t.hex}, festgelegt ist ${FEST.get(t.n)} -- wird zurueckgesetzt`);
|
||||
console.log(` Ton ${t.n} stand auf ${t.hex}, festgelegt ist ${FEST.get(t.n)} -- zurueckgesetzt`);
|
||||
t.hex = FEST.get(t.n);
|
||||
}
|
||||
t.lab = labHex(t.hex);
|
||||
}
|
||||
|
||||
/* ---------- WELCHE TOENE WERDEN UEBERHAUPT BENUTZT? ------------------
|
||||
/* ---------- WELCHE TOENE WERDEN BENUTZT? -------------------------------
|
||||
|
||||
Die Buntheitsgrenze gilt nur fuer BENUTZTE Toene -- so steht es in
|
||||
pruef-kachelfarben, und das ist der Grund, aus dem die Reparatur
|
||||
ueberhaupt aufgeht: Mit Buntheit >= 0,12 fuer ALLE 45 ist ein
|
||||
Mindestabstand von 0,09 rechnerisch unmoeglich (gemessene Decke:
|
||||
0,0853). Die zwoelf unbenutzten duerfen blasser sein und nehmen
|
||||
damit Druck aus dem engen Blau-Tuerkis-Bereich.
|
||||
ALLE FUENFUNDVIERZIG. Und das ist der Befund, an dem der zweite
|
||||
Anlauf gescheitert ist.
|
||||
|
||||
Gefragt wird das Programm selbst, nicht eine Liste. */
|
||||
process.env.WORKSPACE_DB = process.env.WORKSPACE_DB
|
||||
|| join(mkdtempSync(join(tmpdir(), "toene-")), "workspace.db");
|
||||
const w = await import("../server/workspace.js");
|
||||
Wer das PROGRAMM fragt (bereicheFuer() je Rolle), bekommt 33. Die
|
||||
uebrigen zwoelf sehen dann aus wie freie Reserve -- sie duerfen
|
||||
blasser werden und weiter wandern. Sie sind keine Reserve: Fuer die
|
||||
fuenf Rollen des Agenturhauses gibt bereicheFuer() `null` zurueck,
|
||||
und das heisst „nimm die Liste aus dem Browser". Die steht in
|
||||
workspace/assets/js/bereiche.js, und dort stehen die Toene 1 bis 22
|
||||
und 44 -- genau die zwoelf.
|
||||
|
||||
Gemessen: Ton 1 traegt „Dashboard", Ton 2 „Zahlen", Ton 21
|
||||
„Scout-Pipeline", Ton 22 „Team-Lage". Alles Kacheln, die jeden Tag
|
||||
jemand ansieht.
|
||||
|
||||
Gezaehlt wird deshalb ueber dieselben Quellen, die auch
|
||||
pruef-kachel-universum liest. Die Frage lautet nicht „wem gibt der
|
||||
Server eine Kachel", sondern „steht diese Nummer irgendwo an einer
|
||||
Kachel". */
|
||||
const QUELLEN = ["../server/workspace.js", "../workspace/assets/js/bereiche.js",
|
||||
"../workspace/assets/js/start.js", "../workspace/assets/js/kopf.js",
|
||||
"../workspace/assets/js/befinden.js"];
|
||||
const BENUTZT = new Set();
|
||||
for (const r of ["admin", "hand", "linke", "modi", "gast", "manager", "scout", "creator", "spicy"]) {
|
||||
for (const haus of ["crew", "agentur"]) {
|
||||
let k = [];
|
||||
try { k = [...(w.bereicheFuer({ id: 1, rolle: r, name: "X" }, haus) || []),
|
||||
...(w.zusatzBereicheFuer({ id: 1, rolle: r, name: "X" }, haus) || [])]; }
|
||||
catch { continue; }
|
||||
for (const kachel of k) if (kachel?.ton != null) BENUTZT.add(Number(kachel.ton));
|
||||
for (const q of QUELLEN) {
|
||||
for (const m of readFileSync(new URL(q, import.meta.url), "utf8").matchAll(/ton:\s*(\d+)/g)) {
|
||||
BENUTZT.add(Number(m[1]));
|
||||
}
|
||||
}
|
||||
if (BENUTZT.size < 20) { console.error(`nur ${BENUTZT.size} benutzte Toene gefunden`); process.exit(5); }
|
||||
console.log(`${BENUTZT.size} von ${TOENE.length} Toenen werden benutzt: ${[...BENUTZT].sort((a, b) => a - b).join(", ")}`);
|
||||
/* Auch die unbenutzten sollen so bunt bleiben, wie es noch passt --
|
||||
sonst steht beim naechsten Gebrauch ein blasser Ton bereit und die
|
||||
Pruefung wird rot, obwohl niemand etwas falsch gemacht hat. */
|
||||
const UNBENUTZT_BUNT = Number(process.env.UNBUNT || 0.045);
|
||||
const buntNoetig = (n) => (BENUTZT.has(n) && !BLASS_ERLAUBT.has(n) ? ZIEL_BUNT
|
||||
: Math.min(UNBENUTZT_BUNT, bunt(TOENE.find((t) => t.n === n).alt)));
|
||||
console.log(`${BENUTZT.size} von ${TOENE.length} Toenen werden benutzt`);
|
||||
|
||||
/* Der verlangte Mindestabstand kann je Ton hoeher sein: Wer blass sein
|
||||
darf, muss das beim Abstand ausgleichen (steht so in
|
||||
pruef-kachelfarben und ist dort begruendet). */
|
||||
const abstandFuer = (n) => Math.max(ZIEL_ABSTAND, (BLASS_ERLAUBT.get(n) ?? 0) + 0.004);
|
||||
/* Wer blass sein darf, muss das beim Abstand ausgleichen -- so steht es
|
||||
mit Messung in kachelton-regeln.mjs. */
|
||||
const abstandFuer = (n) => Math.max(ZIEL_ABSTAND, (BLASS_ERLAUBT.get(n) ?? 0) + 0.0005);
|
||||
const BLASS_BIS = new Map(FESTGELEGT.filter((f) => f.blassBis).map((f) => [f.ton, f.blassBis]));
|
||||
const buntFuer = (n) => (BLASS_BIS.has(n) ? BLASS_BIS.get(n) + 0.002 : ZIEL_BUNT);
|
||||
|
||||
/* ---------- Alle erlaubten Farben ------------------------------------ */
|
||||
const ALLE = [];
|
||||
for (let r = 0; r <= 255; r += 2) {
|
||||
for (let g = 0; g <= 255; g += 2) {
|
||||
for (let b = 0; b <= 255; b += 2) {
|
||||
const h = hexVon(r, g, b);
|
||||
if (kontrast(h) < ZIEL_KONTRAST) continue;
|
||||
const c = bunt(h);
|
||||
/* ---------- Das Suchfeld ----------------------------------------------- */
|
||||
const KANDIDATEN = [];
|
||||
for (let r = 0; r <= 255; r += SCHRITT) {
|
||||
for (let g = 0; g <= 255; g += SCHRITT) {
|
||||
for (let b = 0; b <= 255; b += SCHRITT) {
|
||||
const rr = Math.min(255, r), gg = Math.min(255, g), bb = Math.min(255, b);
|
||||
if (kontrastVon(leuchteVon(rr, gg, bb)) < ZIEL_KONTRAST) continue;
|
||||
const L = labVon(rr, gg, bb);
|
||||
const c = buntVon(L);
|
||||
if (c > HOECHST_BUNTHEIT) continue;
|
||||
ALLE.push({ hex: h, bunt: c });
|
||||
KANDIDATEN.push({ hex: hexVon(rr, gg, bb), L, bunt: c });
|
||||
}
|
||||
}
|
||||
}
|
||||
const kandidatenFuer = (n) => ALLE.filter((x) => x.bunt >= buntNoetig(n)).map((x) => x.hex);
|
||||
console.log(`${ALLE.length} erlaubte Farben (Kontrast >= ${ZIEL_KONTRAST.toFixed(2)}, `
|
||||
+ `Buntheit bis ${HOECHST_BUNTHEIT}; benutzte Toene ab ${ZIEL_BUNT.toFixed(3)})`);
|
||||
console.log(`${KANDIDATEN.length} Farben im Suchfeld (Kontrast >= ${ZIEL_KONTRAST.toFixed(2)}, `
|
||||
+ `Buntheit bis ${HOECHST_BUNTHEIT}, Raster ${SCHRITT})`);
|
||||
|
||||
/* ---------- Was steht zu eng? ---------------------------------------- */
|
||||
const engstes = () => {
|
||||
let m = Infinity, wo = null;
|
||||
/* ---------- Messen ------------------------------------------------------ */
|
||||
function engstes() {
|
||||
let fehl = 0, klein = Infinity, wo = null;
|
||||
for (let i = 0; i < TOENE.length; i++) {
|
||||
for (let j = i + 1; j < TOENE.length; j++) {
|
||||
const noetig = Math.max(abstandFuer(TOENE[i].n), abstandFuer(TOENE[j].n));
|
||||
const d = abst(TOENE[i].hex, TOENE[j].hex);
|
||||
if (d < noetig && d - noetig < m) { m = d - noetig; wo = { i, j, d, noetig }; }
|
||||
const d = Math.sqrt(d2(TOENE[i].lab, TOENE[j].lab));
|
||||
if (d < noetig) fehl++;
|
||||
if (d < klein) { klein = d; wo = [TOENE[i], TOENE[j]]; }
|
||||
}
|
||||
}
|
||||
return wo;
|
||||
};
|
||||
return { fehl, klein, wo };
|
||||
}
|
||||
const zuBlass = () => TOENE.filter((t) =>
|
||||
buntVon(t.lab) < (BLASS_BIS.get(t.n) ?? MINDEST_BUNTHEIT));
|
||||
|
||||
const naechsterZu = (h, ausser) => {
|
||||
/** Wie gut steht dieser Ton gerade? (Abstand zum naechsten, in Einheiten
|
||||
* dessen, was er braucht -- >= 1 heisst „in Ordnung".) */
|
||||
function guete(lab, selbst) {
|
||||
let m = Infinity;
|
||||
for (const t of TOENE) { if (t === ausser) continue; m = Math.min(m, abst(h, t.hex)); }
|
||||
return m;
|
||||
};
|
||||
|
||||
console.log("");
|
||||
let bewegt = 0;
|
||||
for (let runde = 0; runde < 200; runde++) {
|
||||
const p = engstes();
|
||||
if (!p) break;
|
||||
const a = TOENE[p.i], b = TOENE[p.j];
|
||||
|
||||
/* Wer wird bewegt? Nicht der Festgelegte. Sonst der, fuer den die
|
||||
naechste erlaubte Stelle naeher an seinem eigenen Ton liegt. */
|
||||
const kandidatFuer = (t) => {
|
||||
if (FEST.has(t.n)) return null;
|
||||
let best = null, bestWeg = Infinity;
|
||||
const noetig = abstandFuer(t.n);
|
||||
for (const h of kandidatenFuer(t.n)) {
|
||||
const weg = abst(t.alt, h);
|
||||
if (weg >= bestWeg) continue;
|
||||
let ok = true;
|
||||
for (const u of TOENE) {
|
||||
if (u === t) continue;
|
||||
if (abst(h, u.hex) < Math.max(noetig, abstandFuer(u.n))) { ok = false; break; }
|
||||
}
|
||||
if (ok) { best = h; bestWeg = weg; }
|
||||
}
|
||||
return best ? { hex: best, weg: bestWeg } : null;
|
||||
};
|
||||
const ka = kandidatFuer(a), kb = kandidatFuer(b);
|
||||
if (!ka && !kb) {
|
||||
console.error(`Fuer ${a.hex} (Ton ${a.n}) und ${b.hex} (Ton ${b.n}) gibt es keinen Platz.`);
|
||||
process.exit(4);
|
||||
for (const u of TOENE) {
|
||||
if (u === selbst) continue;
|
||||
const noetig = Math.max(abstandFuer(selbst.n), abstandFuer(u.n));
|
||||
const v = Math.sqrt(d2(lab, u.lab)) / noetig;
|
||||
if (v < m) m = v;
|
||||
}
|
||||
/* WER WEICHT AUS? ZUERST DER, DEN NIEMAND SIEHT.
|
||||
|
||||
Gemessen: In JEDEM der acht zu engen Paare ist mindestens einer
|
||||
der beiden Toene gar nicht in Gebrauch -- 33 der 45 tragen eine
|
||||
Kachel, zwoelf stehen nur bereit. Wer den unbenutzten verschiebt,
|
||||
loest dasselbe Problem, ohne dass sich fuer irgendjemanden auch
|
||||
nur eine Kachel veraendert.
|
||||
|
||||
Das ist mehr als Bequemlichkeit: Eine Kachel, die ihre Farbe
|
||||
wechselt, muss neu gelernt werden. Wer zwei Jahre lang „das
|
||||
Tuerkise" angesteuert hat, sucht danach. Diese Kosten entstehen
|
||||
hier schlicht nicht.
|
||||
|
||||
Erst wenn beide benutzt sind (oder beide nicht), entscheidet der
|
||||
kuerzere Weg. */
|
||||
const frei = (t, k) => k && !BENUTZT.has(t.n);
|
||||
const nimm = frei(a, ka) && !frei(b, kb) ? { t: a, k: ka }
|
||||
: frei(b, kb) && !frei(a, ka) ? { t: b, k: kb }
|
||||
: !ka ? { t: b, k: kb } : !kb ? { t: a, k: ka }
|
||||
: (ka.weg <= kb.weg ? { t: a, k: ka } : { t: b, k: kb });
|
||||
console.log(` ${p.d.toFixed(4)} (noetig ${p.noetig.toFixed(3)}) Ton ${a.n} ${a.hex} <-> `
|
||||
+ `Ton ${b.n} ${b.hex} -> Ton ${nimm.t.n} wandert nach ${nimm.k.hex} `
|
||||
+ `(${nimm.k.weg.toFixed(3)})`);
|
||||
nimm.t.hex = nimm.k.hex;
|
||||
bewegt++;
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ---------- NACHZIEHEN -----------------------------------------------
|
||||
/* ---------- Der wachsende Spielraum ------------------------------------- */
|
||||
const anfang = engstes();
|
||||
console.log(`vorher: kleinster Abstand ${anfang.klein.toFixed(4)}, `
|
||||
+ `${anfang.fehl} Paare unter dem Soll, ${zuBlass().length} zu blass`);
|
||||
console.log("");
|
||||
|
||||
Der Durchgang oben arbeitet sich vom engsten Paar nach vorn und
|
||||
entscheidet dabei immer nur mit dem, was GERADE dasteht. Wer
|
||||
frueh ausweichen muss, weicht deshalb manchmal weiter aus als
|
||||
noetig -- die Stelle, die ihm gepasst haette, wird erst zwei
|
||||
Schritte spaeter frei.
|
||||
/* Der Spielraum waechst in jeder Runde ein wenig. Er MUSS wachsen,
|
||||
auch wenn sich gerade etwas bewegt hat: Sonst haengt ein Ton, der
|
||||
mehr Platz braucht als die anderen, fuer immer fest, waehrend die
|
||||
uebrigen noch hin und her rutschen -- gemessen blieben so vier zu
|
||||
blasse Toene stehen, obwohl das Verfahren noch lange nicht am
|
||||
Ende war. */
|
||||
let spielraum = 0.015;
|
||||
for (let runde = 0; runde < 300; runde++) {
|
||||
const lage = engstes();
|
||||
const blass = zuBlass();
|
||||
if (!lage.fehl && !blass.length) break;
|
||||
|
||||
Gemessen war das kein Schoenheitsfehler: Ton 10 (#ff6877, ein
|
||||
Korallrot) landete beim ersten Lauf auf #a86cb4, einem Violett --
|
||||
0,178 entfernt, und niemand haette die Kachel wiedererkannt.
|
||||
let bewegt = 0;
|
||||
for (const t of TOENE) {
|
||||
if (FEST.has(t.n)) continue;
|
||||
const jetztGuete = Math.min(guete(t.lab, t), 1);
|
||||
const jetztBunt = buntVon(t.lab);
|
||||
const bunkOk = jetztBunt >= (BLASS_BIS.get(t.n) ?? MINDEST_BUNTHEIT);
|
||||
if (jetztGuete >= 1 && bunkOk) continue; // dieser ist fertig
|
||||
|
||||
Also am Ende noch einmal von vorn: Jeder bewegte Ton darf so weit
|
||||
zu seiner alten Farbe zurueck, wie die Bedingungen es zulassen.
|
||||
Das aendert am Mindestabstand nichts -- es wird nur geprueft, ob
|
||||
es NAEHER geht. */
|
||||
const grenze2 = spielraum * spielraum;
|
||||
const mindBunt = buntFuer(t.n);
|
||||
/* IST DIE BUNTHEIT DAS PROBLEM, ZAEHLT DIE JETZIGE LAGE NICHT.
|
||||
|
||||
Hier stand `bestGuete = jetztGuete`. Fuer einen Ton, der weit
|
||||
genug von allen wegliegt (Guete 1) und nur zu blass ist, konnte
|
||||
damit KEIN Kandidat gewinnen -- kein Wert ist groesser als 1,
|
||||
wenn bei 1 gedeckelt wird. Vier Toene blieben deshalb blass
|
||||
stehen, obwohl das Suchfeld voller passender Farben war.
|
||||
|
||||
Mit -1 als Ausgangswert gewinnt jeder Kandidat, der ueberhaupt
|
||||
bunt genug ist -- und unter denen wieder der naechste. */
|
||||
let best = null, bestGuete = bunkOk ? jetztGuete : -1, bestWeg = 0;
|
||||
for (const k of KANDIDATEN) {
|
||||
if (k.bunt < mindBunt) continue;
|
||||
const weg2 = d2(t.altLab, k.L);
|
||||
if (weg2 > grenze2) continue;
|
||||
/* GEDECKELT BEI 1. „Weiter weg als noetig" ist kein Gewinn --
|
||||
ohne den Deckel sucht das Verfahren das Maximum und wandert
|
||||
dafuer quer durch den Farbraum. Mit ihm gewinnt unter allen
|
||||
Farben, die reichen, die NAECHSTE. */
|
||||
const g = Math.min(guete(k.L, t), 1);
|
||||
if (g > bestGuete + 1e-9 || (g > bestGuete - 1e-9 && best && weg2 < bestWeg)) {
|
||||
best = k; bestGuete = g; bestWeg = weg2;
|
||||
}
|
||||
}
|
||||
if (best && best.hex !== t.hex) {
|
||||
t.hex = best.hex; t.lab = best.L; bewegt++;
|
||||
}
|
||||
}
|
||||
spielraum += 0.0015;
|
||||
if (spielraum > 0.26) { console.error("Spielraum ausgeschoepft -- es geht nicht auf."); break; }
|
||||
}
|
||||
|
||||
/* ---------- Nachziehen: so weit zurueck wie moeglich --------------------- */
|
||||
for (let runde = 0; runde < 12; runde++) {
|
||||
let geruckt = false;
|
||||
for (const t of TOENE) {
|
||||
if (FEST.has(t.n) || t.hex === t.alt) continue;
|
||||
const jetzt = abst(t.alt, t.hex);
|
||||
const noetig = abstandFuer(t.n);
|
||||
let best = null, bestWeg = jetzt;
|
||||
for (const h of kandidatenFuer(t.n)) {
|
||||
const weg = abst(t.alt, h);
|
||||
if (weg >= bestWeg) continue;
|
||||
let ok = true;
|
||||
for (const u of TOENE) {
|
||||
if (u === t) continue;
|
||||
if (abst(h, u.hex) < Math.max(noetig, abstandFuer(u.n))) { ok = false; break; }
|
||||
}
|
||||
if (ok) { best = h; bestWeg = weg; }
|
||||
}
|
||||
if (best) {
|
||||
console.log(` nachgezogen: Ton ${t.n} ${t.hex} -> ${best} `
|
||||
+ `(${jetzt.toFixed(3)} -> ${bestWeg.toFixed(3)} vom alten Ton)`);
|
||||
t.hex = best; geruckt = true;
|
||||
const jetztWeg = d2(t.altLab, t.lab);
|
||||
const mindBunt = buntFuer(t.n);
|
||||
let best = null, bestWeg = jetztWeg;
|
||||
for (const k of KANDIDATEN) {
|
||||
if (k.bunt < mindBunt) continue;
|
||||
const weg2 = d2(t.altLab, k.L);
|
||||
if (weg2 >= bestWeg) continue;
|
||||
if (guete(k.L, t) < 1) continue;
|
||||
best = k; bestWeg = weg2;
|
||||
}
|
||||
if (best) { t.hex = best.hex; t.lab = best.L; geruckt = true; }
|
||||
}
|
||||
if (!geruckt) break;
|
||||
}
|
||||
|
||||
/* ---------- Nachmessen ------------------------------------------------ */
|
||||
let klein = Infinity, wo = null, unterSoll = 0;
|
||||
for (let i = 0; i < TOENE.length; i++) {
|
||||
for (let j = i + 1; j < TOENE.length; j++) {
|
||||
const d = abst(TOENE[i].hex, TOENE[j].hex);
|
||||
const noetig = Math.max(abstandFuer(TOENE[i].n), abstandFuer(TOENE[j].n));
|
||||
if (d < noetig) unterSoll++;
|
||||
if (d < klein) { klein = d; wo = [TOENE[i], TOENE[j]]; }
|
||||
}
|
||||
}
|
||||
/* ---------- Ergebnis ----------------------------------------------------- */
|
||||
const ende = engstes();
|
||||
const blassEnde = zuBlass();
|
||||
const geaendert = TOENE.filter((t) => t.hex !== t.alt);
|
||||
const zuBlass = TOENE.filter((t) => BENUTZT.has(t.n)
|
||||
&& bunt(t.hex) < MINDEST_BUNTHEIT && !BLASS_ERLAUBT.has(t.n));
|
||||
const zuDunkel = TOENE.filter((t) => kontrast(t.hex) < MINDEST_KONTRAST);
|
||||
const doppelt = TOENE.length - new Set(TOENE.map((t) => t.hex)).size;
|
||||
|
||||
console.log("");
|
||||
console.log(`geaendert ${geaendert.length} von ${TOENE.length}`);
|
||||
for (const t of geaendert) {
|
||||
console.log(`geaendert ${geaendert.length} von ${TOENE.length}`);
|
||||
for (const t of geaendert.sort((a, b) => d2(b.altLab, b.lab) - d2(a.altLab, a.lab))) {
|
||||
console.log(` Ton ${String(t.n).padStart(2)} ${t.alt} -> ${t.hex} `
|
||||
+ `verschoben ${abst(t.alt, t.hex).toFixed(3)} Buntheit ${bunt(t.hex).toFixed(3)} `
|
||||
+ `Kontrast ${kontrast(t.hex).toFixed(2)}:1`);
|
||||
+ `verschoben ${Math.sqrt(d2(t.altLab, t.lab)).toFixed(3)} `
|
||||
+ `Buntheit ${buntVon(t.lab).toFixed(3)} `
|
||||
+ `Kontrast ${kontrastVon(leuchteVon(...[1, 3, 5].map((i) => parseInt(t.hex.slice(i, i + 2), 16)))).toFixed(2)}:1`);
|
||||
}
|
||||
console.log(`kleinster Abstand ${klein.toFixed(4)} (Ton ${wo[0].n} ${wo[0].hex} / Ton ${wo[1].n} ${wo[1].hex})`);
|
||||
console.log(`unter dem Soll ${unterSoll}`);
|
||||
console.log(`zu blass ${zuBlass.map((t) => `Ton ${t.n} ${bunt(t.hex).toFixed(3)}`).join(", ") || "keiner"}`);
|
||||
console.log(`zu dunkel ${zuDunkel.map((t) => `Ton ${t.n}`).join(", ") || "keiner"}`);
|
||||
console.log(`doppelt ${doppelt}`);
|
||||
console.log(`kleinster Abstand ${ende.klein.toFixed(4)} `
|
||||
+ `(Ton ${ende.wo[0].n} ${ende.wo[0].hex} / Ton ${ende.wo[1].n} ${ende.wo[1].hex})`);
|
||||
console.log(`unter dem Soll ${ende.fehl}`);
|
||||
console.log(`zu blass ${blassEnde.map((t) => `Ton ${t.n} ${buntVon(t.lab).toFixed(3)}`).join(", ") || "keiner"}`);
|
||||
console.log(`doppelt ${doppelt}`);
|
||||
console.log(`groesste Aenderung ${geaendert.length ? Math.max(...geaendert.map((t) => Math.sqrt(d2(t.altLab, t.lab)))).toFixed(3) : "0"}`);
|
||||
for (const [n, h] of FEST) {
|
||||
const t = TOENE.find((x) => x.n === n);
|
||||
console.log(`festgelegt Ton ${n} ${t.hex} ${t.hex === h ? "(unveraendert)" : "*** ABWEICHUNG ***"}`);
|
||||
console.log(`festgelegt Ton ${n} ${t.hex} ${t.hex === h ? "(unveraendert)" : "*** ABWEICHUNG ***"}`);
|
||||
}
|
||||
|
||||
if (process.argv[2] === "schreiben") {
|
||||
if (unterSoll || zuBlass.length || zuDunkel.length || doppelt
|
||||
if (ende.fehl || blassEnde.length || doppelt
|
||||
|| [...FEST].some(([n, h]) => TOENE.find((x) => x.n === n).hex !== h)) {
|
||||
console.error("\nNICHT geschrieben -- das Ergebnis haelt die Bedingungen nicht.");
|
||||
process.exit(1);
|
||||
|
||||
@@ -108,6 +108,70 @@ const FESTGELEGT = [
|
||||
Haus ohnehin steht. Die Bedingung dazu steht unten bei der
|
||||
Pruefung und ist echt: Faellt der Abstand unter 0,10, wird diese
|
||||
Zeile rot. */
|
||||
{ ton: 44, kachel: "Support", hex: "#a8d8ff", blassErlaubt: 0.10,
|
||||
{ ton: 44, kachel: "Support", hex: "#a8d8ff", blassErlaubt: 0.10, blassBis: 0.070,
|
||||
warum: "Babyblau auf Wunsch -- blass, dafuer mit gemessenem Sicherheitsabstand" },
|
||||
|
||||
/* ==== TUERKIS GEHT NICHT BUNTER (26.09.2026) =====================
|
||||
|
||||
Ton 1 traegt „Dashboard" im Agenturhaus und liegt bei einer
|
||||
Buntheit von 0,112 -- acht Tausendstel unter der Hausgrenze.
|
||||
Aufgefallen ist das erst, als diese Pruefung endlich AUCH die
|
||||
Toene des Agenturhauses ansah; vorher kannte sie nur 33 von 45.
|
||||
|
||||
DIE NAHELIEGENDE ANTWORT WAERE, IHN BUNTER ZU MACHEN. Gemessen
|
||||
geht das nicht: Durchsucht man ALLE 16,7 Millionen sRGB-Farben
|
||||
nach der naechstgelegenen, die gleichzeitig Buntheit >= 0,124,
|
||||
Kontrast >= 4,65 und 0,09 Abstand zu allen 44 anderen haelt,
|
||||
kommt #7a75c8 heraus -- ein Blauviolett, 0,113 entfernt. Aus
|
||||
dem Tuerkis des Dashboards wuerde also Flieder.
|
||||
|
||||
Der Grund ist der Farbraum selbst: Tuerkis erreicht in sRGB nur
|
||||
eine geringe Buntheit, und dieses schmale Band teilen sich
|
||||
bereits die Toene 15, 16, 23, 26, 31, 32 und 43. Es ist voll.
|
||||
|
||||
ALSO EINE AUSNAHME MIT ZAHL, KEIN FREIBRIEF: `blassBis` sagt,
|
||||
wie blass er hoechstens sein darf -- blasser als heute darf er
|
||||
nicht werden. Und `blassErlaubt` fordert weiterhin den vollen
|
||||
Abstand ein. Eine Kachel, die ihre Farbfamilie behaelt, ist die
|
||||
bessere Antwort auf „richtig geil und speziell" als eine, die
|
||||
acht Tausendstel bunter ist und niemand wiedererkennt. */
|
||||
{ ton: 1, kachel: "Dashboard", blassErlaubt: 0.09, blassBis: 0.108,
|
||||
warum: "Tuerkis -- in diesem schmalen Band gibt es keine buntere Farbe mit Abstand" },
|
||||
|
||||
/* ==== DER SECHSUNDVIERZIGSTE TON (26.09.2026) ====================
|
||||
|
||||
Filipe: „fuer die modis, rechte und linke hand und dogfather
|
||||
eine kachel ... sie soll: Notizen, heissen."
|
||||
|
||||
GESUCHT WAR BERNSTEIN -- die Farbe von Papier. Gemessen gibt es
|
||||
sie nicht mehr: Durchsucht man alle sRGB-Farben im Bernsteinband
|
||||
(Farbwinkel 55 bis 100 Grad) nach der, die am weitesten von
|
||||
allen 45 vorhandenen wegliegt, kommt #d0ac48 heraus -- mit
|
||||
0,0799 Abstand, also UNTER der Hausgrenze von 0,09.
|
||||
|
||||
Und Platz zu schaffen hilft nicht: Setzt man diesen Bernstein
|
||||
fest und laesst die Nachbarn ausweichen, geht es zwar auf --
|
||||
aber ein warmer Ton muss dafuer das Band verlassen. Gemessen
|
||||
wanderte Ton 45 (Orange) 0,212 weit ins Magenta; haelt man auch
|
||||
den fest, trifft es Ton 40 mit 0,246. Das warme Band ist voll,
|
||||
und jede neue Farbe darin kostet eine vorhandene Kachel ihre
|
||||
Identitaet. Das ist ein schlechter Tausch.
|
||||
|
||||
DIE FREIEN LUECKEN, alle gemessen:
|
||||
|
||||
Gruen #9cde90 0,0917 <- genommen
|
||||
Violett #ca50ac 0,0913
|
||||
Blau #746afc 0,0890
|
||||
Tuerkis #66e2bc 0,0821
|
||||
Bernstein #d0ac48 0,0799
|
||||
|
||||
Gruen passt zur Sache besser, als es zunaechst klingt: Das
|
||||
Notizblatt im Haus traegt seine Linien in dieser Farbe, und ein
|
||||
linierter Block in Gruen ist Papier, seit es Papier gibt.
|
||||
|
||||
ER IST FESTGELEGT, weil die Kachel ihre eigene Oberflaeche hat
|
||||
(siehe `papier` in start.css): Ein Farblauf, der ihn verschiebt,
|
||||
wuerde Linien und Flaeche auseinanderlaufen lassen. */
|
||||
{ ton: 46, kachel: "Notizen", hex: "#9cde90",
|
||||
warum: "die groesste freie Luecke der Palette -- Bernstein gibt es nicht mehr" },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user