Files
dogfather-universe/tools/crew-buehnen.mjs
T
DogFatherGitandClaude Opus 5 241b408edc Eigene Buehnen fuer die Modi-App -- und die Kopfleiste dazu
Filipe: "auch die hintergrund bilder vom crew espace sollen anderes
aussehen, der community angepasst wie die zugangscode seite die ist
mega."

UMFAERBEN HAETTE HIER NICHT GEREICHT. Bei der Zugangswand war Rot das
Problem; innen zeigen die neun Buehnen das SPICY-MEDIA-LOGO selbst, in
`halle` und `arena` vielfach nebeneinander. Ein blaues fremdes Logo
waere schlimmer gewesen als ein rotes.

Die Motive gibt es laengst -- auf der oeffentlichen Dogfather-Seite:
  studio/lounge  "Team Dogi -- Familie. Treue. Zusammenhalt."
  halle          TEAM DOGI mit den Menschen des Teams
  skyline/portal "Streamplan": Dogi und HasiDog auf der Buehne
  arena          "DogFathers Galerie": Filmrollen, ein Archiv
  garage         "Der Streamer": Filipe an seinem Platz

Fuer `garage` stand zuerst der Holo-Kontrollraum da -- grossartiges
Bild, aber darauf steht "WERDE MODI". Eine Einladung hinter dem
Aufgabenbrett von Leuten, die laengst dabei sind, liest sich falsch.
Ein Bild sagt etwas, auch wenn es nur Hintergrund ist.

DIE ABDUNKLUNG IST GERECHNET, NICHT GEWAEHLT
Mein erster fester Wert haette 18 von 27 Fassungen HELLER gemacht als
die, die sie ersetzen (114 gegen 67) -- ueber ihnen steht derselbe
kleine Text. Das Werkzeug misst jetzt jede Fassung gegen die bestehende
Buehne derselben Szene und senkt sie genau auf deren Wert.

Ueber eine KURVE statt eines schwarzen Schleiers: v' = 255*(v/255)^g
senkt die Lichter viel staerker als die Tiefen -- das Bild wird dunkler
und behaelt seine Zeichnung. Ein Schleier haette bei den hellsten
Motiven ueber 80 % gebraucht, und darunter liegt dann Nebel statt Bild
(derselbe Fehler wie beim Tresorbild).

Der erste Anlauf mit einer geschlossenen Formel war falsch: Die Kurve
wirkt auf jeden FARBKANAL, die Helligkeit ist eine gewichtete Summe der
drei, und L(v^g) ist nicht L(v)^g. Die Rechnung sagte 61,5 und heraus
kamen 67. Jetzt wird genaehert und nachgemessen, hoechstens sechsmal.

Beim Hochkant-Ausschnitt wird zusaetzlich das FENSTER GESUCHT: fuenf
Kandidaten, erst "dunkel genug", dann "am meisten Motiv". Die Mitte --
die das Haus sonst nimmt -- ist bei diesen Bildern das Hellste.

ZWEI FUNDE AUS DEM BILDSCHIRMFOTO, die nichts mit den Buehnen zu tun
hatten:

1. DIE KOPFLEISTE sagte "SPICY & DOGI · CREATOR WORKSPACE". Reitertitel,
   Begruessung und Symbol waren laengst richtig -- nur die Zeile, die
   man als Erstes ansieht, nicht. Der Grund: Auf Unterseiten traegt die
   Leiste einen Rueckweg-Verweis, auf der Startseite blossen Text; mein
   Code kannte nur die erste Bauform.

2. DAS CHILI-ZEICHEN kommt aus dem CSS als Bild, an drei Stellen
   (Kopfleiste, Siegel im Ring, die schwebenden Wasserzeichen). Auf
   crew. wird die Datei serverseitig durch den Husky ersetzt -- alle
   drei auf einmal, und ohne Flackern, weil schon der erste Abruf das
   richtige Bild bekommt.

GEMESSEN
pruef-crew-adresse  123 (statt 117): Fuer JEDE Buehne aus start.css --
                    die Namen werden dort GELESEN, nicht abgeschrieben --
                    gibt es auf crew. ein eigenes Motiv, auf workspace.
                    das alte, und die beiden sind nachweislich
                    verschiedene Dateien.
pruef-rollen        274   pruef-start-ansicht, pruef-zwischenspeicher 21
pruef-modi-wortleck   5 -- er hat wieder angeschlagen, zweimal auf
                    Kommentare, die ich selbst geschrieben hatte.

27 Fassungen, 3,5 MB (die bisherigen neun Szenen brauchen 7,1).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-10 17:02:54 +02:00

447 lines
20 KiB
JavaScript

/* ===================================================================
Die Buehnen der Modi-App (crew.dogfather-universe.com, 10.09.2026)
Filipe: "auch die hintergrund bilder vom crew espace sollen anderes
aussehen, der community angepasst wie die zugangscode seite die ist
mega."
---------------------------------------------------------------------
WARUM UMFAERBEN HIER NICHT GENUEGT
Bei der Zugangswand hat es gereicht, die Szene ins Blau zu ziehen --
dort war Rot das Problem. INNEN ist es etwas anderes: Die neun
Buehnen zeigen nicht bloss rotes Licht, sondern das SPICY-MEDIA-LOGO
selbst, in `halle` und `arena` sogar vielfach nebeneinander. Ein
blaues Spicy-Logo waere schlimmer als ein rotes: Es saehe aus, als
haette jemand die falsche Marke eingefaerbt.
Die Modi-App braucht deshalb eigene Motive -- und die gibt es
laengst. Auf der oeffentlichen Dogfather-Seite liegen die
Community-Bilder, die genau das zeigen, was in dieser App passiert.
---------------------------------------------------------------------
WELCHES BILD WOHIN -- und das folgt dem, was auf der Seite passiert
studio Startseite "Team Dogi -- Familie. Treue.
Zusammenhalt." Filipe mit seinem
Husky. Der Eingang gehoert dem,
worum es geht.
lounge Chat, Community dasselbe Motiv: Wo geredet wird,
steht das Miteinander im Bild.
halle Angebote, Eingang, TEAM DOGI mit den Menschen des
Steckbrief Teams -- hier geht es um sie.
skyline Kalender "Streamplan": Dogi und HasiDog
auf der Buehne. Woertlich das
Thema der Seite.
portal Ideen-Board, dieselbe Buehne -- dort wird
Live-Ablauf gesendet, und dorthin gehen die
Ideen.
showbuehne Dashboard, Reports ebenfalls die Buehne.
arena Dateien, Wissen "DogFathers Galerie": Filmrollen
und Bildschirme. Ein Archiv sieht
so aus.
garage Aufgaben, Technik "Der Streamer": Filipe an seinem
Platz, Monitore, Ringlicht. Dort
wird gearbeitet.
ZUERST STAND HIER DER HOLO-KONTROLL-
RAUM, und er sah grossartig aus --
aber darauf steht "WERDE MODI".
Eine Einladung hinter dem
Aufgabenbrett von Leuten, die
laengst dabei sind, liest sich
falsch. Ein Bild sagt etwas, auch
wenn es nur Hintergrund ist.
wald Start-Check wieder das Familienmotiv; die
Seite gibt es fuer diese Rollen
gar nicht und sie ist nur da,
damit kein Rueckfall auf eine
Spicy-Buehne moeglich ist.
FUENF BILDER AUF NEUN PLAETZE, mit denselben Regeln wie im Haus:
Paare, die inhaltlich benachbart sind und nie nebeneinander auf einer
Seite stehen -- man sieht immer nur eins.
ALLE NEUN werden gebaut, auch die, die eine Modi-Rolle nie zu sehen
bekommt. Der Grund steht in `start.css`: Dort ist `studio` der
Rueckfall fuer jede Seite ohne eigene Szene. Eine Luecke hier hiesse,
dass irgendwann doch ein Spicy-Logo im Hintergrund der Modi-App
steht -- und zwar genau auf der Seite, an die niemand gedacht hat.
---------------------------------------------------------------------
DIE GEGENPROBE: NICHT HELLER ALS DAS, WAS SCHON LAEUFT
Ueber diesen Bildern steht Text. Die Dogfather-Motive sind heller als
die Spicy-Szenen -- weisse Schrift im Bild, helles Fell, Scheinwerfer.
Ein Bild, das gut aussieht und die Schrift darueber unlesbar macht,
ist der schnellste Weg zu einer Oberflaeche, die niemand benutzt.
Deshalb wird jede fertige Fassung gegen die BESTEHENDE gleicher Szene
und Groesse gehalten: Das obere Zehntel der Helligkeit im mittleren
Band -- dort, wo der Inhalt steht -- darf nicht heller sein als dort.
Das ist kein selbst gewaehlter Grenzwert, sondern der Stand, der seit
Wochen laeuft und den pruef-buehne an echten Bildpunkten gemessen hat.
Aufrufen mit: node tools/crew-buehnen.mjs
=================================================================== */
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const WURZEL = join(dirname(fileURLToPath(import.meta.url)), "..");
const QUELLE = join(WURZEL, "assets", "img");
const ZIEL = process.env.BUEHNEN_ZIEL || join(WURZEL, "workspace", "assets", "img");
/* Zum Vergleichen immer die echten -- auch wenn das Ziel umgelenkt ist. */
const VERGLEICH = join(WURZEL, "workspace", "assets", "img");
const SZENEN = {
studio: "bg-community.jpg",
lounge: "bg-community.jpg",
wald: "bg-community.jpg",
halle: "bg-modis.jpg",
skyline: "bg-streamplan.jpg",
portal: "bg-streamplan.jpg",
showbuehne: "bg-streamplan.jpg",
arena: "bg-dogfather.jpg",
garage: "bg-streamer.jpg",
};
/* WIE STARK ABGEDUNKELT -- AUSGERECHNET, NICHT GEWAEHLT.
Erster Anlauf: ein fester Wert fuer alle, 0,34. Die Messung unten
hat ihn zerlegt -- achtzehn von siebenundzwanzig Fassungen waren
heller als die Buehne, die sie ersetzen, teils deutlich (114 gegen
67). Ueber ihnen steht derselbe kleine Text; ich haette Bilder
ausgeliefert, die gut aussehen und die Schrift darueber schlechter
lesbar machen.
EIN FESTER WERT KANN DAS AUCH NICHT LOESEN. Die fuenf Motive sind
verschieden hell: "Team Dogi" ist eine Nacht mit einem Ring, der
Holo-Kontrollraum ist von vorne bis hinten beleuchtet. Was fuer das
eine reicht, macht das andere zum Schemen -- und genau das ist beim
Tresorbild schon einmal passiert (0,62 uebernommen, man sah nichts
mehr).
Also wird je Fassung gerechnet: Erst ohne Abdunklung bauen und
messen, dann ausrechnen, wie viel Schwarz noetig ist, um genau auf
den Wert der bestehenden Buehne zu kommen -- und nur so viel.
Schwarz mit Deckkraft a macht aus L ein L*(1-a); daraus folgt
a = 1 - Ziel/Ist. So dunkel wie noetig, nicht dunkler. */
const GAMMA_MAX = 3.2; // darueber bleibt vom Motiv ein Schemen
/** Ein ERSTER Schaetzwert fuer die Kurve.
*
* Aus v' = 255*(v/255)^g folgt g = ln(Ziel/255) / ln(Ist/255) -- das
* gilt aber nur fuer GRAUE Bildpunkte. Die Kurve wirkt auf jeden
* Farbkanal einzeln, die Helligkeit ist eine gewichtete Summe der
* drei, und L(v^g) ist nicht L(v)^g, sobald ein Bildpunkt farbig ist.
*
* Genau daran ist der erste Anlauf gescheitert: Die Rechnung sagte
* 61,5 und heraus kamen 67. Deshalb ist das hier nur der Startwert --
* danach wird nachgemessen und nachgezogen, bis es stimmt. */
function gammaFuer(ist, ziel) {
if (!(ist > 0) || ist >= 255 || ziel <= 0) return 1;
const g = Math.log(ziel / 255) / Math.log(ist / 255);
return Math.min(Math.max(g, 1), GAMMA_MAX);
}
/**
* Baut so lange nach, bis der Spitzenwert das Ziel erreicht.
*
* ZWEI WERKZEUGE, IN DIESER REIHENFOLGE:
* 1. Die Kurve -- sie senkt die Lichter viel staerker als die Tiefen
* und laesst dem Bild seine Zeichnung.
* 2. Erst wenn die Kurve am Anschlag ist (GAMMA_MAX), kommt ein
* flacher schwarzer Schleier dazu. Er senkt alles gleich und legt
* Nebel ueber das Bild -- deshalb so spaet und so wenig wie
* moeglich.
*
* Hoechstens sechs Anlaeufe. Ein Naeherungsverfahren ohne Deckel ist
* eine Endlosschleife mit Umweg.
*/
async function annaehern(bauArgs, ziel) {
let gamma = 1, dunkel = 0;
let r = await bauen({ ...bauArgs, gamma, dunkel });
gamma = gammaFuer(r.spitze, ziel);
for (let runde = 0; runde < 6; runde++) {
r = await bauen({ ...bauArgs, gamma, dunkel });
if (r.spitze <= ziel + 1) break;
if (gamma < GAMMA_MAX) {
/* Noch einmal dieselbe Rechnung, diesmal vom gemessenen Stand
aus -- die Kurven verketten sich. */
gamma = Math.min(gamma * gammaFuer(r.spitze, ziel), GAMMA_MAX);
} else {
/* Kurve am Anschlag: den Rest flach wegnehmen. Schwarz mit
Deckkraft a macht aus L ein L*(1-a) -- und das gilt fuer
Farbe genauso wie fuer Grau, hier rechnet nichts falsch. */
dunkel = Math.min(1 - (1 - dunkel) * (ziel / r.spitze), 0.8);
}
}
return { ...r, gamma, dunkel };
}
/* WIE STARK GEKUEHLT.
Die Motive sind unterschiedlich warm: "Team Dogi" hat einen orangen
Ring, "TEAM DOGI mit den Menschen" ist Feuer. Nebeneinander in einer
App wirken sie wie fuenf verschiedene Programme.
Ein Drittel Blau ueber den Mischmodus "color" zieht sie zusammen,
ohne sie zu faelschen -- das Feuer bleibt Feuer, es glueht nur
kuehler. Ganz blau waere die Zugangswand, und die ist eine Buehne
fuer EIN Motiv; hier sind es fuenf, die zusammenpassen muessen. */
const KUEHLE = 0.32;
for (const datei of new Set(Object.values(SZENEN))) {
const p = join(QUELLE, datei);
if (!existsSync(p)) { console.error(`Fehlt: ${p}`); process.exit(1); }
}
const alsDaten = (p) => "data:image/jpeg;base64," + readFileSync(p).toString("base64");
const { chromium } = await import(
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
const browser = await chromium.launch();
const seite = await (await browser.newContext({ deviceScaleFactor: 1 })).newPage();
await seite.setViewportSize({ width: 400, height: 300 });
if (!existsSync(ZIEL)) mkdirSync(ZIEL, { recursive: true });
/** Baut eine Fassung und misst sie gleich mit. */
async function bauen({ quelle, breite, ausschnitt = null, gamma = 1, dunkel = 0, guete }) {
const roh = await seite.evaluate(async (a) => {
const i = new Image(); i.src = a.daten; await i.decode();
const qx = a.ausschnitt ? a.ausschnitt[0] * i.width : 0;
const qy = a.ausschnitt ? a.ausschnitt[1] * i.height : 0;
const qb = a.ausschnitt ? a.ausschnitt[2] * i.width : i.width;
const qh = a.ausschnitt ? a.ausschnitt[3] * i.height : i.height;
const zb = a.breite;
const zh = Math.round(zb * (qh / qb));
const c = document.createElement("canvas");
c.width = zb; c.height = zh;
const g = c.getContext("2d");
g.imageSmoothingQuality = "high";
g.filter = "contrast(1.06) saturate(1.04)";
g.drawImage(i, qx, qy, qb, qh, 0, 0, zb, zh);
g.filter = "none";
/* Kuehlen: Farbton und Saettigung von der Farbe, Helligkeit vom
Bild -- jede Kante und jeder Funke bleibt stehen. */
g.globalCompositeOperation = "color";
g.globalAlpha = a.kuehle;
g.fillStyle = "#2a5f8c";
g.fillRect(0, 0, zb, zh);
g.globalAlpha = 1;
g.globalCompositeOperation = "source-over";
/* Vignette: aussen dunkler, damit die Raender der Seite ruhig
bleiben und die Mitte trotzdem etwas zeigt. */
const v = g.createRadialGradient(zb / 2, zh / 2, Math.min(zb, zh) * 0.26,
zb / 2, zh / 2, Math.max(zb, zh) * 0.74);
v.addColorStop(0, "rgba(0,0,0,0)");
v.addColorStop(0.6, "rgba(0,0,0,.18)");
v.addColorStop(1, "rgba(0,0,0,.46)");
g.fillStyle = v;
g.fillRect(0, 0, zb, zh);
/* ===================================================================
ABDUNKELN MIT EINER KURVE, NICHT MIT EINEM SCHLEIER
Ein flaches Schwarz mit Deckkraft a macht aus jedem L ein
L*(1-a): Es senkt alles im selben Verhaeltnis. Bei den hellsten
Motiven waeren dafuer ueber 80 % noetig gewesen -- und darunter
liegt dann ein grauer Nebel statt eines Bildes. Genau das ist
beim Tresorbild schon einmal passiert.
Eine Gammakurve senkt die Lichter viel staerker als die Tiefen:
v' = 255 * (v/255)^g. Bei g = 1,8 faellt 200 auf 165 (-17 %),
aber 60 auf 19 (-69 %). Das Bild wird dunkler UND behaelt seine
Zeichnung -- die hellen Stellen bleiben die hellen, sie
schreien nur nicht mehr.
`g` wird nicht geraten: Aus dem gemessenen Spitzenwert p und dem
Ziel folgt g = ln(Ziel/255) / ln(p/255). Die Tabelle wird einmal
gefuellt und dann 256-fach nachgeschlagen statt millionenfach
gerechnet. */
if (a.gamma && a.gamma !== 1) {
const tabelle = new Uint8ClampedArray(256);
for (let v = 0; v < 256; v++) tabelle[v] = Math.round(255 * (v / 255) ** a.gamma);
const bd = g.getImageData(0, 0, zb, zh);
const px = bd.data;
for (let k = 0; k < px.length; k += 4) {
px[k] = tabelle[px[k]];
px[k + 1] = tabelle[px[k + 1]];
px[k + 2] = tabelle[px[k + 2]];
}
g.putImageData(bd, 0, 0);
}
/* Der flache Schleier -- nur, wenn die Kurve nicht gereicht hat. */
if (a.dunkel > 0) {
g.fillStyle = `rgba(0,0,0,${a.dunkel})`;
g.fillRect(0, 0, zb, zh);
}
/* MESSUNG: das obere Zehntel der Helligkeit im MITTLEREN BAND.
Dort steht der Inhalt; die Raender sind ohnehin dunkel und
wuerden den Wert schoenrechnen. */
const by = Math.round(zh * 0.18), bh = Math.round(zh * 0.64);
const d = g.getImageData(0, by, zb, bh).data;
const hell = [];
for (let k = 0; k < d.length; k += 4) {
hell.push(0.2126 * d[k] + 0.7152 * d[k + 1] + 0.0722 * d[k + 2]);
}
hell.sort((x, y) => x - y);
/* Die Streuung sagt, wie viel MOTIV in dem Ausschnitt steckt.
Eine leere dunkle Ecke ist zwar schoen dunkel -- und zeigt
nichts. */
let summe = 0, quadrate = 0;
for (const h of hell) { summe += h; quadrate += h * h; }
const mittel = summe / hell.length;
return {
bild: c.toDataURL("image/webp", a.guete),
spitze: hell[Math.floor(hell.length * 0.9)],
streuung: Math.sqrt(Math.max(quadrate / hell.length - mittel * mittel, 0)),
breite: zb, hoehe: zh,
};
}, { daten: alsDaten(quelle), breite, ausschnitt, gamma, dunkel, guete, kuehle: KUEHLE });
return { ...roh, puffer: Buffer.from(roh.bild.split(",")[1], "base64") };
}
/** Dieselbe Messung an einer BESTEHENDEN Datei -- der Massstab. */
async function messen(pfad) {
if (!existsSync(pfad)) return null;
return await seite.evaluate(async (b64) => {
const i = new Image(); i.src = "data:image/webp;base64," + b64; await i.decode();
const c = document.createElement("canvas");
c.width = i.width; c.height = i.height;
const g = c.getContext("2d");
g.drawImage(i, 0, 0);
const by = Math.round(i.height * 0.18), bh = Math.round(i.height * 0.64);
const d = g.getImageData(0, by, i.width, bh).data;
const hell = [];
for (let k = 0; k < d.length; k += 4) {
hell.push(0.2126 * d[k] + 0.7152 * d[k + 1] + 0.0722 * d[k + 2]);
}
hell.sort((x, y) => x - y);
return hell[Math.floor(hell.length * 0.9)];
}, readFileSync(pfad).toString("base64"));
}
const maengel = [];
let gebaut = 0, verglichen = 0, gesamt = 0;
console.log("\n--- Die neun Buehnen der Modi-App ---");
for (const [szene, datei] of Object.entries(SZENEN)) {
const quelle = join(QUELLE, datei);
const zeile = [];
for (const [name, breite, guete] of [
["uhd", 2400, 0.88],
["breit", 1600, 0.88],
/* HOCHKANT: die Mitte, hochkant beschnitten -- ein auf 9:16
gequetschtes Breitbild zeigt sonst nur Rand. Und dunkler: Auf dem
Handy liegen die kleinsten Texte der ganzen Anwendung genau in
der Bildmitte. Dieselbe Ueberlegung wie bei den Spicy-Buehnen,
dort am 07.09.2026 an echten Bildpunkten gemessen. */
["schmal", 900, 0.87],
]) {
/* Der Massstab: dieselbe Szene, dieselbe Groesse, wie sie seit
Wochen laeuft. Ohne ihn ist "nicht heller" eine Behauptung --
deshalb ist sein Fehlen ein FEHLER und kein Ueberspringen. */
const alt = await messen(join(VERGLEICH, `buehne-${szene}-${name}.webp`));
if (alt === null) {
maengel.push(`${szene}/${name}: keine bestehende Fassung zum Vergleichen`);
continue;
}
verglichen++;
/* ===================================================================
DER HOCHKANT-AUSSCHNITT WIRD GESUCHT, NICHT GESETZT
Das Haus schneidet fuer das Handy die MITTE heraus -- bei den
Spicy-Szenen richtig, dort steht das Logo mittig und der Grund
ringsum ist dunkel. Bei den Dogfather-Motiven ist die Mitte
genau das Hellste: "STREAMPLAN" in weisser Schrift, die
Scheinwerfer, der beleuchtete Kontrollraum.
Fuenf von neun Fassungen blieben deshalb auch mit 66 % Schwarz
zu hell -- und 81 % waeren noetig gewesen, also ein Schemen.
Jetzt werden fuenf Fenster durchprobiert und zwei Fragen
gestellt, in dieser Reihenfolge:
1. Wird es mit hoechstens 66 % Schwarz dunkel genug? (Pflicht)
2. In welchem steckt am meisten Motiv? (Kuer, ueber die
Streuung)
So viel Motiv wie moeglich, so dunkel wie noetig. Faellt Frage 1
ueberall durch, wird das dunkelste genommen UND gemeldet -- die
Messung ganz unten schlaegt dann ohnehin an. */
let ausschnitt = null;
if (name === "schmal") {
/* Das Fenster wird gesucht: erst dunkel genug, dann so viel
Motiv wie moeglich. */
let bester = null;
for (const x of [0, 0.11, 0.22, 0.33, 0.44]) {
const fenster = [x, 0, 0.56, 1];
const probe = await bauen({ quelle, breite, ausschnitt: fenster, guete });
const g = gammaFuer(probe.spitze, alt);
const passt = g < GAMMA_MAX;
if (!bester
|| (passt && !bester.passt)
|| (passt === bester.passt
&& (passt ? probe.streuung > bester.probe.streuung
: probe.spitze < bester.probe.spitze))) {
bester = { fenster, probe, passt };
}
}
ausschnitt = bester.fenster;
}
const r = await annaehern({ quelle, breite, ausschnitt, guete }, alt);
const { gamma, dunkel } = r;
writeFileSync(join(ZIEL, `crew-buehne-${szene}-${name}.webp`), r.puffer);
gebaut++;
gesamt += r.puffer.length;
/* Und danach NOCH EINMAL messen. Die Rechnung sagt, was noetig
waere; ob es gereicht hat, sagt nur das fertige Bild -- WebP
rundet, und die Deckelung bei 0,66 kann greifen. */
if (r.spitze > alt + 2) {
maengel.push(`${szene}/${name}: bleibt heller als die bestehende Buehne `
+ `(${r.spitze.toFixed(1)} gegen ${alt.toFixed(1)}, Kurve ${gamma.toFixed(2)}, `
+ `Schleier ${(dunkel * 100).toFixed(0)} %)`);
}
zeile.push(`${name} ${Math.round(r.puffer.length / 1024)} KB `
+ `· Kurve ${gamma.toFixed(2)}${dunkel ? ` +${(dunkel * 100).toFixed(0)} %` : ""}`
+ ` · ${r.spitze.toFixed(0)}/${alt.toFixed(0)}`
+ (ausschnitt ? ` · ab ${(ausschnitt[0] * 100).toFixed(0)} %` : ""));
}
console.log(` ${szene.padEnd(11)} ${datei.padEnd(22)} ${zeile.join(" · ")}`);
}
await browser.close();
/* Die Zahlen gehoeren in die Bedingung, nicht nur in den Bericht. */
const soll = Object.keys(SZENEN).length * 3;
if (gebaut !== soll) {
console.error(`\nFEHLER: ${gebaut} von ${soll} Fassungen erzeugt.`); process.exit(1);
}
if (verglichen !== soll) {
console.error(`\nFEHLER: nur ${verglichen} von ${soll} gegen eine bestehende Buehne gehalten.`);
process.exit(1);
}
if (maengel.length) {
console.error("\nFEHLER:\n " + maengel.join("\n ")); process.exit(1);
}
console.log(`\n${gebaut} Fassungen, ${Math.round(gesamt / 1024 / 1024 * 10) / 10} MB.`
+ ` Keine ist heller als die bestehende Buehne derselben Szene --`
+ ` der Text darueber bleibt also mindestens so lesbar wie bisher.`);