fix: Abhängigkeits-Kanten laufen hinter den Knoten (D41-Nachtrag 2)

Die Basis-Kanten lagen schon richtig; „drüber gemalt" wurde allein die
Hervorhebung des ausgewählten Knotens (`svg.dep-front`, z-index 4). Eine
durchgezogene Linie quer über einen fremden Knoten durchstreicht dessen
Titel — und beim Tippen wechselt die Hervorhebung mit jeder Zeile.

`dep-front` entfällt; hervorgehobene Kanten hängen zuletzt in derselben
hinteren Ebene, liegen damit über den übrigen Kanten und unter jedem
Knoten. Export und Druck waren nie betroffen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-24 15:19:35 +02:00
co-authored by Claude Opus 5
parent 6881475193
commit 6d58164577
6 changed files with 78 additions and 13 deletions
+1
View File
@@ -19,6 +19,7 @@ reverse.
## 2026-08-24 ## 2026-08-24
- Dependency links now run behind every node, highlighted ones included — no more lines struck through node titles
- Fix: the line to an only child was torn where the child does not sit centred in its cell - Fix: the line to an only child was torn where the child does not sit centred in its cell
- An optional node (`+`) joins the cheapest path while it is being worked on — started work is the open front - An optional node (`+`) joins the cheapest path while it is being worked on — started work is the open front
- Fix: a started alternative lost its `|`/`=` group to an untouched cheaper one, although the choice was already made - Fix: a started alternative lost its `|`/`=` group to an untouched cheaper one, although the choice was already made
+55
View File
@@ -2805,6 +2805,61 @@ Zweiter Schritt derselben Rückmeldung: Auch die **Pfeilspitze** ist jetzt
Dreiecks — das Dreieck stach als einziger satter Fleck aus der gepunkteten Dreiecks — das Dreieck stach als einziger satter Fleck aus der gepunkteten
Linie heraus. Die Hervorhebung bekommt denselben Winkel in Tinte. Linie heraus. Die Hervorhebung bekommt denselben Winkel in Tinte.
**Nachtrag 2 — auch die hervorgehobenen Kanten laufen hinter den Knoten
durch.** Gemeldet: „die Abhängigkeits-Pfeile sollen im Hintergrund durch andere
Knoten durchlaufen, derzeit werden die Pfeile drüber gemalt."
**Nachgemessen war es genau eine Hälfte.** Die **Basis**-Kanten lagen schon
richtig: In einem eigens gebauten Fall — eine Kante quer über drei breite
Knoten hinweg, zur Sichtbarkeit magenta und 6 px dick gefärbt — erschien sie
**nur in den Lücken** zwischen den Knoten, nie über deren Fläche. Sichtbar
„drüber" war allein die **Hervorhebung**: Steht der Cursor auf einer Zeile mit
`:#…`, wandern deren Kanten auf `svg.dep-front` (`z-index:4`) und laufen als
durchgezogene Tinte-Linie mitten durch die Beschriftung der Knoten dazwischen.
**Damit fällt die Begründung des Haupttextes** — „vorn dürfen sie den Baum
überlagern, denn die Hervorhebung ist flüchtig und gerade angefragt". Der
Einwand des Nutzers wiegt schwerer, und er ist derselbe, aus dem die
Basis-Kanten von Anfang an hinten liegen: Eine Linie quer über einen fremden
Knoten **durchstreicht dessen Titel**. Das trifft ausgerechnet die Knoten, die
mit der Sache nichts zu tun haben — die Kante sagt etwas über ihre beiden
Enden, nicht über das, was zufällig dazwischen steht. „Flüchtig" ist dabei kein
Freibrief, sondern eher das Gegenteil: Beim Tippen wechselt die Hervorhebung
mit jeder Zeile, der Schaden ist also nicht selten, sondern dauernd.
**Eine Ebene statt zweier.** `svg.dep-front` entfällt; hervorgehobene Kanten
werden **zuletzt** in dieselbe hintere Ebene gehängt und liegen dadurch über
den übrigen Kanten, aber unter jedem Knoten. Die Unterscheidung trägt weiterhin
das Aussehen (durchgezogen in Tinte gegen gepunktet in Blassgrau), und das ist
genug: Sie war nie an der Ebene festgemacht, sondern nur zusätzlich betont.
**Der Preis, benannt:** In einer dichten Reihe breiter Knoten bleibt von der
hervorgehobenen Kante wenig mehr als ein Stück in jeder Lücke. Zu **finden**
ist ihr Ziel trotzdem, denn die **Pfeilspitze sitzt auf der Knotenkante** und
liegt damit außerhalb jedes Kastens. Für den Fall, dass das eines Tages nicht
reicht, steht das Hausmittel bereit: eine **abgetönte Kopie davor**, wie sie
der günstigste Pfad seit D18 für genau dieses Problem hat („deutet den Verlauf
beim Durchschreiten eines Knotens nur schwach an"). Bewusst nicht vorab
gebaut — gefragt war, dass die Kanten hinten laufen, und eine zweite Ebene
zurückzuholen, um die erste zu erklären, ist der falsche Anfang.
**Was dadurch obsolet wird:** D25-Nachtrag 3 gab `.dep-front` seinen
`z-index:4`, weil die hervorgehobenen Kanten sonst unter dem hervorgehobenen
Knoten endeten („vorn heißt vorn"). Das war für eine vordere Ebene richtig und
ist mit ihr weggefallen; für `.cheap-front` (`z-index:5`) gilt es unverändert
weiter — der Stationspunkt gehört über den Knoten.
**Export und Druck waren nie betroffen:** `diagramToSvg()` zeichnet die
Basis-Kanten in Schritt 1a, also vor den Knoten, und die Hervorhebung gar nicht
(Interaktion, siehe oben). Das Bild auf dem Schirm zieht damit nach, statt dass
etwas Neues entsteht — derselbe Befund wie bei D46-Nachtrag.
**Nachgemessen** nach der Änderung, an demselben Fall: `svg.dep-front` gibt es
nicht mehr, alle Kanten hängen in `svg.dep-back` als erstem Kind von `#out`,
die hervorgehobene als letztes Element darin. Die Tinte-Linie ist in den Lücken
zu sehen und über den Knoten nicht mehr; die Pfeilspitze steht unverändert an
der Kante des gebrauchten Knotens.
## D42 — Closure-Pfad: erschöpfend über die gekoppelten Gruppen, gierig nur benannt ## D42 — Closure-Pfad: erschöpfend über die gekoppelten Gruppen, gierig nur benannt
Die letzte Baustelle aus D34: Mit Abhängigkeiten zählt der günstigste Pfad Die letzte Baustelle aus D34: Mit Abhängigkeiten zählt der günstigste Pfad
nicht mehr den gewählten Teilbaum, sondern die **Hülle** — jeder nötige Knoten nicht mehr den gewählten Teilbaum, sondern die **Hülle** — jeder nötige Knoten
+6 -2
View File
@@ -961,9 +961,13 @@ eigenen Zeichenebene **hinter** den Knoten, mit einer kleinen **offenen
Pfeilspitze auf das Gebrauchte** („braucht“-Richtung; ein Winkel aus zwei Pfeilspitze auf das Gebrauchte** („braucht“-Richtung; ein Winkel aus zwei
Strichen, kein gefülltes Dreieck). Der Baum trägt weiterhin die Hauptaussage. Strichen, kein gefülltes Dreieck). Der Baum trägt weiterhin die Hauptaussage.
- **Alle** Kanten laufen **hinter** den Knoten durch, auch die hervorgehobenen:
sichtbar in den Lücken, verdeckt vom Knoten selbst. Der Baum bleibt damit
lesbar — eine Kante, die quer über fremde Knoten hinwegläuft, durchstreicht
deren Beschriftung.
- Die Kanten des **ausgewählten** Knotens — Tastaturfokus im Diagramm, sonst - Die Kanten des **ausgewählten** Knotens — Tastaturfokus im Diagramm, sonst
der Knoten der Cursor-Zeile — liegen hervorgehoben in Tinte auf einer der Knoten der Cursor-Zeile — sind in Tinte hervorgehoben, ein- wie
vorderen Ebene, ein- wie ausgehende. ausgehende; sie liegen über den übrigen Kanten, aber unter jedem Knoten.
- Kanten zu gerade nicht sichtbaren Knoten (eingeklappt §9, verworfen - Kanten zu gerade nicht sichtbaren Knoten (eingeklappt §9, verworfen
ausgeblendet §4) entfallen; bei doppelter ID zielt die Kante auf die erste ausgeblendet §4) entfallen; bei doppelter ID zielt die Kante auf die erste
Vergabe (D36/D39). Vergabe (D36/D39).
+10 -5
View File
@@ -426,18 +426,23 @@ function drawDepLinks(){
cx:(r.left - outRect.left + r.width/2)/z, cx:(r.left - outRect.left + r.width/2)/z,
cy:(r.top - outRect.top + r.height/2)/z}; }; cy:(r.top - outRect.top + r.height/2)/z}; };
const w = outRect.width/z, h = outRect.height/z; const w = outRect.width/z, h = outRect.height/z;
/* EINE Ebene, und die liegt hinter den Knoten (D41-Nachtrag 2): Die Kanten
laufen durch fremde Knoten hindurch, statt über sie hinweg. Die
hervorgehobenen werden zuletzt eingehängt — damit liegen sie über den
übrigen Kanten, aber weiterhin unter jedem Knoten. */
const back = overlaySvg('dep-overlay dep-back', w, h); const back = overlaySvg('dep-overlay dep-back', w, h);
const front = overlaySvg('dep-overlay dep-front', w, h);
const hi = activeDepNode(); const hi = activeDepNode();
const hoch = [];
for(const [from, to] of edges){ for(const [from, to] of edges){
const hl = hi && (from === hi || to === hi); const hl = hi && (from === hi || to === hi);
const c = depCurve(rect(from), rect(to)); const c = depCurve(rect(from), rect(to));
const layer = hl ? front : back; const kante = svgEl('path', {class:'dep-edge' + (hl ? ' hl' : ''), d:c.d});
layer.appendChild(svgEl('path', {class:'dep-edge' + (hl ? ' hl' : ''), d:c.d})); const spitze = svgEl('path', {class:'dep-arrow' + (hl ? ' hl' : ''), d:depArrow(c.end, c.ctrl)});
layer.appendChild(svgEl('path', {class:'dep-arrow' + (hl ? ' hl' : ''), d:depArrow(c.end, c.ctrl)})); if(hl) hoch.push(kante, spitze);
else { back.appendChild(kante); back.appendChild(spitze); }
} }
for(const p of hoch) back.appendChild(p);
out.insertBefore(back, out.firstChild); out.insertBefore(back, out.firstChild);
if(front.childNodes.length) out.appendChild(front);
} }
/* Hervorhebung folgt dem Fokus; die Basis-Kanten selbst ändern sich nicht. */ /* Hervorhebung folgt dem Fokus; die Basis-Kanten selbst ändern sich nicht. */
out.addEventListener('focusin', drawDepLinks); out.addEventListener('focusin', drawDepLinks);
+5 -5
View File
@@ -918,12 +918,12 @@
Blättern zeigen, welche Knoten wirklich auf dem Pfad enden — durchquerte Blättern zeigen, welche Knoten wirklich auf dem Pfad enden — durchquerte
Fremdknoten tragen keinen Punkt. Reihenfolge im DOM steuert vorn/hinten. */ Fremdknoten tragen keinen Punkt. Reihenfolge im DOM steuert vorn/hinten. */
.cheap-overlay{position:absolute;left:0;top:0;overflow:visible;pointer-events:none} .cheap-overlay{position:absolute;left:0;top:0;overflow:visible;pointer-events:none}
/* Die VORDEREN Ebenen müssen über JEDEN Knoten — auch über die, die sich /* Die VORDERE Ebene muss über JEDEN Knoten — auch über die, die sich selbst
selbst eine Stapelposition nehmen: Cursor-Zeile (`z-index:3`, D25), eine Stapelposition nehmen: Cursor-Zeile (`z-index:3`, D25),
Strahlenkränze (`z-index:2`, D28/D32). Ohne das deckt ausgerechnet der Strahlenkränze (`z-index:2`, D28/D32). Ohne das deckt ausgerechnet der
hervorgehobene Knoten seinen eigenen Stationspunkt zu, und die hervorgehobene Knoten seinen eigenen Stationspunkt zu. „Vorn" heißt vorn.
hervorgehobenen Abhängigkeits-Kanten enden unter ihm. „Vorn" heißt vorn. */ (Die Abhängigkeits-Kanten hatten aus demselben Grund einmal `.dep-front`
.dep-overlay.dep-front{z-index:4} mit `z-index:4`; sie liegen jetzt vollständig hinten — D41-Nachtrag 2.) */
.cheap-overlay.cheap-front{z-index:5} .cheap-overlay.cheap-front{z-index:5}
/* Querverbindungen der Abhängigkeiten (SPEC §9, D41): blassgrau GEPUNKTET /* Querverbindungen der Abhängigkeiten (SPEC §9, D41): blassgrau GEPUNKTET
und gekrümmt — Punktierung und Krümmung unterscheiden sie doppelt von den und gekrümmt — Punktierung und Krümmung unterscheiden sie doppelt von den
@@ -1 +1 @@
{"version":"4.1.11","results":[[":frontend/tests/render.test.js",{"duration":98.47688799999997,"failed":false}],[":frontend/tests/focusmark.test.js",{"duration":35.79027499999995,"failed":false}],[":frontend/tests/fold.test.js",{"duration":41.154863999999975,"failed":false}],[":frontend/tests/optional.test.js",{"duration":35.42018200000001,"failed":false}],[":frontend/tests/parser.test.js",{"duration":33.422897999999975,"failed":false}],[":frontend/tests/xor.test.js",{"duration":52.44134100000002,"failed":false}],[":frontend/tests/desc.test.js",{"duration":31.248038000000008,"failed":false}],[":frontend/tests/deps.test.js",{"duration":31.736107000000004,"failed":false}],[":frontend/tests/closure.test.js",{"duration":24.077101999999968,"failed":false}],[":frontend/tests/effective.test.js",{"duration":27.93907200000001,"failed":false}],[":frontend/tests/ids.test.js",{"duration":25.51923499999998,"failed":false}],[":frontend/tests/remote.test.js",{"duration":20.999619999999993,"failed":false}],[":frontend/tests/warnings.test.js",{"duration":9.594386999999983,"failed":false}],[":frontend/tests/fresh.test.js",{"duration":22.15039999999999,"failed":false}]]} {"version":"4.1.11","results":[[":frontend/tests/render.test.js",{"duration":76.60164399999996,"failed":false}],[":frontend/tests/focusmark.test.js",{"duration":40.93189599999994,"failed":false}],[":frontend/tests/fold.test.js",{"duration":56.39246000000003,"failed":false}],[":frontend/tests/optional.test.js",{"duration":34.46531600000003,"failed":false}],[":frontend/tests/parser.test.js",{"duration":25.05400099999997,"failed":false}],[":frontend/tests/xor.test.js",{"duration":65.50341500000002,"failed":false}],[":frontend/tests/desc.test.js",{"duration":59.75648000000001,"failed":false}],[":frontend/tests/deps.test.js",{"duration":67.85368099999994,"failed":false}],[":frontend/tests/closure.test.js",{"duration":29.96200700000003,"failed":false}],[":frontend/tests/effective.test.js",{"duration":55.79072299999996,"failed":false}],[":frontend/tests/ids.test.js",{"duration":48.80462399999999,"failed":false}],[":frontend/tests/remote.test.js",{"duration":17.515127999999947,"failed":false}],[":frontend/tests/warnings.test.js",{"duration":9.082986000000005,"failed":false}],[":frontend/tests/fresh.test.js",{"duration":21.491277000000025,"failed":false}],[":frontend/tests/shortids.test.js",{"duration":40.74205600000005,"failed":false}],[":frontend/tests/snapshots.test.js",{"duration":105.82867499999998,"failed":false}],[":frontend/tests/frontier.test.js",{"duration":80.81959199999994,"failed":false}],[":frontend/tests/continuation.test.js",{"duration":36.78602599999999,"failed":false}],[":frontend/tests/news.test.js",{"duration":21.62978800000002,"failed":false}]]}