test: U-Form-Faelle, Formkriterium laengs und Reihenanordnung der Linienzug-Tests

Drei neue Modus-2-Testfaelle aus echten Laeufen, ein Kriterium, das eine
heute eingebaute Fehlgeometrie gefunden haette, und ein aufgeraeumtes
Layout der Testketten.

1. Drei U-Form-Faelle (tests/testdata/linienzug_modus2_uform_*.json),
   aufgezeichnet mit lib/vf_journal_export.py. Gefaltete Laeufe: der Pfad
   wechselt ueber zwei 90-Grad-Vario-Kurven dreimal die Fahrtrichtung, die
   Kletterstrecke liegt in einem ANDEREN Schenkel als der Lauf-Eingang -
   genau der am 2026-10-29 korrigierte Fall. Variiert werden GF-Verteilung
   (alles am Einlauf vs. 1/2+1/2) und Winkel; beides ist noetig, weil die
   Verteilung entscheidet, ob ueberhaupt GF im fremden Schenkel steht, und
   der Restfehler der alten Bilanz mit L_GF skalierte.
   c:TEST_LINIENZUG_MODUS2 ist dafuer tabellengesteuert - ein weiterer Fall
   ist jetzt eine Zeile in *tlz-m2-faelle*.

2. RUECKNAHME des Schluss-Solves (vfl-zsolve-schluss-ziel, heute frueh
   eingebaut). Er loeste den letzten Fueller gegen Richtung UND Laenge und
   traf den Zielpunkt dadurch in X und Y exakt - um den Preis, dass er die
   letzte Gerade um 6.15 Grad verdrehte (hz-plan -90 -> -96.15), in der
   Zeichnung als schiefer Schenkel sichtbar. Das widerspricht der
   Entscheidung vom 2026-10-26: quer MUSS 0 sein, LAENGS DARF ABWEICHEN -
   der Hauptstrang bleibt gerade, der seitliche Versatz des ES-Elements ist
   dessen Bauform. Die Messwerte von heute (0.56 / 0.7 mm quer) stammen
   nicht von dieser Aenderung, sondern von der Laengenbilanz des gefalteten
   Laufs (35ebee1) - der Zweig blieb dort ungenutzt. Die Begruendung steht
   jetzt an der Aufrufstelle, damit niemand den Solve erneut baut.

3. laengs wird geprueft - als FORM-, nicht als Genauigkeitskriterium
   (erwartung_laengs_mm, Toleranz 5 mm). Der Wert ist die Ausladung des
   ES-Elements und je Fall eine Konstante (104.68 bei ES_Element_30,
   637.58 bei ES_Element_90). Punkt 2 war am Querwert NICHT erkennbar - die
   Verdrehung macht ihn gerade null, der Test meldete OK, gefunden wurde sie
   mit dem Auge. Ein verdrehter Hauptstrang zielt auf den Endpunkt und
   drueckt laengs auf ~0; daran faellt er auf. Gegengeprueft: mit kuenstlich
   auf 0.01 mm gesetzter Toleranz kippen alle fuenf Faelle auf ABWEICHUNG.

4. Alle sieben Ketten des Menueeintrags entstehen als Reihe auf Y=0, 20 m
   Abstand, statt verstreut. Die Modus-1-Skripte brauchen dafuer nur ihren
   EINEN point_abs (alles Weitere ist point_rel mit dL/hz); bei Modus 2
   mussten PT-Eintraege UND die Pfadkoordinaten in SEGS (Felder 6..9)
   mitwandern, sonst zeigt der Pfad woanders hin als Start und Ziel.
   Reine Translation - Laengen, Winkel und die geprueften Abweichungen sind
   davon unabhaengig; Z blieb stehen, weil die Starthoehe ein Eingabewert
   des Solvers ist. Geprueft: erstes Segment beginnt exakt am Startpunkt,
   letztes endet exakt am Zielpunkt, Messwerte unveraendert.

5. Die drei letzten Verweise auf doc/VarioFoerderer_Linienzug_Prinzipien.md
   (bewusst unversioniert) tragen ihre Begruendung jetzt selbst: Entkopplung
   Neigung/XY, warum es genau EINEN Kletterer je Lauf gibt, und warum ein
   VF-Sub-Segment auf der 3-Grad-Basis beginnen und enden muss.

Geprueft: TEST_UNIT 897/897 (die 7 Tests der entfernten Funktion sind mit
ihr gegangen), TEST_LINIENZUG_MODUS2 5 von 5 OK, voller Menueablauf baut
sieben Ketten an den vorgesehenen Stellen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-29 14:28:57 +01:00
parent 11e334d0a2
commit b709f89404
12 changed files with 585 additions and 185 deletions
+103 -25
View File
@@ -270,7 +270,7 @@
;; ============================================================
;; MODUS 2 (aus 3D-Linie mit Zielhoehe) - Replay zweier echter Laeufe
;; MODUS 2 (aus 3D-Linie mit Zielhoehe) - Replay echter Laeufe
;; ============================================================
;; Anders als die Modus-1-Faelle oben braucht Modus 2 KEINE Mocks und KEINEN
;; gezeichneten Pfad: sein Journal traegt einen SEGS-Eintrag mit der kompletten
@@ -279,19 +279,51 @@
;; schon vfl-konvertiere-ent beruht (vfl-journal-hat-segs-p). Der Test spielt
;; daher das echte, aus der Zeichnung ausgelesene Journal ab.
;;
;; Die beiden Faelle sind BEWUSST ein Paar: derselbe Pfad, nur die 90-Grad-Ecke
;; einmal als Vario-Kurve (waagerecht) und einmal als GF-Bogen (geneigt). Sie
;; laufen durch verschiedene Zweige - mit und ohne flache Zone - und genau der
;; VERGLEICH hat am 2026-10-26/27 Fehler aufgedeckt, die ein einzelner Lauf
;; nicht gezeigt haette (ES-Fussabdruck am falschen Segment: 878 mm nur in der
;; GF-Bogen-Variante). Einen davon zu loeschen nimmt dem Test seinen Wert.
;; Die Faelle sind zwei BEWUSST gebildete Paare, kein Sammelsurium:
;;
;; Aufgezeichnet mit lib/vf_journal_export.py aus einem echten BricsCAD-Lauf.
;; A/B (Ecke als Vario-Kurve bzw. als GF-Bogen) - derselbe Pfad, nur die
;; 90-Grad-Ecke einmal waagerecht und einmal geneigt eingebaut. Sie laufen
;; durch verschiedene Zweige (mit und ohne flache Zone), und genau der
;; VERGLEICH hat am 2026-10-26/27 Fehler aufgedeckt, die ein einzelner
;; Lauf nicht gezeigt haette (ES-Fussabdruck am falschen Segment: 878 mm
;; nur in der GF-Bogen-Variante).
;;
;; U-Form 1..3 (gefalteter Lauf) - der Pfad wechselt ueber zwei 90-Grad-
;; Vario-Kurven dreimal die Fahrtrichtung, die Kletterstrecke liegt
;; dadurch in einem ANDEREN Schenkel als der Lauf-Eingang. Hier variieren
;; GF-VERTEILUNG (alles am Einlauf vs. 1/2+1/2) und WINKEL. Beides ist
;; noetig: die Verteilung entscheidet, ob ueberhaupt GF im fremden
;; Schenkel steht (Nachrechner, Fueller-A), und der Restfehler der alten
;; Bilanz skalierte mit L_GF, also mit dem Winkel (6 Grad 90 mm, 9 Grad
;; 934 mm) - zwei Laeufe mit demselben Winkel haetten das verdeckt.
;;
;; Einen Fall zu loeschen nimmt dem Test den jeweiligen Vergleich und damit
;; seinen Wert.
;;
;; Aufgezeichnet mit lib/vf_journal_export.py aus echten BricsCAD-Laeufen
;; (A/B am 2026-10-27, U-Form am 2026-10-29).
;; Pruefkriterium (Nutzer-Vorgabe 2026-10-26): quer zur ES-KS_AUS-Achse und dZ
;; MUESSEN ~0 sein, laengs DARF abweichen (Ausladung des ES-Elements).
(setq *tlz-m2-toleranz* 1.0) ; mm; 0.2 ist die Messaufloesung der Blockmasse
;; Toleranz fuer den LAENGS-Wert gegen "erwartung_laengs_mm" aus den Testdaten.
;; Der Wert ist die Ausladung des ES-Elements und damit je Fall eine Konstante
;; (gemessen: 104.68 mm bei ES_Element_30, 637.58 mm bei ES_Element_90) - 5 mm
;; reichen also fuer Rundung, ohne echte Abweichungen durchzulassen.
;;
;; Warum ueberhaupt geprueft, wo laengs doch ausdruecklich abweichen DARF:
;; Am 2026-10-29 loeste ein Experiment den Schluss-Fueller gegen Richtung UND
;; Laenge und traf den Zielpunkt dadurch in X und Y exakt - um den Preis, dass
;; es die letzte Gerade um 6.15 Grad verdrehte (sichtbar schiefer Schenkel im
;; U-Form-Fall). quer und dZ waren dabei NULL, der Test meldete OK; gefunden
;; wurde der Fehler nur mit dem Auge in der Zeichnung.
;; laengs ist der Wert, der das verraet: ein verdrehter Hauptstrang zielt auf
;; den Endpunkt und drueckt laengs von ~638 mm auf ~0. Die Pruefung ist also
;; kein zweites Genauigkeitskriterium, sondern ein Formkriterium - sie stellt
;; sicher, dass der ES-Versatz noch da ist, wo er hingehoert.
(setq *tlz-m2-laengs-toleranz* 5.0)
;; Wert als String - eigene Kopie statt hmrec:als-string aus
;; test_hm_recformat.lsp: diese Datei soll fuer sich allein ladbar bleiben.
(defun tlz-m2-str (v)
@@ -321,7 +353,8 @@
;; Einen Modus-2-Fall abspielen. Rueckgabe: (status laengs quer dz offen).
(defun tlz-m2-run (json-datei test-id anzeige /
daten journal meta offen err status abw j
old-headless old-gui quer dz laengs)
old-headless old-gui quer dz laengs
soll-laengs laengs-ok)
(if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek))
(princ (strcat "\n\n--- " anzeige " ---"))
;; Vorbelegen, damit der Ergebnis-Record auch im Fehlerzweig Zahlen hat -
@@ -375,38 +408,83 @@
(if (> offen 0)
(princ (strcat "\n WARNUNG: " (itoa offen)
" Journal-Eintraege NICHT verbraucht (Ablauf weicht ab).")))
;; laengs gegen den erwarteten ES-Versatz pruefen, sofern die
;; Testdaten einen nennen (siehe *tlz-m2-laengs-toleranz*).
(setq soll-laengs (if meta (ssg-val meta "erwartung_laengs_mm") nil))
(setq laengs-ok
(if (numberp soll-laengs)
(<= (abs (- laengs (float soll-laengs))) *tlz-m2-laengs-toleranz*)
T))
(if (not laengs-ok)
(princ (strcat "\n WARNUNG: laengs " (rtos laengs 2 1)
" statt erwarteter " (rtos (float soll-laengs) 2 1)
" mm - der Hauptstrang zeigt nicht mehr in die"
" gezeichnete Richtung (ES-Versatz verschwunden?).")))
(setq status
(if (and (<= (abs quer) *tlz-m2-toleranz*)
(<= (abs dz) *tlz-m2-toleranz*)
laengs-ok
(= offen 0))
"OK" "ABWEICHUNG"))
(princ (strcat "\n -> " status
" (Soll: |quer| und |dZ| <= " (rtos *tlz-m2-toleranz* 2 1)
" mm, laengs frei)"))))
" mm, laengs = "
(if (numberp soll-laengs)
(strcat (rtos (float soll-laengs) 2 0) " +/- "
(rtos *tlz-m2-laengs-toleranz* 2 0) " mm")
"frei")
")"))))
(ssg-end)))
(list status laengs quer dz offen))
(defun c:TEST_LINIENZUG_MODUS2 ( / basis a b)
;; Testfall-Tabelle: (Dateiname test-id Anzeigename). Ein weiterer Fall ist
;; damit EINE Zeile - der Ablauf darunter ist generisch. Reihenfolge =
;; Ausgabereihenfolge; die beiden Paare stehen bewusst beieinander.
(setq *tlz-m2-faelle*
(list
(list "linienzug_modus2_variokurve.json" "LZ_M2_variokurve"
"Variante A: Ecke = Vario-Kurve")
(list "linienzug_modus2_gfbogen.json" "LZ_M2_gfbogen"
"Variante B: Ecke = GF-Bogen")
(list "linienzug_modus2_uform_gf1.json" "LZ_M2_uform_gf1"
"U-Form 1: GF alles am Einlauf")
(list "linienzug_modus2_uform_halb.json" "LZ_M2_uform_halb"
"U-Form 2: GF 1/2 + 1/2")
(list "linienzug_modus2_uform_halb_steil.json" "LZ_M2_uform_halb_steil"
"U-Form 3: GF 1/2 + 1/2, steilere Winkel-Option")))
(defun c:TEST_LINIENZUG_MODUS2 ( / basis fall r erg zeilen anzahl-ok)
;; Doppelte Backslashes: "\t" waere in AutoLISP ein TABULATOR, der Pfad
;; wuerde zu "...dxfmakros<TAB>ests<TAB>estdata" (real passiert 2026-10-27).
(setq basis (strcat (getenv "DXFMAKRO") "\\tests\\testdata\\"))
(princ "\n================================================================")
(princ "\n TEST_LINIENZUG_MODUS2 - zwei Varianten DERSELBEN Strecke")
(princ (strcat "\n TEST_LINIENZUG_MODUS2 - " (itoa (length *tlz-m2-faelle*))
" Ketten aus echten BricsCAD-Laeufen"))
(princ "\n================================================================")
(setq a (tlz-m2-run (strcat basis "linienzug_modus2_variokurve.json")
"LZ_M2_variokurve" "Variante A: Ecke = Vario-Kurve"))
(setq b (tlz-m2-run (strcat basis "linienzug_modus2_gfbogen.json")
"LZ_M2_gfbogen" "Variante B: Ecke = GF-Bogen"))
(setq erg '() zeilen '() anzahl-ok 0)
(foreach fall *tlz-m2-faelle*
(setq r (tlz-m2-run (strcat basis (car fall)) (cadr fall) (caddr fall)))
(if (= (car r) "OK") (setq anzahl-ok (1+ anzahl-ok)))
(setq erg (cons (list (caddr fall) (car r)) erg))
(setq zeilen
(cons (strcat " { \"test_id\": \"" (cadr fall)
"\", \"status\": \"" (car r)
"\", \"quer_mm\": " (rtos (caddr r) 2 2)
", \"dz_mm\": " (rtos (cadddr r) 2 2)
", \"laengs_mm\": " (rtos (cadr r) 2 2) " }")
zeilen)))
(setq erg (reverse erg) zeilen (reverse zeilen))
(princ "\n\n================================================================")
(princ (strcat "\n ERGEBNIS: Variante A " (car a) " | Variante B " (car b)))
(princ "\n ERGEBNIS")
(princ "\n----------------------------------------------------------------")
(foreach e erg
(princ (strcat "\n " (car e) ": " (cadr e))))
(princ (strcat "\n----------------------------------------------------------------"
"\n " (itoa anzahl-ok) " von " (itoa (length *tlz-m2-faelle*))
" OK"))
(princ "\n================================================================")
;; Ergebnis fuer run-all (Konvention wie linienzug:export-results)
(setq *linienzug-m2-test-results*
(strcat " { \"test_id\": \"LZ_M2_variokurve\", \"status\": \"" (car a)
"\", \"quer_mm\": " (rtos (caddr a) 2 2)
", \"dz_mm\": " (rtos (cadddr a) 2 2) " },\n"
" { \"test_id\": \"LZ_M2_gfbogen\", \"status\": \"" (car b)
"\", \"quer_mm\": " (rtos (caddr b) 2 2)
", \"dz_mm\": " (rtos (cadddr b) 2 2) " }"))
;; Ergebnis fuer run-all (Konvention wie linienzug:export-results).
;; ssg-join aus ssg_core - die Zeilen sind bereits fertige JSON-Objekte.
(setq *linienzug-m2-test-results* (ssg-join zeilen ",\n"))
(princ)
)