# VEKTOR — Befund

Zweig `vektor`, Arbeitsbaum `/root/osum-vektor`, ausgehend von
`44a5af3` (merge9 nach ECHTHARDWARE-4). Nichts nach GitHub gepusht,
nichts nach merge9 zurueckgeschrieben.

---

## 1. Portieren oder neu bauen — die Entscheidung

**Ergebnis: neu bauen, in Osums eigener Festkommaart, mit den
Verfahren aus Firn als Vorlage.** Die lange Fassung steht in
`BEFUND-VEKTOR-ENTSCHEIDUNG.md` im Baum; hier das Gemessene.

Firn hat unter `lib/paint` echte Arbeit liegen (painter 1241 Zeilen,
stroke 833, canvas 744, font/raster 928). Portieren waere richtig,
wenn es ginge. Es geht nicht, und zwar aus einem Grund, der
niedergeschrieben ist:

| Datei | `grep -c f64` |
|---|---:|
| `lib/paint/painter.fi` | 178 |
| `lib/paint/stroke.fi` | 101 |
| `lib/font/raster.fi` | 83 |

Gegenprobe in Osum, ueber **alle 109** `kernel/*.fi`:

```
$ grep -l 'f64' kernel/*.fi | wc -l
0
```

`docs/ROUNDK10W.md`, Abschnitt 4.2:

> Es gibt keine Gleitkommazahlen in diesem Kernel und es soll keine
> geben: `profile kernel` hat keine SSE-Rettung im Kontextwechsel, und
> eine Rasterung, die je nach Rundungsmodus anders aussieht, ist nicht
> nachpruefbar.

Dazu: `painter.fi` haengt an `html.mem`, `browser.node`, `css.cascade`,
`layout.box` — am Anzeigebaum eines Browsers, den ein Kernel nicht hat.
Und unter `profile kernel` ist Arithmetik geprueft (SPEC 13 L9), jede
Addition muesste umgeschrieben werden.

Ein "Port" waere also: jede Zeile anfassen, jede Zahl auf 26.6
umstellen, jede Addition pruefen, die Fremdbezuege herausschneiden —
eine Neuschrift mit fremder Vorlage, die die Erprobung *nicht* erbt
(erprobt ist der `f64`-Code, nicht der umgeschriebene).

**Uebernommen** sind die Verfahren: die Bogenzerlegung ueber
kappa = 0.5522847 (in 26.6 als `KAPPA = 35`), die Strichlogik aus
`stroke.fi` (Normale je Abschnitt, Ecke, Ende) und die Trennung
"Deckung sammeln, dann mischen" aus `painter.fi`.

**Was Osum schon hatte** und was diese Runde zusammenfuehrt: drei
Verfahren fuer dieselbe Frage — `ttf.fi` (Glyphen, 26.6, 4x4),
`zeiger.fi` (Vieleck, 3x3), `wm.fi:corner_cov` (Kreis, 4x4). Die
Rechenart stand damit fest und ist erprobt.

---

## 2. Was gebaut wurde

**`kernel/vektor.fi`** — der Unterbau. Pfade aus Linien, quadratischen
und kubischen Bezier; Rechteck mit Eckradius, Kreis, Ellipse,
abgerundetes Vieleck; Fuellen nach Nichtnull und Gerade-Ungerade mit
4x4-Unterabtastung (17 Stufen, Deckung `(cov*255+8)/16`); Strich mit
Breite, Enden und Ecken; Beschnitt auf Rechteck und auf Pfad;
Farbverlauf linear und radial; Zwischenspeicher fuer gerasterte Formen.
Alles in 26.6, feste Puffer, kein Allokator.

**`kernel/user/wlibc.fi`** — dieselbe Rechenart fuer Ring 3
(`vrect`, `vkreis`, `vring`). Eine zweite Fassung ist noetig, weil
`kernel/user/*.fi` in eigene Objektdateien uebersetzt wird und nichts
aus dem Kern rufen darf — dieselbe Regel wie in `uprog.fi`. Gleiche
Konstanten, gleiche rundende Division: ein Knopf und der Fensterrahmen
daneben runden gleich.

**`tools/design/vektorpruef.py`** — 66 Pruefungen, alle gruen.

---

## 3. Der Selbsttest hat drei echte Fehler gefunden

Das ist das Ergebnis, auf das es ankommt: alle drei waeren sonst
durchgerutscht.

### 3.1 Bezierzerlegung teilte abschneidend

Von 65 Punkten eines Kreises hatten **56 keinen Spiegelpartner**.
Abschneiden zieht immer zur Null, also wandern gespiegelte Bogen in
dieselbe Richtung statt auseinander. Mit rundender Division (`rdiv`):
**0 von 65**.

### 3.2 Der Schnittpunkt Kante/Abtastzeile teilte abschneidend

Bei r=8 waren **60 von 60** Abtastzeilen unsymmetrisch — links 252,
rechts 253 Einheiten vom Rand. Im Bild bis zu 48 von 255
Deckungsstufen Unterschied zwischen linker und rechter Kante. Mit
`rdiv`: **0**.

Danach: senkrechte Spiegelsymmetrie **exakt 0**, waagerecht hoechstens
**16 von 255** — eine von siebzehn Stufen, das Minimum, das nicht null
ist. Sie ruehrt daher, dass eine Kante exakt auf einem Abtastpunkt
landen kann (bei Kreisen 3.12 % der Kanten); das ist die uebliche,
flaechentreue halboffene Regel. Sie durch Runden zu ersetzen wurde
gemessen und ist schlechter (4694 statt 296 unsymmetrische Faelle von
5000).

### 3.3 `strichen` loeschte die Kanten des vorigen Strichs

`strichen()` rief `pfad_neu()`, und das setzt auch `k_n` auf null —
also die Kanten. Wer zwei Striche in einen Pfad legt (das Kreuz des
Schliessknopfes), verlor den ersten. **Auf dem 2560er Beleg lag
dadurch ein waagerechter Balken quer ueber den Knopf.** Bei 1280 fiel
es nicht auf, weil das Zeichen halb so gross ist.

Die Pruefung, die gefehlt hat, steht jetzt drin (Abschnitt 8): ein
Strich gegen Laenge mal Breite, das Kreuz gegen die Summe beider
Diagonalen, und die Gegenprobe *"ein Strich ist halb so viel wie
zwei"* — waere der erste verlorengegangen, waeren beide gleich.

---

## 4. Was umgestellt ist

### 4.1 Fensterknoepfe (`kernel/wm.fi`, `cap_glyph`)

Vorher: gesetzte Bildpunkte auf **festen** 10x10 — `cap_px` in einer
Schleife fuer das Kreuz, `cap_box` aus vier Linien fuer das Quadrat.
`cap_w` waechst mit `uisc` (wm.fi:3043), die Zehn wuchs nicht mit: bei
Vervielfachung 2 war die Schaltflaeche 60 Bildpunkte breit und das
Zeichen darin 10.

Nachher: Striche aus `vektor.strichen` mit runden Enden, Groesse
`10*uisc`, Strichbreite `1.5*uisc`.

**Gemessen am Kreuz des Schliessknopfes, 1280x800:**

| | Punkte | Grauwerte |
|---|---:|---:|
| vorher | 66 | **1** (nur 26) |
| nachher | 116 | **4** (26, 40, 82, 83) |

Ein Grauwert heisst: jeder Punkt ganz an oder ganz aus — eine Treppe.
Vier heisst: die vollen Punkte haben Nachbarn mit Teildeckung, und das
ist die Kantenglaettung.

**Bei 2560x1440 mit uiscale 2:** 162 gegen **537** Punkte. Das Zeichen
ist jetzt wirklich doppelt so gross statt gleich klein zu bleiben.

### 4.2 Regler (`kernel/user/wlib.fi`, `paint_slider`)

Vorher: Rinne, Fuellung und Griff waren drei `wlibc.rect` — der Griff
also ein **Rechteck**. Dazu standen `rh=4`, `gw=10`, `gh=14` nackt im
Quelltext, waehrend `wlibc.metric` jede Laenge mit `ui_scale()`
malnimmt (wlibc.fi:3969): bei Vervielfachung 2 wurde das Widget
doppelt so hoch, die Rinne blieb vier Bildpunkte duenn.

Nachher: Rinne als Stadion (Radius = halbe Hoehe), Griff als **Kreis**
mit Rand, alle drei Masse mal `ui_scale`.

### 4.3 Was bewusst NICHT umgestellt wurde

**`wlibc.rrect`** — die runde Ecke eines Rechtecks bleibt bei
`corner_cov`. Nachgemessen ist die Umstellung eine *Verschlechterung*:

| Form | soll | corner_cov | Bezier |
|---|---:|---:|---:|
| 40x24 r=6 | 929.10 | **-0.39** | -0.60 |
| 64x64 r=16 | 3876.25 | **+1.31** | -2.76 |
| 48x48 r=24 | 1809.56 | **+0.69** | -7.13 |
| 100x30 r=8 | 2945.06 | **+0.60** | -0.84 |

Ein Viertelkreis als kubische Bezier liegt bauartbedingt **innen**;
`corner_cov` tastet den echten Kreis ab. Der Bezierfehler ist rund
0.4 % und waechst mit dem Quadrat des Radius — unsichtbar, aber es
waere Arbeit mit schlechterem Ergebnis.

Die Regel daraus: `vrect`/`vkreis` sind fuer das da, was `corner_cov`
**nicht** kann — Kreise, Ringe, Striche, Diagonalen, beliebige
Umrisse. Beide rechnen mit 4x4 und `(cov*255+8)/16`, also passen die
Kanten zusammen, wo sie aneinanderstossen.

---

## 5. Bildzeit

### 5.1 Erst musste sie ueberhaupt messbar werden

Es gab **keine** Zahl fuer "wie lange dauert ein Bild" — nur die Zahl
der Bilder und der Bildpunkte. `compose()` nimmt jetzt die Zeit mit
`time.tsc()`; die serielle Ausgabe druckt Mittelwert, Groesstwert,
Zaehler ueber 16 ms, ein Histogramm aus acht Faechern und —
getrennt — Groesstwert und Zaehler **ab dem elften Bild** (die ersten
zehn sind Start und Moduswechsel, nicht Betrieb).

### 5.2 Ein Befund zur Messmethode: TCG luegt

`tools/design/aufnahme.sh` faehrt mit TCG (aus gutem Grund, siehe
dort). Unter reiner Emulation misst `rdtsc` aber die Zeit des
**Wirts** beim Nachahmen, nicht die des Gastes. Derselbe Kern,
dasselbe Bild, nur der Beschleuniger getauscht:

| | Mittel | Max | ueber 16 ms |
|---|---:|---:|---:|
| TCG | 18620 us | 479067 us | 20 |
| KVM | 1191 us | 7736 us | 0 |

Faktor **15.6**. Fuer die Bilder ist TCG richtig, fuer die Zeit nicht.
**Alle Zeitzahlen hier sind KVM-Zahlen.**

### 5.3 Die Messreihe

Einzellaeufe streuen stark, deshalb fuenf je Kern.

**2560x1440, uiscale 2, KVM:**

| Lauf | Grundlinie Mittel | Betrieb Max | u16 | Vektor Mittel | Betrieb Max | u16 |
|---:|---:|---:|---:|---:|---:|---:|
| 1 | 4976 | 14495 | 0 | 4693 | 34115 | 3 |
| 2 | 5033 | 27034 | 1 | 5343 | 17145 | 1 |
| 3 | 4932 | 19118 | 1 | 4997 | 16925 | 1 |
| 4 | 5426 | 32076 | 2 | 4589 | 15542 | 0 |
| 5 | 5584 | 32338 | 2 | 4954 | 19443 | 1 |

| | Grundlinie | Vektor |
|---|---:|---:|
| Mittel, Median | 5033 us | **4954 us** |
| Betrieb Max, Median | 27034 us | **17145 us** |
| ueber 16 ms im Betrieb, Median | 1 | **1** |

**1280x800, uiscale 1, KVM (Einzellauf):** Mittel 1191 -> 1398 us,
Max 7736 -> 7679 us, ueber 16 ms **0 -> 0**.

### 5.4 Die ehrliche Aussage

Der Unterschied der Mediane ist **79 us**. Die Streuung eines
einzelnen Kerns zwischen bestem und schlechtestem Lauf ist **652 us**.
Der Unterschied ist kleiner als das Rauschen, also:

> **Keine messbare Aenderung der Bildzeit.** Nicht "schneller", nicht
> "langsamer" — nicht unterscheidbar.

Dass es so ist, liegt am Zwischenspeicher. Ohne ihn (Einzellauf) stieg
das Mittel auf 5211 us und der Betriebs-Groesstwert auf 20936 us, und
26 Bilder rutschten aus dem Fach 4..8 ms in das Fach 8..16 ms. Mit
ihm wird jedes Zeichen **einmal je Vervielfachung** gerastert.

### 5.5 Zur harten Grenze von 16 ms

Sie ist **nicht durchgaengig eingehalten** — weder vorher noch
nachher. In beiden Faellen liegt im Median **ein** Bild darueber, und
es ist dasselbe Bild: der Moduswechsel, der den ganzen Schirm malt.
3.7 Millionen Bildpunkte bei rund 219 Mio/s sind 16.8 ms. Das ist
Speicherbandbreite, nicht Zeichenarbeit; die Grundlinie lag bei
18.7 ms.

**Der Umbau hat kein Bild ueber die Grenze geschoben.** Die Grenze
selbst wird vom Vollbild-Neuaufbau gerissen, und das ist eine eigene
Aufgabe (Teilaktualisierung statt Vollbild) — sie steht in
`/root/osum-roadmap/OFFEN.md`.

---

## 6. Bilder

Alle mit **echter Skalierung**: die 2560er Laeufe stehen auf
`uiscale=2`. Nachgemessen an der Leistenhoehe: 1280 -> 8 Bildpunkte,
2560 -> 16. Genau doppelt, also wirklich skaliert.

```
vorher/1280/    vorher/2560/          die Grundlinie (44a5af3 + Messung)
nachher/hell-1280/    nachher/dunkel-1280/
nachher/hell-2560/    nachher/dunkel-2560/
ausschnitte/          vergroesserte Knopfzeichen, vorher gegen nachher
```

**Zur Belegbild-Regel.** Beim Pruefen der Namen fiel auf, dass das
Drehbuch von `aufnahme.sh` den Dateimanager und die Einstellungen
**nicht oeffnet** (`warteauf explorer: ready -> NICHT DA`). Die Bilder
hiessen trotzdem `03-explorer.png` und `06-einstellungen.png`, und
`03-explorer.png` war byteweise identisch mit `04-dialog.png`.

Das gilt hier als Fehler, also sind die Namen korrigiert: die Bilder
heissen jetzt nach dem, was **darauf zu sehen** ist
(`03-startmenue-nach-klick.png` statt `03-explorer.png`), und
byteweise doppelte Bilder sind entfernt. Gegenprobe: **0 doppelte
Pruefsummen** im ganzen Belegordner.

Was das offenlaesst: der Dateimanager und die Einstellungen sind in
dieser Runde **nicht im Bild**, weil sie im Testlauf nicht starten.
Das ist ein Mangel des Drehbuchs, nicht des Umbaus — steht unter
"offen".

---

## 7. Was offen blieb

1. **Der Mauszeiger ist noch nicht umgestellt.** `kernel/zeiger.fi`
   hat weiter sein eigenes Verfahren (Punkt-in-Vieleck, 3x3). Es sind
   damit noch **zwei** Rasterverfahren statt einem — `vektor.fi` fuer
   Knoepfe und Regler, `zeiger.fi` fuer den Zeiger. Der Umbau ist
   vorbereitet (`vieleck_rund` kann genau das), aber nicht gemacht und
   nicht gemessen.

2. **Runde Fensterecken waren schon da.** Der Auftrag nennt
   `FM_RADIUS` als offene Kernluecke. Nachgemessen am Beleg ist das
   **nicht mehr richtig**: die Ecke oben links des Fensters bei 1280
   zeigt einen sauberen Bogen ueber 12 Bildpunkte mit Kantenglaettung.
   `fill_round`/`corner_cov` zeichnet ihn, vermutlich seit
   ECHTHARDWARE-3/4. Es gab also nichts umzustellen — und nach 4.3
   waere Umstellen auch schlechter.

3. **Kontrollzentrum-Kacheln, Textfeldrahmen, Trennlinien** laufen
   weiter ueber `rrect`/`rect`. Nach der Messung in 4.3 ist das fuer
   Rechtecke mit runden Ecken die bessere Wahl; was fehlt, sind die
   Stellen mit **echten Kurven** (Umschalter, runde Anzeigen).

4. **Der Helligkeitsregler im Kontrollzentrum** (`kernel/user/qs.fi`)
   malt seinen Griff mit `mal_tafel`, nicht mit `paint_slider`. Er ist
   damit weiter ein abgerundetes Rechteck, kein Kreis. Umgestellt ist
   der Regler aus `wlib` (`paint_slider`), den u. a. der Betrachter
   benutzt.

5. **Die 16-ms-Grenze beim Vollbild** (siehe 5.5) — Teilaktualisierung
   statt Vollbild-Neuaufbau.

6. **Zwei Belegbilder fehlen**, weil das Drehbuch Dateimanager und
   Einstellungen nicht oeffnet (siehe 6).

---

## 8. Commits auf `vektor`

```
79aa697  Teil 5: strichen loeschte die Kanten des vorigen Strichs
0c055d2  Teil 4: die Zeichen liegen fertig, und die Messung bekommt ein Histogramm
bd3faab  Teil 3: Fensterknoepfe und Regler kommen auf den Unterbau
9d84729  Teil 2: die Bildzeit wird gemessen, nicht behauptet
ccdad5e  Teil 1: ein Rasterer statt drei -- und zwei Fehler, die der Selbsttest gefunden hat
```

Nicht nach GitHub gepusht. Nicht nach `merge9` zurueckgeschrieben.
Das Abbild liegt unter `/tmp/vekt/v4.mb`; der Zeiger auf "aktuell"
ist **nicht** angefasst.
