[CHANGE] VF-Linienzug: Winkel-Standardisierung + Glied-Schema + Vario-Kurve editierbar

Winkel im Linienzug durchgaengig als echter Wert (30/60/90) statt Auswahl-
Index (1/2/3) journalisiert - behebt den Bug, bei dem ein verrutschter
String-Wert zum falschen Bogen fuehrte (vfl-menu-winkel/-winkel-live/
-winkel-normieren, GF-Bogen und Vario-Kurve).

Zentrales Glied-Schema (*vfl-glied-schema*) als single source of truth fuer
AS/ES/GF-Bogen/Vario-Kurve: generisches bauen/lesen/validieren, benanntes
Debug-Log (GF-Bogen.winkel = 90 statt roher [INT]-Zeilen). VF-Einheit ueber
Sub-Step-Referenz (*vfl-vf-substeps*) benannt geloggt, ohne sie in ein starres
Feld-Schema zu zwingen.

Vario-Kurve als eigenes Sub-Glied editierbar (eigener STEP-Marker wie ES):
vfl-vario-slice-bauen/-werte/-splicen, vfl-glied-vario-dialog, Dispatcher-Zweig,
Label. Schwanz-erhaltender Splice (vfl-vario-slice-splicen), da die Vario-Kurve
mitten in der VF-Einheit steht und ihr Slice nachfolgende VF-Antworten enthaelt
- naiver Splice haette diese verworfen (adversarial verifiziert). Uebrige
VF-Sub-Segmente bleiben bewusst nur ueber Sektions-Ruecksprung editierbar.

Neuer Unittest tests/test_vfl_schema.lsp (TEST_VFL_SCHEMA, in alltests.json):
Roundtrip + Validierung + Vario-Schwanz-Splice ohne Geometrie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-28 23:12:30 +02:00
parent cd31380a91
commit c36d2774bf
5 changed files with 879 additions and 82 deletions
+624 -70
View File
@@ -190,6 +190,208 @@
(list (cdr e)))
nil))
;; ============================================================
;; TEIL 0b-1b: GLIED-SCHEMA (single source of truth)
;; ============================================================
;; Das Journal ist und bleibt eine flache (kind . value)-Liste - das Format auf
;; der XDATA aendert sich NICHT (keine Migration noetig, Replay unveraendert).
;; Neu ist nur eine zentrale, DEKLARATIVE Beschreibung, welche Felder in welcher
;; Reihenfolge und mit welchem Typ ein Glied ausmachen. Damit gibt es genau EINE
;; Stelle, die die Struktur eines Glieds kennt - Bauen, Lesen, Validieren und
;; das Debug-Log leiten sich alle daraus ab (kein Auseinanderlaufen von je-Typ-
;; slice-bauen/slice-werte mehr, aus dem der Winkel-Bug entstand).
;;
;; Ein Feld ist: (name kind werte). name = Feldname (fuers Log), kind = Journal-
;; kind ("STR"/"INT"/"REAL"/"PT"), werte = Liste erlaubter Werte oder nil (frei).
;; Das fuehrende STEP (Glied-Checkpoint) ist NICHT Teil der Feldliste - es wird
;; von den Slice-Helfern separat vorangestellt bzw. beim Lesen uebersprungen.
;;
;; Nur die FESTEN, kurzen Glieder sind hier erfasst (AS, ES, GF-Bogen, Vario-
;; Kurve). Die VF-Einheit/Horizontal-VF ist ein variabler Dialog-Fluss mit
;; optionsabhaengiger Feldzahl - sie ist bewusst KEIN Feld-Record und wird
;; weiter direkt behandelt (nicht einzeln editierbar, siehe vfl-edit-glied).
;;
;; Feldkonventionen (wie vom bestehenden Code erzeugt):
;; seite "1"=links / "2"=rechts
;; menue fuehrende "Naechstes Element waehlen"-Antwort (nur GF-Bogen: "1")
;; winkel GF-Bogen/Vario-Kurve als INT (30/60/90), AS/ES als STR ("30"/"90")
(setq *vfl-glied-schema*
(list
(cons "AS"
(list '("winkel" "STR" ("30" "90"))
'("seite" "STR" ("1" "2"))))
(cons "ES"
(list '("winkel" "STR" ("30" "90"))
'("seite" "STR" ("1" "2"))))
(cons "GF-Bogen"
(list '("menue" "STR" ("1"))
'("winkel" "INT" (30 60 90))
'("seite" "STR" ("1" "2"))))
(cons "Vario-Kurve"
(list '("winkel" "INT" (90 60 30))
'("seite" "STR" ("1" "2"))
'("variante" "STR" ("1" "2"))))))
;; --- VF-Einheit: Sub-Step-Referenz (variable Laenge) ---------------------
;; Die VF-Einheit (Linie-VF / Horizontal-VF) ist KEIN Glied mit fester Feldzahl,
;; sondern eine SCHLEIFE, die beliebig viele Sub-Segmente aneinanderhaengt, bis
;; der Nutzer "Ende" waehlt. Sie kann ausserdem Vario-Kurven ENTHALTEN. Ein
;; starres Feld-Schema wie oben passt daher nicht.
;;
;; Diese Referenz beschreibt die EINZELNEN Teilfragen, die im VF-Fluss auftreten
;; koennen - jede fuer sich ein kleiner benannter Record. Anders als bei den
;; festen Gliedern ist die REIHENFOLGE hier verzweigungsabhaengig; die Benennung
;; im Log erfolgt deshalb ueber das Wert-Muster (nicht ueber eine feste
;; Position). Zweck: lesbares Log ("VF.sep-vor = ja") + Wert-Validierung, OHNE
;; den fragilen Bau-Fluss nachzubauen. min/max = wie oft die Teilfrage je
;; VF-Einheit auftreten kann (Doku-Wert; die Schleife macht max offen = nil).
;;
;; Teilfrage kind werte min max Bedeutung
;; menue STR "3"/"4" 1 1 Hauptmenue-Wahl Linie-VF/Horizontal-VF
;; laenge DL - 1 n Reststrecke (Punktwahl -> dL)
;; zielhoehe REAL - 0 n Zielhoehe (nur Auf/Ab bzw. Linie-VF)
;; sep-vor STR "1"/"2" 0 n Separator vor (horizontaler Koerper)
;; sep-nach STR "1"/"2" 0 n Separator nach (horizontaler Koerper)
;; ist-ende STR "1"/"2"/"3" 1 n "Ist Endpunkt der Foerderer?" (Schleife)
;; naechstes-vf STR "1"/"2"/"3" 0 n Fortsetzung Horiz./Vario-Kurve/Auf-Ab
;; es-gewuenscht STR "1"/"2" 0 1 ES setzen? (nur Kettenende-Option 3)
;; variokurve.* siehe Glied "Vario-Kurve" (eingebettet moeglich)
;;
;; Fuer die Log-Benennung (vfl-schema-log-vf) reicht die Unterscheidung nach
;; Wert-Muster: eine STR mit Wert "3" ist ein 1-3-Menue-Schritt, "1"/"2" eine
;; Ja/Nein-Frage; REAL/DL sind Laenge bzw. Hoehe.
(setq *vfl-vf-substeps*
(list
'("STR-menue" "STR" ("3" "4")) ; fuehrende Menue-Wahl
'("STR-janein" "STR" ("1" "2")) ; sep-vor/-nach, es-gewuenscht
'("STR-menue3" "STR" ("1" "2" "3")))) ; ist-ende, naechstes-vf
;; Feldliste eines Glied-Typs (nil, wenn nicht im Schema - z.B. VF-Einheit).
(defun vfl-schema-felder (label) (cdr (assoc label *vfl-glied-schema*)))
(defun vfl-schema-feld-name (feld) (car feld))
(defun vfl-schema-feld-kind (feld) (cadr feld))
(defun vfl-schema-feld-werte (feld) (caddr feld))
;; Prueft EINEN Wert gegen eine Felddefinition. Rueckgabe: nil = ok, sonst ein
;; Fehlertext (fuer Log/Alert). Prueft (a) den Typ passend zum kind und (b) die
;; erlaubten Werte, falls im Feld angegeben.
(defun vfl-schema-feld-pruefen (feld val / kind werte typ-ok)
(setq kind (vfl-schema-feld-kind feld) werte (vfl-schema-feld-werte feld))
(setq typ-ok
(cond ((= kind "STR") (= (type val) 'STR))
((= kind "INT") (= (type val) 'INT))
((= kind "REAL") (or (= (type val) 'REAL) (= (type val) 'INT)))
((= kind "PT") (and (listp val) (= (length val) 3)))
(t t)))
(cond
((not typ-ok)
(strcat (vfl-schema-feld-name feld) ": erwartet " kind ", ist "
(vl-princ-to-string (type val))))
((and werte (not (member val werte)))
(strcat (vfl-schema-feld-name feld) "=" (vl-princ-to-string val)
" nicht in erlaubten Werten " (vl-princ-to-string werte)))
(t nil)))
;; Baut eine Journal-Slice aus einem Glied-Label und einer Werteliste (in
;; Feldreihenfolge). Stellt das STEP voran. AS ist ein Sonderfall (kein STEP -
;; Praeambel), daher hier NICHT erlaubt (nil-Rueckgabe). Validiert jeden Wert
;; vor dem Bauen; bei einem Fehler wird er als dbgmsg protokolliert, der Wert
;; aber trotzdem geschrieben (Sicherung geht vor - lieber ein warnendes Log als
;; ein verworfener Bau). Rueckgabe: forward-order Slice (Liste (kind . value)).
(defun vfl-schema-slice-bauen (label werte / felder out feld val fehler)
(setq felder (vfl-schema-felder label))
(if (or (null felder) (= label "AS"))
nil
(progn
(setq out (list (cons "STEP" label)))
(while felder
(setq feld (car felder) val (car werte))
(setq fehler (vfl-schema-feld-pruefen feld val))
(if fehler (dbgmsg (strcat ">> SCHEMA-WARN " label "." fehler)))
(setq out (append out (list (cons (vfl-schema-feld-kind feld) val))))
(setq felder (cdr felder) werte (cdr werte)))
out)))
;; Liest die Feldwerte aus einer Glied-Slice (STEP + Felder) in Feldreihenfolge.
;; Nimmt der Reihe nach den naechsten Eintrag, dessen kind zum jeweiligen Feld
;; passt (ueberspringt das fuehrende STEP automatisch). Fehlt ein Wert oder
;; stimmt der Typ nicht, wird der (optionale) Default aus defaults an dieser
;; Position eingesetzt. Rueckgabe: Werteliste in Feldreihenfolge.
(defun vfl-schema-slice-werte (label slice defaults / felder rest out feld val gefunden)
(setq felder (vfl-schema-felder label) rest slice out '())
(while felder
(setq feld (car felder) gefunden nil)
;; naechsten passenden Eintrag suchen (STEP und Fremdtypen ueberspringen)
(while (and rest (not gefunden))
(if (= (car (car rest)) (vfl-schema-feld-kind feld))
(setq val (cdr (car rest)) gefunden t))
(setq rest (cdr rest)))
(setq out (append out (list (if gefunden val (car defaults)))))
(setq felder (cdr felder) defaults (cdr defaults)))
out)
;; Benanntes Debug-Log fuer eine ganze Glied-Slice: statt roher [INT] value=90-
;; Zeilen die Felder als "GF-Bogen.winkel = 90". No-Op ohne offene .dbg-Datei.
;; Wird beim Sichern (vfl-segment-xdata-sichern) genutzt.
(defun vfl-schema-log-slice (label slice / felder werte feld fehler)
(setq felder (vfl-schema-felder label))
(if felder
(progn
(setq werte (vfl-schema-slice-werte label slice
(mapcar '(lambda (f) nil) felder)))
(dbgmsg (strcat ">> GLIED " label ":"))
(while felder
(setq feld (car felder))
(setq fehler (vfl-schema-feld-pruefen feld (car werte)))
(dbgmsg (strcat " " (vfl-schema-feld-name feld) " = "
(vl-princ-to-string (car werte))
(if fehler (strcat " <!! " fehler) "")))
(setq felder (cdr felder) werte (cdr werte)))
(dbgflush))))
;; Benennt einen EINZELNEN VF-Slice-Eintrag (kind . value) nach Wert-Muster
;; (siehe *vfl-vf-substeps*). Rueckgabe: (name . fehlertext-oder-nil). laufend =
;; wie oft schon eine STR gesehen wurde (die 1. STR einer VF-Einheit ist die
;; menue-Wahl "3"/"4", spaetere STR sind Ja/Nein- bzw. 1-3-Menue-Schritte).
(defun vfl-vf-substep-benennen (eintrag pos-str / kind val name fehler)
(setq kind (car eintrag) val (cdr eintrag))
(cond
((= kind "STEP") (cons "STEP" nil))
((= kind "DL") (cons "laenge" nil)) ; Punktwahl -> Reststrecke
((= kind "REAL") (cons "hoehe" nil)) ; Zielhoehe (Auf/Ab bzw. Linie-VF)
((= kind "PT") (cons "punkt" nil))
((= kind "INT") ; eingebettete Vario-Kurve
(setq fehler (vfl-schema-feld-pruefen '("vk-winkel" "INT" (90 60 30)) val))
(cons "variokurve.winkel" fehler))
((= kind "STR")
(cond
((= pos-str 0) ; 1. STR = fuehrende Menue-Wahl
(setq fehler (vfl-schema-feld-pruefen '("menue" "STR" ("3" "4")) val))
(cons "menue" fehler))
((member val '("3")) ; nur 1-3-Menue kann "3" sein
(cons "ist-ende/naechstes-vf" nil))
((member val '("1" "2")) ; ja/nein ODER 1-3 (1/2)
(cons "wahl(1/2)" nil))
(t (cons "str" (strcat "unerwarteter STR-Wert " (vl-princ-to-string val))))))
(t (cons kind nil))))
;; Benanntes Debug-Log fuer eine ganze VF-Einheit-Slice (variable Laenge). Statt
;; roher [STR]/[REAL]-Zeilen jede Teilfrage benannt ("VF.menue = 4",
;; "VF.laenge = 4566.7", "VF.wahl(1/2) = 2"). Validiert dabei, was validierbar
;; ist (menue-Wert, eingebettete Vario-Kurven-Winkel). No-Op ohne offene .dbg.
(defun vfl-schema-log-vf (label slice / rest e paar name fehler pos-str)
(dbgmsg (strcat ">> GLIED " label " (VF-Einheit, variable Laenge):"))
(setq rest slice pos-str 0)
(foreach e rest
(if (/= (car e) "STEP")
(progn
(setq paar (vfl-vf-substep-benennen e pos-str))
(setq name (car paar) fehler (cdr paar))
(dbgmsg (strcat " " name " = " (vl-princ-to-string (cdr e))
(if fehler (strcat " <!! " fehler) "")))
(if (= (car e) "STR") (setq pos-str (1+ pos-str))))))
(dbgflush))
;; ============================================================
;; TEIL 0b-2: WIZARD-EINGABE (DCL statt Konsole)
;; ============================================================
@@ -413,10 +615,13 @@
;; ------------------------------------------------------------
;; Gruppe "Winkel+Seite": GF-Bogen (3 Winkel-Optionen) ODER ES-Element am
;; Kettenende (2 Winkel-Optionen). Original-Reihenfolge/-Journal:
;; vfl-in-int/vfl-in-value (Winkel), dann vfl-in-string (Seite).
;; winkel-kind: "INT" (GF-Bogen, vfl-in-int liefert Zahl) oder "STR"
;; (ES-Element, vfl-in-value liefert "30"/"90" als String).
(defun vflw-gruppe-winkel-seite-impl (kopf-key winkel-optionen winkel-kind / dat dcl-pfad ergebnis gwinkel gseite)
;; vfl-menu-winkel/vfl-in-value (Winkel), dann vfl-in-string (Seite).
;; winkel-werte: Liste der zu den Optionen gehoerenden WINKELWERTE, in der
;; Reihenfolge der Labels - GF-Bogen (30 60 90), ES ("90" "30"). Der Winkel
;; wird IMMER als echter Wert in die Pending-Queue geschrieben (nie als Index),
;; damit Journal/Log selbsterklaerend sind. GF-Bogen legt Zahlen ab (vom
;; vfl-menu-winkel/vfl-in-value als "INT" journalisiert), ES Strings "90"/"30".
(defun vflw-gruppe-winkel-seite-impl (kopf-key winkel-optionen winkel-werte / dat dcl-pfad ergebnis gwinkel gseite)
(setq dat (load_dialog (vflw-dcl-pfad)))
(if (not (new_dialog "vflw_winkel_seite" dat))
(if (and dat (>= dat 0)) (unload_dialog dat))
@@ -439,14 +644,15 @@
(if (= ergebnis 1)
(vflw-pending-push-all
(list
(if (= winkel-kind "INT") (1+ (atoi gwinkel)) (nth (atoi gwinkel) '("90" "30")))
(nth (atoi gwinkel) winkel-werte)
(if (= gseite "1") "2" "1"))))))
(princ))
;; ------------------------------------------------------------
;; Gruppe "Vario-Kurve": Winkel(90/60/30) + Seite + Variante(aussen/innen).
;; Original-Reihenfolge/-Journal: vfl-in-int (Winkel), vfl-in-string
;; (Seite), vfl-in-string (Variante).
;; Reihenfolge/Journal: vfl-menu-winkel (Winkel als ECHTER Wert 90/60/30, als
;; INT journalisiert - kein Index mehr), vfl-in-string (Seite), vfl-in-string
;; (Variante).
(defun vflw-gruppe-variokurve-impl ( / dat dcl-pfad ergebnis gwinkel gseite gvariante)
(setq dat (load_dialog (vflw-dcl-pfad)))
(if (not (new_dialog "vflw_variokurve" dat))
@@ -479,7 +685,7 @@
(unload_dialog dat)
(if (= ergebnis 1)
(vflw-pending-push-all
(list (1+ (atoi gwinkel))
(list (nth (atoi gwinkel) '(90 60 30))
(if (= gseite "1") "2" "1")
(if (= gvariante "1") "2" "1"))))))
(princ))
@@ -754,6 +960,50 @@
(setq *vflw-menu-frage* frage-key)
(vfl-in-int prompt))
;; Winkel-Menue: fragt einen WINKELWERT (30/60/90) ab und journalisiert ihn
;; als echten Winkel (INT-Eintrag = 30/60/90), NICHT als Auswahl-Index 1/2/3.
;; Damit ist jedes Journal/Log selbsterklaerend und alle GF-Bogen-/Vario-Kurve-
;; Stellen (Live-Bau, Slice, Edit) teilen denselben Wert. winkel-werte ist die
;; Liste der zur Optionsreihenfolge gehoerenden Winkel (z.B. (30 60 90) oder
;; (90 60 30)); default-winkel der vorbelegte Winkelwert. Bei Konsolen-Eingabe
;; tippt der Nutzer den Winkel direkt (30/60/90); ungueltige Eingaben fallen auf
;; den naechstliegenden gueltigen Wert bzw. den Default. Rueckgabe: Winkel (Int).
(defun vfl-menu-winkel (prompt option-labels winkel-werte default-winkel frage-key /
*vflw-menu-optionen* *vflw-menu-default* *vflw-menu-frage* v)
(setq *vflw-menu-optionen* option-labels)
(setq *vflw-menu-frage* frage-key)
;; Default-Index = Position des Default-Winkels in winkel-werte (1-basiert).
(setq *vflw-menu-default*
(+ 1 (- (length winkel-werte) (length (member default-winkel winkel-werte)))))
(setq v (vfl-in-value "INT"
(function (lambda () (vfl-winkel-live prompt winkel-werte default-winkel)))))
;; Absicherung: nur gueltige Winkel zulassen (auch bei Alt-Journals mit Index).
(vfl-winkel-normieren v winkel-werte default-winkel))
;; Live-Abfrage eines Winkelwerts. Wizard: Auswahl aus den Labels, Position ->
;; winkel-werte. Konsole: direkte Zahleneingabe (ENTER = Default).
(defun vfl-winkel-live (prompt winkel-werte default-winkel / vs idx r)
(if (and *vfl-wizard-mode* *vflw-menu-optionen*)
(progn
(setq vs (vflw-wahl (if *vflw-menu-frage* (ssg-text *vflw-menu-frage*) prompt)
*vflw-menu-optionen* *vflw-menu-default*))
(setq idx (if (and vs (> (strlen vs) 0)) (atoi vs) 0))
(if (and (>= idx 1) (<= idx (length winkel-werte)))
(nth (1- idx) winkel-werte)
default-winkel))
(progn
(setq r (getint prompt))
(if r r default-winkel))))
;; Normiert einen (moeglicherweise aus Alt-Journals stammenden Index oder einen
;; frei getippten Wert) auf einen gueltigen Winkel aus winkel-werte. Ein Wert,
;; der bereits gueltig ist, bleibt unveraendert. Ansonsten Default.
(defun vfl-winkel-normieren (v winkel-werte default-winkel / n)
(setq n (cond ((null v) default-winkel)
((= (type v) 'STR) (atoi v))
(T v)))
(if (member n winkel-werte) n default-winkel))
;; Generischer Wrapper fuer eine Eingabe, die nicht direkt ein getXXX ist,
;; sondern das Ergebnis einer fragenden Hilfsfunktion (z.B. die gemeinsame
;; vf-frage-element-winkel). livefn ist ein aufrufbares Objekt ohne Argumente;
@@ -950,20 +1200,48 @@
;; ergaenzt) und schreibt sie auf JEDES seit seg-lastEnt neu erzeugte Entity
;; (entnext-Wanderung, gleiches Muster wie vfl-block-erstellen/
;; vfl-modus-abbruch-sichern).
(defun vfl-segment-xdata-sichern (seg-lastEnt wahl / forward-journal seg-idx seg-journal e)
(defun vfl-segment-xdata-sichern (seg-lastEnt wahl / forward-journal seg-idx seg-journal e cnt slice-label)
(setq forward-journal (reverse *vfl-journal*))
(setq seg-idx (length (vfl-journal-glieder forward-journal)))
(setq cnt 0)
(if (> seg-idx 0)
(progn
(setq seg-journal (vfl-journal-slice forward-journal seg-idx))
;; Tatsaechliches Label der geschnittenen Slice (der STEP-Wert am Anfang).
;; Kann von wahl (dem Hauptschleifen-Label) ABWEICHEN: enthaelt die
;; VF-Einheit eine Vario-Kurve, ist DEREN STEP das letzte Glied, seg-idx
;; zeigt darauf, und die Slice ist die Vario-Kurve-Slice - waehrend wahl
;; noch "Horizontal-VF"/"Linie-VF" ist. Fuers Log/die Validierung zaehlt
;; das echte Slice-Label, nicht wahl (sonst falsch etikettiert).
(setq slice-label
(if (and seg-journal (= (car (car seg-journal)) "STEP"))
(cdr (car seg-journal)) wahl))
(setq e (if seg-lastEnt (entnext seg-lastEnt) (entnext)))
(while e
(vfl-journal-xdata-schreiben-seg e seg-idx seg-journal)
(setq cnt (1+ cnt))
(setq e (entnext e))
)
;; Diagnose: den tatsaechlichen Segment-Journal-Inhalt als JSON loggen
;; (analog zum Ketten-XDATA-JOURNAL-JSON in vfl-journal-xdata-schreiben)
;; plus die Anzahl der beschriebenen Entities. No-Op ohne offene .dbg.
(dbgmsg (strcat "SEGMENT-XDATA-JSON Glied " (itoa seg-idx) " ("
(if slice-label slice-label "?") ") auf " (itoa cnt) " Entity(s): "
(vfl-journal->json seg-journal)))
;; Zusaetzlich benannt loggen (statt roher [INT]/[STR]-Zeilen). Feste
;; Glieder (AS/ES/GF-Bogen/Vario-Kurve) ueber ihr Feld-Schema
;; (GF-Bogen.winkel = 90), die VF-Einheit (Linie-VF/Horizontal-VF, variable
;; Laenge) ueber den Sub-Step-Logger (VF.menue = 4, VF.laenge = ...). Beide
;; validieren, was validierbar ist, und markieren Abweichungen (<!! ...>).
;; Ueber slice-label (echtes Glied), nicht wahl.
(if (vfl-schema-felder slice-label)
(vfl-schema-log-slice slice-label seg-journal)
(if (member slice-label '("Linie-VF" "Horizontal-VF"))
(vfl-schema-log-vf slice-label seg-journal)))
)
(dbgmsg (strcat "SEGMENT-XDATA: kein Glied vorhanden (" (if wahl wahl "?") ")"))
)
(dbgmsg (strcat "SEGMENT-XDATA: Glied " (itoa seg-idx) " (" (if wahl wahl "?") ") gesichert"))
(dbgflush)
(princ))
;; --- Abbruch-Sicherung ---
@@ -1078,6 +1356,9 @@
((= w "Linie-VF") (ssg-text "vfl-glied-vf"))
((= w "Horizontal-VF") (ssg-text "vfl-glied-horizontal-vf"))
((= w "Linie") (ssg-text "vfl-glied-linie"))
((= w "AS") (ssg-text "vfl-glied-as"))
((= w "ES") (ssg-text "vfl-glied-es"))
((= w "Vario-Kurve") (ssg-text "vfl-glied-vario"))
((= w "?") (ssg-text "vfl-glied-offen"))
(t w)))
@@ -2219,16 +2500,15 @@
(vl-catch-all-apply 'vflw-gruppe-winkel-seite-impl
(list "vfl-gf-bogen-winkel-header"
(list (ssg-text "vfl-winkel-30") (ssg-text "vfl-winkel-60") (ssg-text "vfl-winkel-90"))
"INT")))
'(30 60 90))))
(princ (ssg-text "vfl-gf-bogen-winkel-header"))
(princ (ssg-text "vfl-winkel-30"))
(princ (ssg-text "vfl-winkel-60"))
(princ (ssg-text "vfl-winkel-90"))
(setq antwort (vfl-menu-int (ssg-text "vfl-prompt-wahl-1-3-def3")
(setq bwinkel (vfl-menu-winkel (ssg-text "vfl-prompt-wahl-winkel-30-60-90")
(list (ssg-text "vfl-winkel-30") (ssg-text "vfl-winkel-60")
(ssg-text "vfl-winkel-90")) 3 "vfl-gf-bogen-winkel-header"))
(if (null antwort) (setq antwort 3))
(setq bwinkel (cond ((= antwort 1) 30) ((= antwort 2) 60) (t 90)))
(ssg-text "vfl-winkel-90"))
'(30 60 90) 90 "vfl-gf-bogen-winkel-header"))
(princ (ssg-text "vfl-gf-bogen-seite-header"))
(princ (ssg-text "gf-seite-links"))
(princ (ssg-text "gf-seite-rechts"))
@@ -2283,17 +2563,27 @@
;; KSYS_EIN/KSYS_AUS fuehren - extract-ks-from-block-raw normalisiert das
;; (siehe ks-normalize-name).
;; Interaktiv (i18n): Vario-Kurve-Winkel + Seite + Variante abfragen, dann Kern.
;; Die Vario-Kurve bekommt - wie das ES-Element (vfl-frage-es-seite) - einen
;; EIGENEN STEP-Marker "Vario-Kurve", obwohl sie INNERHALB der VF-Einheit-
;; Schleife gebaut wird und keinen eigenen Hauptschleifen-Durchlauf hat. Damit
;; wird sie zu einem regulaeren, waehlbaren Glied und ist ueber die bestehende
;; Glied-Maschinerie (vfl-journal-glieder/-slice/-splice) editierbar. Der Marker
;; steht VOR den drei Eingaben (Winkel INT, Seite STR, Variante STR), sodass
;; diese als Glied-Inhalt darauf folgen. Beim Replay ueberspringt vfl-replay-pop
;; den Marker automatisch; die drei Werte poppen danach regulaer. Der Glied-Edit
;; liest allein aus dem Ketten-Journal (Segment-XDATA ist nur additive Deko),
;; daher ist KEIN Umbau der Segment-XDATA-Sicherung noetig.
(defun vfl-insert-vario-kurve (frame / kwinkel kseite kvariante antwort)
(vfl-journal-mark "Vario-Kurve")
(if (vfl-wizard-aktiv) (vl-catch-all-apply 'vflw-gruppe-variokurve-impl nil))
(princ (ssg-text "vfl-variokurve-winkel-header"))
(princ (ssg-text "vfl-opt1-90grad"))
(princ (ssg-text "vfl-opt2-60grad"))
(princ (ssg-text "vfl-opt3-30grad"))
(setq antwort (vfl-menu-int (ssg-text "vfl-prompt-wahl-1-3-def1")
(setq kwinkel (vfl-menu-winkel (ssg-text "vfl-prompt-wahl-winkel-30-60-90")
(list (ssg-text "vfl-opt1-90grad") (ssg-text "vfl-opt2-60grad")
(ssg-text "vfl-opt3-30grad")) 1 "vfl-variokurve-winkel-header"))
(if (null antwort) (setq antwort 1))
(setq kwinkel (cond ((= antwort 1) 90) ((= antwort 2) 60) (t 30)))
(ssg-text "vfl-opt3-30grad"))
'(90 60 30) 90 "vfl-variokurve-winkel-header"))
(princ (ssg-text "vfl-variokurve-seite-header"))
(princ (ssg-text "gf-seite-links"))
(princ (ssg-text "gf-seite-rechts"))
@@ -2369,12 +2659,23 @@
;; ES-Winkel (30/90) + Seite erst am Kettenende abfragen. Setzt *vfl-es-winkel*
;; und die ES-Masse (ein-dx/dz) fuer die Variante. Rueckgabe: Seite (links/rechts).
;;
;; Das ES-Element bekommt einen EIGENEN STEP-Marker "ES" (vfl-journal-mark),
;; obwohl es nicht in einem eigenen Hauptschleifen-Durchlauf, sondern am Ende
;; des letzten Segments gebaut wird. Damit wird ES zu einem regulaeren "Glied"
;; und ist ueber die bestehende Glied-Maschinerie (vfl-journal-glieder/-slice/
;; -splice) genauso editierbar wie ein GF-Bogen - ohne fehleranfaelligen
;; Tail-Sonderfall. Der Marker steht VOR den beiden ES-Eingaben (Winkel, Seite),
;; sodass diese als Glied-Inhalt darauf folgen. Beim Replay wird der Marker in
;; vfl-replay-pop automatisch uebersprungen; die beiden STR-Werte poppen danach
;; regulaer.
(defun vfl-frage-es-seite ( / antwort es-seite)
(vfl-journal-mark "ES")
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-winkel-seite-impl
(list "vf-winkel-ein-header"
(list (ssg-text "vf-winkel-90") (ssg-text "vf-winkel-30"))
"STR")))
(list "90" "30"))))
(setq *vfl-es-winkel*
(vfl-in-value "STR"
(function (lambda () (vf-frage-element-winkel "vf-winkel-ein-header"))))) ; 30/90 vor Seite
@@ -3318,12 +3619,14 @@
;; bestehende Replay-Schleife von allein (jedes Glied baut relativ zum Frame
;; des Vorgaengers), ohne Zusatzlogik.
;;
;; Umfang dieser ersten Version: sauber editierbar ist der GF-Bogen (genau eine
;; Winkel- + eine Seiten-Entscheidung, ein eigenstaendiges Top-Level-Glied).
;; Andere Glieder (Linie-GF/-VF, Horizontal-VF, Linie-bis-Ende) enthalten
;; Punktwahl + Hoehen und teils VF-interne Vario-Kurven - fuer die ist der
;; Sektions-Ruecksprung ("rueck") der passende Weg. Sie werden hier mit einem
;; Hinweis abgewiesen (statt eine halbfertige In-Place-Bearbeitung anzubieten).
;; Umfang: sauber einzeln editierbar sind Glieder mit reiner Winkel-/Seiten-
;; Entscheidung ohne Punkt-/Hoehen-Eingabe: GF-Bogen (Winkel 30/60/90 + Seite),
;; AS-Element (Winkel 30/90 + Seite, liegt in der Praeambel) und ES-Element
;; (Winkel 30/90 + Seite, eigenes STEP-Glied seit vfl-frage-es-seite). Andere
;; Glieder (Linie-GF/-VF, Horizontal-VF, Linie-bis-Ende) enthalten Punktwahl +
;; Hoehen und teils VF-interne Vario-Kurven - fuer die ist der Sektions-
;; Ruecksprung ("rueck") der passende Weg. Sie werden hier mit einem Hinweis
;; abgewiesen (statt eine halbfertige In-Place-Bearbeitung anzubieten).
;; Neue Journal-Teilsequenz fuer einen GF-Bogen aus den im Dialog gewaehlten
;; Werten bauen. Aufbau exakt wie vom Live-Bau erzeugt: STEP-Eintrag, dann
@@ -3332,40 +3635,37 @@
;; den STEP-Checkpoint VOR dieser Menuefrage, vfl-journal-steplabel schreibt
;; danach nur das Label um, fuegt aber KEINEN neuen Eintrag ein; die
;; Menueantwort bleibt also als eigener Journal-Eintrag direkt nach dem STEP
;; stehen), dann ("INT" . winkel-idx) + ("STR" . seite-code) aus
;; vfl-insert-gf-bogen (vfl-menu-int Winkel, vfl-menu Seite). winkel-idx
;; 1/2/3 = 30/60/90, seite-code "1"=links, "2"=rechts. Ein GF-Bogen kann nie
;; das erste Kettenglied sein (am Kettenanfang bietet das Menue "GF-Bogen"
;; gar nicht erst an, siehe Hauptschleife) - die Menueantwort ist daher immer
;; aus dem 5er-Menue (frame vorhanden) und immer "1". Rueckgabe: forward-order
;; Journal-Teilsequenz.
(defun vfl-gf-bogen-slice-bauen (winkel-idx seite-code)
(list (cons "STEP" "GF-Bogen")
(cons "STR" "1")
(cons "INT" winkel-idx)
(cons "STR" seite-code)))
;; stehen), dann ("INT" . winkel) + ("STR" . seite-code) aus
;; vfl-insert-gf-bogen (vfl-menu-winkel Winkel, vfl-menu Seite). winkel ist der
;; ECHTE Winkelwert 30/60/90 (kein Index mehr), seite-code "1"=links,
;; "2"=rechts. Ein GF-Bogen kann nie das erste Kettenglied sein (am
;; Kettenanfang bietet das Menue "GF-Bogen" gar nicht erst an, siehe
;; Hauptschleife) - die Menueantwort ist daher immer aus dem 5er-Menue (frame
;; vorhanden) und immer "1". Rueckgabe: forward-order Journal-Teilsequenz.
;; Struktur kommt aus *vfl-glied-schema* ("GF-Bogen": menue winkel seite) -
;; hier nur der Adapter (Aufrufsignatur bleibt winkel/seite-code).
(defun vfl-gf-bogen-slice-bauen (winkel seite-code)
(vfl-schema-slice-bauen "GF-Bogen" (list "1" winkel seite-code)))
;; Aktuelle (Winkel-Index, Seiten-Code) aus einer bestehenden GF-Bogen-Slice
;; lesen (fuer die Dialog-Vorbelegung). Die Slice traegt ZWEI STR-Eintraege
;; (die fuehrende "Naechstes Element waehlen"-Menueantwort, danach - NACH dem
;; INT-Winkel - der Seiten-Code) - nur der STR NACH dem INT ist der Seiten-
;; Code, daher explizit per Positions-Flag statt "letzter gesehener STR
;; gewinnt" (siehe vfl-gf-bogen-slice-bauen). Faellt bei unerwartetem Aufbau
;; auf die Live-Bau-Defaults zurueck (Winkel 90 = idx 3, Seite links = "1").
(defun vfl-gf-bogen-slice-werte (slice / w s nach-int)
(setq w 3 s "1" nach-int nil)
(foreach e slice
(cond ((= (car e) "INT") (setq w (cdr e) nach-int t))
((and (= (car e) "STR") nach-int) (setq s (cdr e) nach-int nil))))
(list w s))
;; Aktuellen (Winkel, Seiten-Code) aus einer bestehenden GF-Bogen-Slice lesen
;; (fuer die Dialog-Vorbelegung). Liest ueber das Schema (menue/winkel/seite)
;; und wirft das menue-Feld weg. Der Winkel wird zusaetzlich ueber
;; vfl-winkel-normieren auf 30/60/90 gebracht (faengt auch Alt-Journals mit
;; Index 1/2/3 ab: idx-Werte werden zum Default 90 normiert - solche Altbloecke
;; sind ohnehin selten und der Nutzer sieht/korrigiert den Winkel im
;; vorbelegten Dialog). Defaults: menue "1", Winkel 90, Seite links "1".
(defun vfl-gf-bogen-slice-werte (slice / werte w s)
(setq werte (vfl-schema-slice-werte "GF-Bogen" slice (list "1" 90 "1")))
(setq w (cadr werte) s (caddr werte))
(list (vfl-winkel-normieren w '(30 60 90) 90) s))
;; Pre-fill-faehiger Winkel+Seite-Dialog fuer den GF-Bogen (nutzt das
;; bestehende vflw_winkel_seite-Tile aus dem Wizard-DCL, aber mit Vorbelegung
;; auf die aktuellen Werte - anders als vflw-gruppe-winkel-seite-impl, das
;; immer bei 0 startet und daher nicht wiederverwendet werden kann, ohne den
;; Live-Bau zu beruehren). Rueckgabe: (winkel-idx seite-code) oder nil bei
;; Abbruch. winkel-idx 1/2/3, seite-code "1"/"2".
(defun vfl-glied-gf-bogen-dialog (vor-winkel-idx vor-seite-code /
;; Live-Bau zu beruehren). Rueckgabe: (winkel seite-code) oder nil bei
;; Abbruch. winkel 30/60/90 (echter Wert), seite-code "1"/"2".
(defun vfl-glied-gf-bogen-dialog (vor-winkel vor-seite-code /
dat dcl-pfad ergebnis gwinkel gseite)
(setq dcl-pfad (strcat (getenv "DXFM_DCL") "/vf_linienzug_wizard.dcl"))
(setq dat (load_dialog dcl-pfad))
@@ -3379,7 +3679,9 @@
(add_list (ssg-text "vfl-winkel-60"))
(add_list (ssg-text "vfl-winkel-90"))
(end_list)
(set_tile "winkel" (itoa (1- vor-winkel-idx))) ; idx 1/2/3 -> 0-basiert 0/1/2
;; Winkel 30/60/90 -> Listenposition 0/1/2
(set_tile "winkel" (itoa (- (length '(30 60 90))
(length (member vor-winkel '(30 60 90))))))
(start_list "seite")
(add_list (ssg-text "gf-seite-links"))
(add_list (ssg-text "gf-seite-rechts"))
@@ -3391,31 +3693,253 @@
(setq ergebnis (start_dialog))
(unload_dialog dat)
(if (= ergebnis 1)
(list (1+ (atoi gwinkel)) ; 0-basiert -> idx 1/2/3
(list (nth (atoi gwinkel) '(30 60 90)) ; Listenposition 0/1/2 -> 30/60/90
(if (= gseite "1") "2" "1")) ; Liste 0=links/1=rechts -> code "1"/"2"
nil))))
;; Einzelnes Glied bearbeiten. glieder = Glied-Label-Liste (Bau-Reihenfolge).
;; journal = volles Ketten-Journal. Waehlt per Liste ein Glied, laesst dessen
;; Eigenschaften aendern (aktuell nur GF-Bogen), ersetzt die Teilsequenz und
;; baut die Kette komplett neu auf (stummer Voll-Replay des gespliceten
;; Journals). Bricht der Neuaufbau ab, wickelt der bestehende *error*-Handler
;; in vf-linienzug-modus den Zwischenstand - kein Sonderfall noetig.
(defun vfl-edit-glied (ent journal glieder / pos label alt-slice vorwerte
neuwerte neu-slice spliced)
(setq pos (vfl-dlg-position glieder))
;; Pre-fill-faehiger Winkel+Seite+Variante-Dialog fuer die Vario-Kurve (nutzt
;; das bestehende vflw_variokurve-Tile mit Vorbelegung). Wizard-Optionsreihen-
;; folge: Winkel (90 60 30), Seite (links rechts), Variante (aussen innen) -
;; genau wie vflw-gruppe-variokurve-impl. vor-* sind die aktuellen Werte:
;; vor-winkel echter Wert 90/60/30, vor-seite-code/vor-variante-code je "1"/"2".
;; Rueckgabe: (winkel seite-code variante-code) oder nil bei Abbruch.
(defun vfl-glied-vario-dialog (vor-winkel vor-seite-code vor-variante-code /
dat dcl-pfad ergebnis gwinkel gseite gvariante)
(setq dcl-pfad (strcat (getenv "DXFM_DCL") "/vf_linienzug_wizard.dcl"))
(setq dat (load_dialog dcl-pfad))
(if (not (new_dialog "vflw_variokurve" dat))
(progn (if (and dat (>= dat 0)) (unload_dialog dat))
(alert (ssg-textf "vfl-edit-dialog-fehlt" (list dcl-pfad))) nil)
(progn
(set_tile "kopf" (ssg-text "vfl-variokurve-winkel-header"))
(start_list "winkel")
(add_list (ssg-text "vfl-opt1-90grad"))
(add_list (ssg-text "vfl-opt2-60grad"))
(add_list (ssg-text "vfl-opt3-30grad"))
(end_list)
;; Winkel 90/60/30 -> Listenposition 0/1/2
(set_tile "winkel" (itoa (- (length '(90 60 30))
(length (member vor-winkel '(90 60 30))))))
(start_list "seite")
(add_list (ssg-text "gf-seite-links"))
(add_list (ssg-text "gf-seite-rechts"))
(end_list)
(set_tile "seite" (if (= vor-seite-code "2") "1" "0"))
(start_list "variante")
(add_list (ssg-text "vfl-variante-aussen"))
(add_list (ssg-text "vfl-variante-innen"))
(end_list)
(set_tile "variante" (if (= vor-variante-code "2") "1" "0"))
(action_tile "accept"
"(setq gwinkel (get_tile \"winkel\")) (setq gseite (get_tile \"seite\")) (setq gvariante (get_tile \"variante\")) (done_dialog 1)")
(action_tile "cancel" "(done_dialog 0)")
(setq ergebnis (start_dialog))
(unload_dialog dat)
(if (= ergebnis 1)
(list (nth (atoi gwinkel) '(90 60 30)) ; Listenposition 0/1/2 -> 90/60/30
(if (= gseite "1") "2" "1") ; Liste 0=links/1=rechts -> "1"/"2"
(if (= gvariante "1") "2" "1")) ; Liste 0=aussen/1=innen -> "1"/"2"
nil))))
;; Pre-fill-faehiger Winkel+Seite-Dialog fuer AS/ES-Element: nur ZWEI
;; Winkel-Optionen (90/30, in genau dieser Listen-Reihenfolge - wie
;; vf-frage-element-winkel/vflw-gruppe-winkel-seite-impl), Winkel als STRING
;; "90"/"30" (nicht Index). kopf-key waehlt die Kopfzeile (AS: vf-winkel-aus-
;; header, ES: vf-winkel-ein-header). Rueckgabe: (winkel-str seite-code) oder
;; nil bei Abbruch. seite-code "1"=links / "2"=rechts.
(defun vfl-glied-as-es-dialog (kopf-key vor-winkel-str vor-seite-code /
dat dcl-pfad ergebnis gwinkel gseite)
(setq dcl-pfad (strcat (getenv "DXFM_DCL") "/vf_linienzug_wizard.dcl"))
(setq dat (load_dialog dcl-pfad))
(if (not (new_dialog "vflw_winkel_seite" dat))
(progn (if (and dat (>= dat 0)) (unload_dialog dat))
(alert (ssg-textf "vfl-edit-dialog-fehlt" (list dcl-pfad))) nil)
(progn
(set_tile "kopf" (ssg-text kopf-key))
(start_list "winkel")
(add_list (ssg-text "vf-winkel-90"))
(add_list (ssg-text "vf-winkel-30"))
(end_list)
(set_tile "winkel" (if (= vor-winkel-str "30") "1" "0")) ; Liste 0=90/1=30
(start_list "seite")
(add_list (ssg-text "gf-seite-links"))
(add_list (ssg-text "gf-seite-rechts"))
(end_list)
(set_tile "seite" (if (= vor-seite-code "2") "1" "0"))
(action_tile "accept"
"(setq gwinkel (get_tile \"winkel\")) (setq gseite (get_tile \"seite\")) (done_dialog 1)")
(action_tile "cancel" "(done_dialog 0)")
(setq ergebnis (start_dialog))
(unload_dialog dat)
(if (= ergebnis 1)
(list (if (= gwinkel "1") "30" "90") ; Liste 0=90/1=30 -> "90"/"30"
(if (= gseite "1") "2" "1")) ; Liste 0=links/1=rechts -> "1"/"2"
nil))))
;; --- AS-/ES-Element (Winkel 30/90 + Seite links/rechts) ---
;; AS und ES sind KEINE ueber das Hauptmenue gewaehlten Glieder, ihre Slice
;; traegt daher KEINE fuehrende Menue-Antwort (anders als der GF-Bogen). Aufbau:
;; AS: gehoert in die PRAEAMBEL (vor dem 1. STEP), Eintraege: STR(winkel)
;; STR(seite) - siehe vf-linienzug-modus Kettenanfang. AS ist damit KEIN
;; STEP-Glied und wird ueber die Praeambel-Splice (vfl-journal-splice-as)
;; bearbeitet.
;; ES: hat seit vfl-frage-es-seite einen EIGENEN STEP-Marker "ES" und ist
;; damit ein regulaeres Glied. Slice: STEP("ES") STR(winkel) STR(seite).
;; winkel-str ist bei beiden "30" oder "90" (Rueckgabe vf-frage-element-winkel,
;; via vfl-in-value "STR" journalisiert), seite-code "1"=links / "2"=rechts.
;; Aktuelle (winkel-str seite-code) aus einer ES-Glied-Slice lesen (2 STR nach
;; dem STEP: erst Winkel, dann Seite). Struktur/Defaults aus *vfl-glied-schema*
;; ("ES": winkel seite). Defaults 90/links.
(defun vfl-es-slice-werte (slice)
(vfl-schema-slice-werte "ES" slice (list "90" "1")))
;; Neue ES-Glied-Slice bauen: STEP("ES") + STR(winkel) + STR(seite), Struktur
;; aus dem Schema.
(defun vfl-es-slice-bauen (winkel-str seite-code)
(vfl-schema-slice-bauen "ES" (list winkel-str seite-code)))
;; --- Vario-Kurve (Winkel 30/60/90 + Seite + Variante) --------------------
;; Slice: STEP("Vario-Kurve") INT(winkel) STR(seite) STR(variante), Struktur aus
;; *vfl-glied-schema* ("Vario-Kurve"). winkel echter Wert 90/60/30 (kein Index),
;; seite/variante als Menue-Code "1"/"2" (seite: 1=links/2=rechts, variante:
;; 1=aussen/2=innen - genau wie im Live-Bau vfl-insert-vario-kurve
;; journalisiert). Aktuelle Werte aus einer bestehenden Slice lesen; Defaults
;; 90/links/innen (innen = Live-Bau-Default, vfl-menu-Vorgabe 2).
(defun vfl-vario-slice-werte (slice / werte)
(setq werte (vfl-schema-slice-werte "Vario-Kurve" slice (list 90 "1" "2")))
;; Winkel gegen Fremdwerte absichern (analog GF-Bogen).
(list (vfl-winkel-normieren (car werte) '(90 60 30) 90)
(cadr werte) (caddr werte)))
;; Neue Vario-Kurve-Slice bauen.
(defun vfl-vario-slice-bauen (winkel seite-code variante-code)
(vfl-schema-slice-bauen "Vario-Kurve" (list winkel seite-code variante-code)))
;; Neue Vario-Kurve-Slice bauen, die den SCHWANZ der alten Slice erhaelt.
;; WICHTIG - Sonderfall gegenueber GF-Bogen/ES: Die Vario-Kurve ist das EINZIGE
;; STEP-Glied, das MITTEN in einem umschliessenden Konstrukt (der VF-Einheit)
;; steht. Nach ihren drei eigenen Werten (INT winkel, STR seite, STR variante)
;; folgen im Journal - noch VOR dem naechsten STEP - weitere Antworten der
;; VF-Einheit-Schleife ("Ist Endpunkt?", "Naechstes VF-Element?" usw.). Ein
;; naiver vfl-journal-splice (der die GANZE Slice bis zum naechsten STEP
;; ersetzt) wuerde diese nachfolgenden VF-Antworten VERWERFEN und beim Replay
;; die restliche Kette verschieben (Bug, adversarial verifiziert). Daher hier:
;; nur die drei Vario-Felder direkt nach dem STEP ersetzen, ALLE weiteren
;; Eintraege der alten Slice unveraendert anhaengen. alt-slice = die vom
;; vfl-journal-slice gelieferte Original-Slice dieses Vario-Glieds.
;; Rueckgabe: neue Slice (STEP INT STR STR + originaler Schwanz).
(defun vfl-vario-slice-splicen (alt-slice winkel seite-code variante-code /
schwanz rest n)
;; Schwanz = alt-slice OHNE das fuehrende STEP und die naechsten drei
;; Eintraege (winkel/seite/variante). Robust ueber die feste Feldzahl (3)
;; des Vario-Schemas statt ueber kind-Rateraten.
(setq rest (cdr alt-slice)) ; STEP weg
(setq n 0)
(while (and rest (< n 3)) ; die drei Vario-Felder weg
(setq rest (cdr rest) n (1+ n)))
(setq schwanz rest) ; alles Weitere (VF-Antworten) bleibt
(append (vfl-vario-slice-bauen winkel seite-code variante-code) schwanz))
;; AS-Werte aus der PRAEAMBEL lesen: die beiden STR direkt VOR dem 1. STEP, die
;; auf den AS-ja/nein-STR folgen (Reihenfolge im Journal: PT REAL STR(ja/nein)
;; [STR(winkel) STR(seite)]). Nur gueltig, wenn AS vorhanden (as-ja /= "2").
;; Rueckgabe: (winkel-str seite-code) oder nil, wenn kein AS.
(defun vfl-as-werte-lesen (journal / praeambel str-liste)
(setq praeambel (vfl-journal-praeambel journal))
(setq str-liste '())
(foreach e praeambel
(if (= (car e) "STR") (setq str-liste (cons (cdr e) str-liste))))
(setq str-liste (reverse str-liste)) ; [ja/nein, winkel, seite] in Reihenfolge
(if (and (>= (length str-liste) 3) (/= (car str-liste) "2"))
(list (cadr str-liste) (caddr str-liste))
nil))
;; Praeambel (alle Eintraege vor dem 1. STEP) als Liste in Reihenfolge.
(defun vfl-journal-praeambel (journal / out done)
(setq out '() done nil)
(foreach e journal
(if (and (not done) (= (car e) "STEP")) (setq done t))
(if (not done) (setq out (cons e out))))
(reverse out))
;; AS-Winkel/Seite in der Praeambel ersetzen (die beiden STR nach dem
;; ja/nein-STR), Rest des Journals unveraendert. Erwartet, dass AS vorhanden
;; ist (3 STR in der Praeambel). Gibt das komplette neue Journal zurueck.
(defun vfl-journal-splice-as (journal winkel-str seite-code / out str-idx done)
(setq out '() str-idx 0 done nil)
(foreach e journal
(cond
(done (setq out (cons e out))) ; ab 1. STEP: unveraendert
((= (car e) "STEP") (setq done t) (setq out (cons e out)))
((= (car e) "STR")
(setq str-idx (1+ str-idx))
(cond
((= str-idx 2) (setq out (cons (cons "STR" winkel-str) out))) ; AS-Winkel
((= str-idx 3) (setq out (cons (cons "STR" seite-code) out))) ; AS-Seite
(t (setq out (cons e out))))) ; ja/nein bleibt
(t (setq out (cons e out))))) ; PT/REAL bleiben
(reverse out))
;; Einzelnes Glied bearbeiten. glieder = Glied-Label-Liste (Bau-Reihenfolge,
;; enthaelt jetzt ggf. das synthetische "AS" ganz vorne und "ES" als regulaeres
;; Glied). journal = volles Ketten-Journal. Waehlt per Liste ein Glied, laesst
;; dessen Eigenschaften aendern (GF-Bogen, AS, ES), ersetzt die Teilsequenz und
;; baut die Kette komplett neu auf (stummer Voll-Replay). Bricht der Neuaufbau
;; ab, wickelt der bestehende *error*-Handler in vf-linienzug-modus den
;; Zwischenstand - kein Sonderfall noetig.
;;
;; glieder-alle = die dem Picker angezeigte Liste (inkl. evtl. "AS" an Position
;; 1); pos ist 1-basiert darauf. STEP-Glieder (GF-Bogen, ES, ...) werden ueber
;; ihren STEP-Index angesprochen - dieser ist pos MINUS 1, falls "AS" als
;; synthetischer erster Listeneintrag vorhanden ist (AS ist kein STEP).
(defun vfl-edit-glied (ent journal glieder-alle has-as / pos label
step-idx alt-slice vorwerte neuwerte neu-slice spliced)
(setq pos (vfl-dlg-position glieder-alle))
(if (null pos)
(progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit)))
(setq label (nth (1- pos) glieder))
(setq label (nth (1- pos) glieder-alle))
;; STEP-Index eines gewaehlten STEP-Glieds: bei vorhandenem "AS"-Listenkopf
;; um 1 nach vorne verschoben (AS belegt Listenposition 1, ist aber kein STEP).
(setq step-idx (if has-as (1- pos) pos))
(cond
;; --- AS-Element (Praeambel, kein STEP) ---
((= label "AS")
(setq vorwerte (vfl-as-werte-lesen journal))
(if (null vorwerte)
(progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit)))
(setq neuwerte (vfl-glied-as-es-dialog "vf-winkel-aus-header"
(car vorwerte) (cadr vorwerte)))
(if (null neuwerte)
(progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit)))
(setq spliced (vfl-journal-splice-as journal (car neuwerte) (cadr neuwerte)))
(entdel ent)
(princ (ssg-textf "vfl-glied-geaendert" (list (itoa pos))))
(vfl-journal-replay-start spliced)
(vf-linienzug-modus))
;; --- ES-Element (eigenes STEP-Glied) ---
((= label "ES")
(setq alt-slice (vfl-journal-slice journal step-idx))
(setq vorwerte (vfl-es-slice-werte alt-slice))
(setq neuwerte (vfl-glied-as-es-dialog "vf-winkel-ein-header"
(car vorwerte) (cadr vorwerte)))
(if (null neuwerte)
(progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit)))
(setq neu-slice (vfl-es-slice-bauen (car neuwerte) (cadr neuwerte)))
(setq spliced (vfl-journal-splice journal step-idx neu-slice))
(entdel ent)
(princ (ssg-textf "vfl-glied-geaendert" (list (itoa pos))))
(vfl-journal-replay-start spliced)
(vf-linienzug-modus))
;; --- GF-Bogen (STEP-Glied mit fuehrender Menue-Antwort) ---
((= label "GF-Bogen")
(setq alt-slice (vfl-journal-slice journal pos))
(setq alt-slice (vfl-journal-slice journal step-idx))
(setq vorwerte (vfl-gf-bogen-slice-werte alt-slice))
(setq neuwerte (vfl-glied-gf-bogen-dialog (car vorwerte) (cadr vorwerte)))
(if (null neuwerte)
(progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit)))
(setq neu-slice (vfl-gf-bogen-slice-bauen (car neuwerte) (cadr neuwerte)))
(setq spliced (vfl-journal-splice journal pos neu-slice))
(setq spliced (vfl-journal-splice journal step-idx neu-slice))
(entdel ent)
(princ (ssg-textf "vfl-glied-geaendert" (list (itoa pos))))
;; Voller stummer Replay des gespliceten Journals: die Queue beschreibt
@@ -3423,13 +3947,34 @@
;; Live-Modus, sondern baut 1:1 mit dem geaenderten Glied fertig.
(vfl-journal-replay-start spliced)
(vf-linienzug-modus))
;; --- Vario-Kurve (STEP-Glied INNERHALB einer VF-Einheit) ---
;; Gleiche Slice/Splice/Replay-Maschinerie wie GF-Bogen; die Vario-Kurve hat
;; seit vfl-insert-vario-kurve einen eigenen STEP-Marker und ist damit ein
;; regulaeres Glied. Aendert Winkel/Seite/Variante, Rest der Kette bleibt.
((= label "Vario-Kurve")
(setq alt-slice (vfl-journal-slice journal step-idx))
(setq vorwerte (vfl-vario-slice-werte alt-slice))
(setq neuwerte (vfl-glied-vario-dialog (car vorwerte) (cadr vorwerte) (caddr vorwerte)))
(if (null neuwerte)
(progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit)))
;; Schwanz-erhaltende Slice: die Vario-Kurve steht MITTEN in der VF-Einheit,
;; nach ihren 3 Werten folgen weitere VF-Antworten in derselben Slice. Nur
;; die 3 Vario-Felder ersetzen, den Rest behalten (siehe
;; vfl-vario-slice-splicen) - sonst verschiebt sich beim Replay die Kette.
(setq neu-slice (vfl-vario-slice-splicen alt-slice
(car neuwerte) (cadr neuwerte) (caddr neuwerte)))
(setq spliced (vfl-journal-splice journal step-idx neu-slice))
(entdel ent)
(princ (ssg-textf "vfl-glied-geaendert" (list (itoa pos))))
(vfl-journal-replay-start spliced)
(vf-linienzug-modus))
(t
(alert (ssg-textf "vfl-glied-nicht-editierbar"
(list (vfl-glied-label-text label))))
(princ (ssg-text "vfl-edit-abgebrochen"))))
(princ))
(defun vfl-edit-ent (ent / xd marker journal glieder n i pos trunc edit-modus)
(defun vfl-edit-ent (ent / xd marker journal glieder glieder-alle has-as n i pos trunc edit-modus)
(setq xd (vfl-journal-xdata-lesen ent))
(if (null xd)
(progn (alert (ssg-text "vfl-edit-kein-journal")) (exit)))
@@ -3439,6 +3984,15 @@
(progn
(setq glieder (vfl-journal-glieder journal))
(setq n (length glieder))
;; AS ist kein STEP-Glied (steht in der Praeambel). Fuer den Glied-Edit
;; wird es als synthetischer erster Listeneintrag "AS" vorangestellt,
;; falls in der Kette ein AS vorhanden ist (vfl-as-werte-lesen liefert
;; dann nicht nil). has-as steuert in vfl-edit-glied den STEP-Index-
;; Versatz (AS belegt Listenposition 1, ist aber kein STEP). Der Sektions-
;; Ruecksetz-Modus nutzt weiterhin die reine STEP-Liste (glieder), ES ist
;; darin bereits als eigenes Glied enthalten.
(setq has-as (and (vfl-as-werte-lesen journal) t))
(setq glieder-alle (if has-as (cons "AS" glieder) glieder))
;; NEU: zuerst Modus waehlen (Sektion zuruecksetzen ODER ein einzelnes
;; Glied bearbeiten). Alte Bloecke ohne Segment-XDATA funktionieren in
;; beiden Zweigen, da beide allein aus dem Ketten-Journal arbeiten.
@@ -3447,7 +4001,7 @@
((null edit-modus)
(princ (ssg-text "vfl-edit-abgebrochen")))
((= edit-modus "glied")
(vfl-edit-glied ent journal glieder))
(vfl-edit-glied ent journal glieder-alle has-as))
(t
;; --- bestehender Ablauf: Sektion zuruecksetzen (unveraendert) ---
(princ (ssg-textf "vfl-edit-kette-header" (list (itoa n))))