feat: Modus 3 mit optionalem AS/ES, ES-Frage vor dem Bau; Helfer rollenbasiert benannt

WARUM MODUS 3 BLEIBT
Modus 2 und 3 arbeiten beide auf einem gezeichneten Pfad (LINE/ARC), aber sie
loesen verschiedene Aufgaben:
  Modus 2 = Randwertproblem - Start UND Ziel (je X/Y/Z) sind bekannt, die
            Zwischenhoehen werden gerechnet, die Zielhoehe wird exakt
            getroffen.
  Modus 3 = Anfangswertproblem - die Kette wird mit vorgegebenen Winkeln
            vorwaerts durchgebaut, die Endhoehe ist das ERGEBNIS.
Modus 3 kann damit etwas, was Modus 2 grundsaetzlich nicht kann: ohne
vorgegebene Zielhoehe arbeiten ("folge dieser Linie mit 3 Grad und lass uns
sehen, wo wir landen") - und er hat keinen Solver, der scheitern kann.
Dazu kommt ein harter Bestandsgrund: Modus 3 schreibt ein eigenes Journal
(Marker "linienzug3") und ist per Doppelklick editierbar. Faellt der Code
weg, verlieren alle bereits damit gebauten Ketten ihren Editier-Pfad.
Der Stempel "Legacy / wird umgebaut" war ueberholt und ist raus.

MODUS 3 - DREI AENDERUNGEN (Nutzer-Vorgabe 2026-10-22)
1. "AS bauen?" und 2. "ES bauen?" sind jetzt abfragbar (vorher wurden beide
   immer gebaut), zusammen mit Winkel und Seite in je EINEM Dialog - dasselbe
   Muster, das Modus 2 schon nutzt (vflw-gruppe-as-impl), kein neuer Code.
   Ohne AS beginnt die Kette direkt am Startpunkt (flacher Rahmen statt
   Block); die Fussabdruck-Trimmung der ersten Geraden rechnet dann von
   selbst 0, weil Rahmen und Startpunkt zusammenfallen.
3. Die ES-Frage steht jetzt VOR dem Bau. Das behebt einen echten Fehler: die
   letzte Gerade wird um "Separator + ein-dx" gekuerzt, ein-dx wurde aber
   gelesen, BEVOR es gesetzt war - beim ersten Lauf griff der Schaetzwert
   *vfk-as-es-fallback-dx*, bei jedem weiteren stand dort der Wert der
   VORHERIGEN Kette (falscher Winkel, falsche Seite). Jetzt traegt die
   Trimmung den echten Fussabdruck der gewaehlten ES-Variante; ohne ES
   entfaellt sie ganz.

ABWAERTSKOMPATIBILITAET
Das Verschieben der ES-Frage aendert die Journal-Reihenfolge. Bestehende
"linienzug3"-Ketten bleiben per Doppelklick nachbaubar: alt-journal erkennt
sie am WERT des ersten Antwort-Eintrags ("30"/"90" = alt, "1"/"2" = neu, die
Mengen sind disjunkt) und spielt sie in der alten Reihenfolge ab. Dafuer der
neue Helfer vfl-replay-peek-wert neben vfl-replay-peek-art.

DIALOG vflw_as
Die Seitenauswahl wurde bei 30 Grad ausgegraut und NICHT in die Pending-Queue
gelegt - eine reine Modus-1-Regel (dort ersetzte die Zufuehrungsachse die
Seitenfrage). Modus 1 hat seit 2026-10-21 seinen eigenen Dialog; uebrig
blieben ES-Element sowie Modus 2/3, wo die Seite IMMER gebraucht wird - dort
ging bei 30 Grad ein zweites Fenster nur fuer die Seite auf. Entfernt.

NAMENSSCHEMA (faellt in denselben Commit, weil dieselbe Datei)
Die Helfer-Praefixe vfl2-/vfl3- stammten aus einer Planungsphase vor der
endgueltigen Menue-Zuordnung und waren dazu VERTAUSCHT: alle vfl3-* gehoeren
zu Modus 2, die vfl2-* zu Modus 3 - und drei davon zu BEIDEN. Eine
Modus-Nummer war fuer diese drei also gar nicht richtig vergebbar. Sie heissen
jetzt nach ihrer Rolle: vfl-zsolve-* (rechnet die Hoehen durch, nur Modus 2)
und vfl-pfad-* (arbeitet am gezeichneten Pfad, Modus 2 UND 3). 18 Funktionen,
jede Aufrufstelle maschinell gegen die Definitionen geprueft. Die falsche
Block-Ueberschrift "MODUS 3: Pfad + Ziel-Hoehe" (gemeint war Modus 2) ist
korrigiert, beide Gruppen haben jetzt einen erklaerenden Kopf.
vfl-zsolve-bruecke (frueher vfl3-solve-bruecke) wird von niemandem aufgerufen
- bewusst stehen gelassen und markiert, Loeschen gehoert in ein
Tote-Code-Review.

MENUE
Die Modi heissen jetzt nach ihrem Unterschied statt nach der Bedienung:
"aus 3D Linie mit Zielhoehe" / "aus 3D Linie ohne Zielhoehe" (vorher
"3D Objekte stueckweise (in Arbeit)").

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 12:01:16 +02:00
parent 1cc163dc11
commit e1e5fbf0c1
10 changed files with 254 additions and 137 deletions
+4 -4
View File
@@ -101,8 +101,8 @@
(if (null *vfk-etage-hz-winkel*) (setq *vfk-etage-hz-winkel* 30.0))
;; Eckwinkel-Toleranz bei der automatischen Pfad-Klassifizierung (Modus 3,
;; vf-linienzug-modus3 -> vfl2-pruefe-eckwinkel). Aktiv referenziert in
;; vf_linienzug.lsp (beide Aufrufstellen von vfl2-pruefe-eckwinkel).
;; vf-linienzug-modus3 -> vfl-pfad-eckwinkel-pruefen). Aktiv referenziert in
;; vf_linienzug.lsp (beide Aufrufstellen von vfl-pfad-eckwinkel-pruefen).
(if (null *vfk-eckwinkel-toleranz*) (setq *vfk-eckwinkel-toleranz* 5.0))
;; Schwellwert (Grad), ab dem ein Frame noch als "flach" (~0 Grad) gilt.
@@ -123,7 +123,7 @@
;; (vf_etage.lsp Z.41)
;; - Linienzug-Kettenende/Body-Abschluss (nur Motor+1 Separator, KEIN
;; Umlenk-Anteil): 800 als rohes Literal an >10 Stellen in
;; vf_linienzug.lsp (vfl-body-zerlegung, vfl3-waehle-winkel, ...)
;; vf_linienzug.lsp (vfl-body-zerlegung, vfl-zsolve-winkel-waehlen, ...)
;; Diese vier NICHT vereinheitlichen ohne sorgfaeltige Pruefung - sie
;; entstehen aus unterschiedlicher Bauteil-Zusammensetzung (siehe jeweiliger
;; Kommentar im Modul), keine Kopiergleichheit.
@@ -166,7 +166,7 @@
;; Mindestlaenge (mm) fuer die 3-Grad-GF-Anschlussstrecke (GF1 am Einlauf
;; einer VF-Einheit). Bereits als *vfl-gf-min-laenge* in vf_linienzug.lsp
;; benannt (Wert 400.0) - hier nur gespiegelt. Das fruehere rohe 400.0-
;; Duplikat (br-gf1 in vfl3-*) referenziert jetzt *vfl-gf-min-laenge*.
;; Duplikat (br-gf1 in vfl-zsolve-*) referenziert jetzt *vfl-gf-min-laenge*.
(if (null *vfk-gf-anschluss-mindestlaenge*) (setq *vfk-gf-anschluss-mindestlaenge* 400.0))
;; ============================================================
+208 -109
View File
@@ -382,6 +382,18 @@
(while (and q (= (car (car q)) "STEP")) (setq q (cdr q)))
(if q (car (car q)) nil))
;; WERT des naechsten Replay-Eintrags, ohne ihn zu verbrauchen (nil bei leerer
;; Queue). Gegenstueck zu vfl-replay-peek-art fuer den Fall, dass die ART
;; allein nicht reicht: wird an einer Stelle eine neue Frage GLEICHEN Typs
;; eingefuegt, unterscheidet nur noch der Wert alt von neu. Genutzt in
;; vf-linienzug-modus3, wo seit 2026-10-22 ein "AS bauen?" (STR "1"/"2") VOR
;; dem AS-Winkel (STR "30"/"90") steht - die beiden Wertemengen sind disjunkt,
;; ein Blick auf den Wert erkennt das Alt-Journal also eindeutig.
(defun vfl-replay-peek-wert ( / q)
(setq q *vfl-replay-queue*)
(while (and q (= (car (car q)) "STEP")) (setq q (cdr q)))
(if q (cdr (car q)) nil))
;; ============================================================
;; TEIL 0b-1b: GLIED-SCHEMA (single source of truth)
;; ============================================================
@@ -910,33 +922,24 @@
" -> Achse "
(rtos (vfl-planar-hz achse-basis gpunkt) 2 1)
" Grad")))))
;; Seitenauswahl ist OHNE Achsen-Dialog nur beim 90-Grad-Element
;; wirksam: dort folgt die Seite beim 30-Grad-Element aus der
;; Zufuehrungsachse (Nutzer-Vorgabe 2026-10-14). Sie wird darum
;; ausgegraut - und unten auch NICHT in die Pending-Queue gelegt, sonst
;; zoege die Punktfrage sie als vermeintlichen Punkt heraus und
;; erschiene gar nicht. MIT Achsen-Dialog wird die Seite dagegen immer
;; gebraucht (sie ist die AS-Variante, nicht mehr aus der Achse
;; ableitbar) und bleibt daher auch bei 30 Grad waehlbar.
;; Die Seite ist in JEDEM verbleibenden Anwendungsfall noetig und daher
;; auch beim 30-Grad-Element waehlbar. Bis 2026-10-22 wurde sie dort
;; ausgegraut und NICHT in die Pending-Queue gelegt - das war eine
;; reine Modus-1-Regel (dort ersetzte die Zufuehrungsachse die
;; Seitenfrage). Modus 1 hat seit 2026-10-21 seinen eigenen Dialog
;; (vflw_as_achse), uebrig bleiben ES-Element sowie Modus 2/3 - und
;; dort fuehrte die Ausgrauung dazu, dass bei 30 Grad ein ZWEITES
;; Fenster nur fuer die Seite aufging (die nachfolgende Frage fand
;; keinen Wert in der Queue).
(action_tile "setzen"
(if mit-achse
(strcat
"(mode_tile \"winkel_90\" (if (= (get_tile \"setzen\") \"1\") 0 1))
(mode_tile \"winkel_30\" (if (= (get_tile \"setzen\") \"1\") 0 1))
(mode_tile \"seite_links\" (if (= (get_tile \"setzen\") \"1\") 0 1))
(mode_tile \"seite_rechts\" (if (= (get_tile \"setzen\") \"1\") 0 1))
(mode_tile \"pick\" (if (= (get_tile \"setzen\") \"1\") 0 1))"
"(mode_tile \"winkel_90\" (if (= (get_tile \"setzen\") \"1\") 0 1))
(mode_tile \"winkel_30\" (if (= (get_tile \"setzen\") \"1\") 0 1))
(mode_tile \"seite_links\" (if (and (= (get_tile \"setzen\") \"1\") (/= (get_tile \"winkel_30\") \"1\")) 0 1))
(mode_tile \"seite_rechts\" (if (and (= (get_tile \"setzen\") \"1\") (/= (get_tile \"winkel_30\") \"1\")) 0 1))"))
(if (not mit-achse)
(progn
(action_tile "winkel_30"
"(mode_tile \"seite_links\" 1)
(mode_tile \"seite_rechts\" 1)")
(action_tile "winkel_90"
"(mode_tile \"seite_links\" (if (= (get_tile \"setzen\") \"1\") 0 1))
(mode_tile \"seite_rechts\" (if (= (get_tile \"setzen\") \"1\") 0 1))")))
(mode_tile \"seite_rechts\" (if (= (get_tile \"setzen\") \"1\") 0 1))"
(if mit-achse
"(mode_tile \"pick\" (if (= (get_tile \"setzen\") \"1\") 0 1))"
"")))
(if mit-achse
(action_tile "pick"
"(setq gsetzen (get_tile \"setzen\"))
@@ -974,13 +977,11 @@
;; Winkel-Slot wird von vfl-in-value (AS-Winkel, ueber
;; vf-frage-element-winkel) konsumiert - der erwartet direkt
;; "30"/"90", NICHT den Auswahl-Index wie beim Seite-Slot.
;; Die Seite geht IMMER mit (siehe Kommentar oben), nur der
;; Achsen-Punkt haengt am Dialogtyp.
(if mit-achse
(vflw-pending-push-all (list "1" gwinkel gseite gpunkt))
;; Ohne Achsen-Dialog beim 30-Grad-Element KEINE Seite
;; mitgeben (siehe Kommentar oben).
(if (= gwinkel "30")
(vflw-pending-push-all (list "1" gwinkel))
(vflw-pending-push-all (list "1" gwinkel gseite))))
(vflw-pending-push-all (list "1" gwinkel gseite)))
(vflw-pending-push-all (list "2"))))
((= ergebnis (if mit-achse 3 2))
(setq fertig t) (setq *vfl-wizard-abbruch-grund* "zurueck"))
@@ -9800,9 +9801,22 @@
;; - GF-Bogen hat feste Eigen-Neigung -> bei unterschiedlichen Nachbar-
;; Neigungen entsteht ein (akzeptierter) Knick.
;; ============================================================
;; PFAD-HELFER (vfl-pfad-*) - geteilt von MODUS 2 UND MODUS 3
;; ============================================================
;; Alles, was auf dem GEZEICHNETEN Pfad arbeitet: Eckwinkel pruefen,
;; AS-Element einfuegen, VF-Einheit oeffnen/schliessen. Bewusst OHNE
;; Modus-Nummer im Namen - bis 2026-10-22 hiessen sie vfl2-*, was zu Modus 2
;; passte, obwohl die Haelfte davon zu Modus 3 gehoert und drei von ihnen von
;; BEIDEN Modi benutzt werden (vfl-pfad-eckwinkel-pruefen,
;; vfl-pfad-as-gf-einfuegen, vfl-pfad-as-vf-einfuegen). Eine Nummer waere hier
;; also gar nicht richtig vergebbar; der Name nennt jetzt die Rolle.
;; Gegenstueck: vfl-zsolve-* (Hoehen-Rechenkern von Modus 2, weiter unten).
;; ============================================================
;; Alle Eck-Winkel des Pfades gegen 30/60/90 Grad pruefen (Toleranz tol).
;; Rueckgabe: nil (alle ok) oder der erste abweichende Winkel (Grad).
(defun vfl2-pruefe-eckwinkel (kette tol / item obj sa ea sweep bad)
(defun vfl-pfad-eckwinkel-pruefen (kette tol / item obj sa ea sweep bad)
(setq bad nil)
(foreach item kette
(setq obj (car item))
@@ -9829,12 +9843,12 @@
;; Ergebnis: KS_EIN.X = Startpunkt.X, KS_EIN quer um das AS-Y-Mass versetzt,
;; KS_AUS liegt exakt auf dem Pfad -> die ganze Kette folgt der Mittellinie.
;; Rueckgabe: (korrigierter) Frame am KS_AUS.
;; winkel-Parameter bleibt aus Call-Kompatibilitaet bestehen (vfl2-insert-as-vf
;; winkel-Parameter bleibt aus Call-Kompatibilitaet bestehen (vfl-pfad-as-vf-einfuegen
;; ruft mit 0.0), wird aber NICHT mehr fuer eine Kippung verwendet: das AS-
;; Element hat keine eigene Neigung (per Einzel-Insert-Test in BricsCAD
;; bestaetigt, siehe vfl-insert-as-element in Modus 1) - unabhaengig vom
;; folgenden Segmenttyp (GF/VF) wird es IMMER flach eingefuegt.
(defun vfl2-insert-as-gf (startpunkt hz winkel as-seite /
(defun vfl-pfad-as-gf-einfuegen (startpunkt hz winkel as-seite /
ein-hz-as rad-h xu-ein frame blk ks-aus
rad-hz perp-x perp-y d-perp sx sy as-turn
rad-ein de-x de-y de-dot-np t-shift)
@@ -9877,12 +9891,12 @@
;; (0 Grad Neigung). Das AS wird mit KS_EIN auf den Startpunkt gesetzt und dann
;; senkrecht zu hz auf den Pfad geschoben (reine KS-Kettung, KS_AUS auf Pfad).
;; Damit liegt auch eine mit VF beginnende Kette auf dem gezeichneten Pfad.
(defun vfl2-insert-as-vf (startpunkt hz as-seite)
(vfl2-insert-as-gf startpunkt hz 0.0 as-seite))
(defun vfl-pfad-as-vf-einfuegen (startpunkt hz as-seite)
(vfl-pfad-as-gf-einfuegen startpunkt hz 0.0 as-seite))
;; VF-Einheit OEFFNEN (Modus 2): GF1 (Basis-Staustrecke) + Separator +
;; Umlenkstation. Rueckgabe: Frame nach der Umlenkstation (Beginn reiner VF).
(defun vfl2-vf-open (frame hz / f)
(defun vfl-pfad-vf-oeffnen (frame hz / f)
(setq f (vfl-frame-3grad (vfs-vf-entry (car frame) *vfl-gf-min-laenge* hz nil) hz))
(if (> *vfl-gf-min-laenge* 0.1)
(vfl-acc-gf-seg *vfl-gf-min-laenge* (ssg-cfg-or "vario" "gefaelle_winkel" 3)))
@@ -9891,7 +9905,7 @@
;; VF-Einheit SCHLIESSEN (Modus 2): Motorstation + GF2 (Basis), KEIN Separator
;; (der sitzt erst am Kettenende bzw. optional zwischen Foerderern). Rueckgabe:
;; Frame nach GF2.
(defun vfl2-vf-close (frame hz / f)
(defun vfl-pfad-vf-schliessen (frame hz / f)
(setq *vfl-acc-motorseite* (append *vfl-acc-motorseite* (list "rechts")))
(setq f (vfl-frame-3grad (vfs-vf-exit (car frame) *vfl-gf-min-laenge* hz nil) hz))
(if (> *vfl-gf-min-laenge* 0.1)
@@ -9899,7 +9913,7 @@
f)
;; Vario-Winkel (Vertikalbogen) auf die verfuegbaren Werte 3..51 einrasten.
(defun vfl2-snap-vfwinkel (w / liste best bd d)
(defun vfl-pfad-vfwinkel-snappen (w / liste best bd d)
(setq liste (ssg-cfg-or "vario" "bogen_winkel" '(3 6 9 12 15 18 21 27 33 39 45 51)))
(setq best (car liste) bd 1e9)
(foreach x liste (setq d (abs (- x w))) (if (< d bd) (setq bd d best x)))
@@ -9996,7 +10010,7 @@
(vfl-journal-record "NIL" nil) nil)
(progn
(setq segmente (gf-analysiere-kette kette))
(setq ecke-bad (vfl2-pruefe-eckwinkel kette ecke-tol))
(setq ecke-bad (vfl-pfad-eckwinkel-pruefen kette ecke-tol))
(if ecke-bad
(progn
(vfl-meldung (ssg-textf "vfl-vwnb-alert-eckwinkel" (list (rtos ecke-bad 2 1))))
@@ -10013,6 +10027,7 @@
ecke-bad first-line last-line letzt-winkel
aus ein soll-ende ist-ende run-typ richtn best-w
kvariante L_VF ent-fp rad3 just-open
as-vorhanden es-vorhanden alt-journal
dbg-an old-error dbg-old-error vfl-ins)
(princ "\n\n=========================================")
(princ (ssg-text "vfl-vwnb-titel"))
@@ -10057,27 +10072,76 @@
;; Startpunkt + Hoehe (gemeinsame Praeambel-Schale, siehe vfl-frage-punkt-hoehe).
(setq startpunkt (vfl-frage-punkt-hoehe "vfl-m3-prompt-startpunkt"
"vfl-prompt-hoehe-startpunkt" "vfl-abgebrochen" "Starthoehe (Z, mm):"))
;; AS-Element (in Modus 3 immer gesetzt): Winkel(30/90) + Seite als EIN
;; Gruppen-Dialog. vflw-gruppe-winkel-seite-impl legt Winkel ("90"/"30")
;; und Seite ("1"/"2") in *vflw-pending* ab - genau die Reihenfolge, die
;; die beiden Reads unten erwarten. AS-Winkel ueber vfl-in-value (pending-
;; aware; vf-frage-element-winkel selbst ist es NICHT), damit der vom Dialog
;; abgelegte Wert konsumiert wird statt live erneut zu fragen.
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-winkel-seite-impl
(list "vf-winkel-aus-header"
(list (ssg-text "vf-winkel-90") (ssg-text "vf-winkel-30"))
(list "90" "30"))))
(princ (ssg-text "vf-winkel-aus-header"))
(princ (ssg-text "vf-winkel-90"))
(princ (ssg-text "vf-winkel-30"))
(setq *vfl-as-winkel*
(vfl-in-value "STR"
(function (lambda () (vf-frage-element-winkel "vf-winkel-aus-header"))))) ; 30/90 vor Seite
;; --- ALT-JOURNAL-ERKENNUNG (einmal, gilt fuer AS UND ES) ---
;; Bis 2026-10-22 kannte Modus 3 weder "AS bauen?" noch "ES bauen?", und die
;; ES-Frage stand am ENDE (nach dem Bau). Seither steht vor dem AS-Winkel
;; ein Ja/Nein und die ES-Frage VOR dem Bau - beides aendert die Reihenfolge
;; im Journal. Ketten, die vorher gebaut wurden, muessen per Doppelklick
;; weiter nachbaubar bleiben; erkannt werden sie am WERT des naechsten
;; Eintrags: dort steht im Altbestand direkt der AS-Winkel ("30"/"90"), im
;; neuen Format die Ja/Nein-Antwort ("1"/"2"). Die Wertemengen sind
;; disjunkt, die Unterscheidung also eindeutig (vfl-replay-peek-wert).
;; Live (ohne Replay-Queue) ist alt-journal immer nil = neuer Ablauf.
(setq alt-journal
(and *vfl-replay-queue*
(= (vfl-replay-peek-art) "STR")
(member (vfl-replay-peek-wert) '("30" "90"))))
(if alt-journal (princ (ssg-text "vfl-m3-alt-journal")))
;; Winkel-Globals pro Lauf zuruecksetzen (wie in Modus 1/2): ohne AS bzw.
;; ES wuerde sonst der Wert der VORHERIGEN Kette in die Block-Attribute
;; WINKEL_AS/WINKEL_ES wandern.
(setq *vfl-as-winkel* nil *vfl-es-winkel* nil)
;; AS-Element: "bauen?" + Winkel(30/90) + Seite als EIN Gruppen-Dialog -
;; dasselbe Muster wie in Modus 2 (vflw-gruppe-as-impl legt Ja/Nein, Winkel
;; "90"/"30" und Seite "1"/"2" in *vflw-pending* ab, genau in der
;; Reihenfolge, die die Reads unten erwarten). Die Ja/Nein-Frage war in
;; Modus 3 bis 2026-10-22 gar nicht vorhanden (AS wurde immer gebaut,
;; Nutzer-Wunsch).
(if alt-journal
(setq as-vorhanden t)
(progn
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-as-setzen-frage" nil)))
(princ (ssg-text "vfl-as-setzen-frage"))
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-m3-as-nein"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-ja") (ssg-text "vfl-m3-as-nein")) 1
"vfl-as-setzen-frage"))
(setq as-vorhanden (/= antwort "2"))))
;; Winkel + Seite (setzt *vfl-as-winkel* und ueber vf-set-as-masse die
;; aus-dx/aus-dz der Variante) - gemeinsame Schale mit Modus 2.
(if as-vorhanden
(setq as-seite (vfl-frage-as-winkel-seite "gf-seite-aus-header")))
(if dbg-an (progn (dbgmsg "AS-Winkel:") (dbg '*vfl-as-winkel*) (dbgflush)))
(princ (ssg-text "vfl-vwnb-aus-seite-menu"))
(setq as-seite (if (= (vfl-in-string (ssg-text "prompt-wahl-1-2")) "2") "rechts" "links"))
(vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante
;; --- ES-Element JETZT fragen, VOR dem Bau (Nutzer-Vorgabe 2026-10-22,
;; wie in Modus 1) ---
;; Erst damit steht der ECHTE ES-Fussabdruck fest (ein-dx/ein-dz haengen an
;; Winkel UND Seite), den die Trimmung der LETZTEN Geraden unten abzieht.
;; Vorher wurde dort ein Wert benutzt, der zu diesem Zeitpunkt noch gar
;; nicht gesetzt war: beim ersten Lauf griff der Schaetzwert
;; *vfk-as-es-fallback-dx*, bei jedem weiteren stand dort der Wert der
;; VORHERIGEN Kette. Beim Alt-Journal bleibt die Frage an ihrer alten
;; Stelle (Schritt 7), damit die Replay-Reihenfolge stimmt.
(if alt-journal
(setq es-vorhanden t) ; Frage folgt in Schritt 7
(progn
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-es-setzen-frage" nil)))
(princ (ssg-text "vfl-es-setzen-frage"))
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-nein"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 1
"vfl-es-setzen-frage"))
(setq es-vorhanden (/= antwort "2"))
(if es-vorhanden
(setq es-seite (vfl-frage-es-winkel-seite "gf-seite-ein-header"))
;; Ohne ES wird auch kein Fussabdruck reserviert - sonst bliebe die
;; letzte Gerade um Separator+ES zu kurz.
(setq ein-dx 0.0 ein-dz 0.0))))
;; --- 3. Pfad waehlen + Sortieren + Analysieren + Eck-Vorpruefung ---
;; vfl-in-segmente kapselt Live (ssget -> gf-sortiere-objekte ->
@@ -10179,21 +10243,32 @@
;; --- GF-Gerade ---
((or (= antwort "") (= antwort "1"))
(if (= run-typ "VF") ; offenen VF-Lauf schliessen
(progn (setq frame (vfl2-vf-close frame (car (frame->hz-winkel frame))))
(progn (setq frame (vfl-pfad-vf-schliessen frame (car (frame->hz-winkel frame))))
(setq run-typ nil)))
(setq winkel (vfl-in-real (ssg-text "vfl-m3-prompt-gf-neigung")))
(if (null winkel) (setq winkel *vfk-gefaelle-winkel*))
(if (> winkel *vfk-gefaelle-winkel*)
(progn (princ (ssg-text "vfl-m3-hinweis-gf-max")) (setq winkel *vfk-gefaelle-winkel*)))
;; Ohne AS beginnt die Kette direkt am Startpunkt - nur ein flacher
;; Rahmen, kein Block (wie vfl-kettenanfang-baustein in Modus 1).
;; Die Fussabdruck-Trimmung unten rechnet dann automatisch 0, weil
;; (car frame) und startpunkt zusammenfallen.
(if (null frame)
(setq frame (vfl2-insert-as-gf startpunkt hz winkel as-seite)))
(setq frame (if as-vorhanden
(vfl-pfad-as-gf-einfuegen startpunkt hz winkel as-seite)
(make-frame-from-dir startpunkt (hz-winkel->xu hz 0.0)))))
(setq deltaL laenge)
;; erste Gerade: AS-Fussabdruck ENTLANG des Pfads exakt aus KS_AUS abziehen
(if (= i first-line)
(setq deltaL (- deltaL
(+ (* (- (car (car frame)) (car startpunkt)) (cos (* hz (/ pi 180.0))))
(* (- (cadr (car frame)) (cadr startpunkt)) (sin (* hz (/ pi 180.0))))))))
(if (= i last-line) (setq deltaL (- deltaL *vfk-separator-laenge* ein)))
;; letzte Gerade: Platz fuer Separator + ES freihalten - aber nur,
;; wenn ueberhaupt ein ES kommt (seit 2026-10-22 abfragbar). "ein"
;; traegt jetzt den ECHTEN ein-dx der gewaehlten ES-Variante, weil
;; die ES-Frage vor dem Bau steht.
(if (and (= i last-line) es-vorhanden)
(setq deltaL (- deltaL *vfk-separator-laenge* ein)))
(setq deltaL (max *vfk-restlaenge-min-clamp* deltaL))
(setq frame (vfl-insert-gf-segment (car frame) hz deltaL winkel))
(vfl-acc-gf-seg (/ deltaL (cos (* winkel (/ pi 180.0)))) winkel)
@@ -10203,16 +10278,18 @@
(setq richtn (cond ((= antwort "3") "Auf") ((= antwort "4") "horizontal") (t "Ab")))
(setq just-open nil)
(if (null frame) ; AS (flach) am Kettenanfang, KS_AUS auf Pfad
(setq frame (vfl2-insert-as-vf startpunkt hz as-seite)))
(setq frame (if as-vorhanden
(vfl-pfad-as-vf-einfuegen startpunkt hz as-seite)
(make-frame-from-dir startpunkt (hz-winkel->xu hz 0.0)))))
(if (not (= run-typ "VF")) ; VF-Einheit oeffnen
(progn (setq frame (vfl2-vf-open frame hz))
(progn (setq frame (vfl-pfad-vf-oeffnen frame hz))
(setq run-typ "VF" just-open t)))
(if (= richtn "horizontal")
(setq best-w 0)
(progn
(setq best-w (vfl-in-int (ssg-text "vfl-vwnb-prompt-vario-winkel")))
(if (null best-w) (setq best-w 3))
(setq best-w (vfl2-snap-vfwinkel best-w))
(setq best-w (vfl-pfad-vfwinkel-snappen best-w))
(if dbg-an (progn (dbgmsg "Vario-Winkel (gesnapt):") (dbg 'best-w) (dbgflush)))))
(setq L_VF laenge)
(if just-open (setq L_VF (- L_VF ent-fp))) ; Entry-Fussabdruck reservieren
@@ -10243,7 +10320,7 @@
(if (null frame)
(progn (vfl-meldung (ssg-text "vfl-m3-alert-beginnt-bogen")) (exit)))
(if (= run-typ "VF") ; VF-Lauf vor GF-Bogen schliessen
(progn (setq frame (vfl2-vf-close frame (car (frame->hz-winkel frame))))
(progn (setq frame (vfl-pfad-vf-schliessen frame (car (frame->hz-winkel frame))))
(setq run-typ nil)))
(setq frame (vfl-insert-gf-bogen-block frame bwinkel bseite))
(setq run-typ "GF"))))
@@ -10252,16 +10329,21 @@
;; --- 7. Kettenende: offenen VF-Lauf schliessen, dann Separator + ES ---
(if (null frame) (progn (princ (ssg-text "vfl-m3-nichts-gebaut")) (exit)))
;; Alt-Journal: dort steht die ES-Frage GENAU HIER (siehe Schritt 2) - die
;; Replay-Reihenfolge muss stimmen, also wird sie hier nachgeholt.
;; vfl-frage-es-seite nutzt intern bereits vfl-in-value/vfl-menu - wird also
;; automatisch mitgeloggt, kein manueller dbgmsg noetig.
(setq es-seite (vfl-frage-es-seite nil))
(if alt-journal (setq es-seite (vfl-frage-es-seite nil)))
(if (= run-typ "VF")
(progn
(setq frame (vfl2-vf-close frame (car (frame->hz-winkel frame))))
(setq frame (vfl-insert-es-element "VF" frame (car (frame->hz-winkel frame))
0.0 (caddr (car frame)) es-seite)))
(setq frame (vfl-insert-es-element "GF" frame (car (frame->hz-winkel frame))
(if letzt-winkel letzt-winkel *vfk-gefaelle-winkel*) (caddr (car frame)) es-seite)))
(setq frame (vfl-pfad-vf-schliessen frame (car (frame->hz-winkel frame))))
(if es-vorhanden
(setq frame (vfl-insert-es-element "VF" frame (car (frame->hz-winkel frame))
0.0 (caddr (car frame)) es-seite))))
(if es-vorhanden
(setq frame (vfl-insert-es-element "GF" frame (car (frame->hz-winkel frame))
(if letzt-winkel letzt-winkel *vfk-gefaelle-winkel*)
(caddr (car frame)) es-seite))))
;; --- 8. Block + Journal-XDATA + Ist-Ziel-Report ---
(setq hoehe-bis (caddr (car frame)))
@@ -10305,16 +10387,27 @@
)
;; ============================================================
;; MODUS 3: Pfad + Ziel-Hoehe (Randwert-Solver) - M3a
;; Schritt 1+2: Geruest + Klassifizierung + Anker-Report. NOCH KEIN Bauen.
;; Details siehe doc/VarioFoerderer_Linienzug_Prinzipien.md, Abschnitt 14.
;; Z-SOLVER (vfl-zsolve-*) - Rechenkern von MODUS 2 (Pfad + Ziel-Hoehe)
;; ============================================================
;; Gehoert zu vf-linienzug-modus2 (Menue-Modus 2), NICHT zu Modus 3 - die
;; Ueberschrift sagte bis 2026-10-22 faelschlich "MODUS 3", weil die Helfer
;; aus einer Planungsphase vor der endgueltigen Menue-Zuordnung stammen. Seit
;; der Umbenennung tragen sie darum gar keine Modus-Nummer mehr, sondern ihre
;; ROLLE: sie rechnen die Hoehen (Z) durch, waehrend der Grundriss (XY) vom
;; gezeichneten Pfad kommt. Genau diese Entkopplung macht das Randwertproblem
;; loesbar (siehe doc/VarioFoerderer_Linienzug_Prinzipien.md, Abschnitt 14.1).
;;
;; Gegenstueck: die vfl-pfad-*-Helfer weiter oben arbeiten auf dem
;; gezeichneten Pfad selbst (Eckwinkel pruefen, AS einfuegen, VF oeffnen/
;; schliessen) und werden von Modus 2 UND Modus 3 genutzt - auch sie tragen
;; deshalb bewusst keine Modus-Nummer.
;; ============================================================
;; Signierte Z-Aenderung (mm) einer GF-Geraden: negativ = Abfall.
;; l-planar = XY-Planlaenge (aus Pfad), winkel = Neigung in Grad (fallend).
;; Ueber die Planlaenge gilt dz = -L * tan(winkel) (Fussabdruck bleibt L,
;; die 3D-Laenge waechst mit L/cos, siehe Doc 14.1).
(defun vfl3-gf-dz (l-planar winkel / rad)
(defun vfl-zsolve-gf-dz (l-planar winkel / rad)
(setq rad (* (float winkel) (/ pi 180.0)))
(- (* (float l-planar) (/ (sin rad) (cos rad)))))
@@ -10323,10 +10416,10 @@
;; GF-Bogen -> dz aus gf-bogen-masse (Block-KS)
;; Vario-Kurve -> 0 (auf 0 Grad geflacht)
;; VF-Gerade -> nil (unbekannt = Teil der Bruecke)
(defun vfl3-seg-dz (e)
(defun vfl-zsolve-seg-dz (e)
(cond
((= (car e) "Linie")
(if (= (nth 3 e) "GF") (vfl3-gf-dz (caddr e) (nth 4 e)) nil))
(if (= (nth 3 e) "GF") (vfl-zsolve-gf-dz (caddr e) (nth 4 e)) nil))
((= (car e) "Bogen")
(if (= (nth 5 e) "Vario-Kurve")
0.0
@@ -10340,7 +10433,12 @@
;; ihre Laenge variiert (das ist der Ausgleich, siehe Doc 14.3).
;; dH-signiert: negativ = Auf (steigt), positiv = Ab (faellt).
;; Rueckgabe: (winkel L_GF L_VF richtung) oder nil (kein passender Winkel).
(defun vfl3-solve-bruecke (span dH-signiert extra-fest /
;;
;; HINWEIS: derzeit von NIEMANDEM aufgerufen - der Bau-Pfad loest die Bruecke
;; ueber vfl-zsolve-winkel-waehlen. Beim Umbenennen 2026-10-22 bewusst stehen
;; gelassen statt geloescht (gehoert in ein Tote-Code-Review, nicht in eine
;; Umbenennung).
(defun vfl-zsolve-bruecke (span dH-signiert extra-fest /
o-adx o-adz o-eix o-eiz richtung fh res winkel)
(setq richtung (if (< dH-signiert 0) "Auf" "Ab"))
(setq fh (+ (if (boundp '*vfl-feste-horizontal*) *vfl-feste-horizontal* 1300.0)
@@ -10356,7 +10454,7 @@
;; GF-Bogen-dz, wenn der Bogen bei Neigung theta (Grad) KS-gekettet wird:
;; der lokale (dx,dz) des Blocks wird um theta gekippt.
;; Z-Anteil = -dx*sin(theta) + dz*cos(theta) (negativ = Abfall)
(defun vfl3-bogen-dz-incl (bwinkel bseite theta / m dx dz rad)
(defun vfl-zsolve-bogen-dz-incl (bwinkel bseite theta / m dx dz rad)
(setq m (gf-bogen-masse bwinkel bseite) dx (car m) dz (cadr m))
(setq rad (* (float theta) (/ pi 180.0)))
(+ (* (- dx) (sin rad)) (* dz (cos rad))))
@@ -10366,7 +10464,7 @@
;; ES-Eigen-dz. Spiegelt den tatsaechlichen Bau (Trimmung der letzten Geraden um
;; 300+ein-fp, GF-Bogen bei Neigung). Eintritts-Neigung = 3 Grad (die Bruecke
;; endet mit GF2 auf 3 Grad). So wird die Junction-Hoehe exakt statt geschaetzt.
(defun vfl3-dback (plan start-idx n last-idx ein-fp / i seg drop inc w dL rad)
(defun vfl-zsolve-rueckanker (plan start-idx n last-idx ein-fp / i seg drop inc w dL rad)
(setq drop 0.0 inc *vfk-gefaelle-winkel* i start-idx)
(while (< i n)
(setq seg (nth i plan))
@@ -10380,7 +10478,7 @@
(setq inc w))
((= (car seg) "Bogen")
(if (= (nth 5 seg) "GF-Bogen")
(setq drop (- drop (vfl3-bogen-dz-incl (nth 3 seg) (nth 4 seg) inc))))))
(setq drop (- drop (vfl-zsolve-bogen-dz-incl (nth 3 seg) (nth 4 seg) inc))))))
(setq i (1+ i)))
;; Auslauf-Separator (300 mm bei aktueller Neigung inc) + ES-Eigen-dz
(setq rad (* (float inc) (/ pi 180.0)))
@@ -10394,7 +10492,7 @@
;; Horizontal-Mitte-Koerper zwei 3-Grad-Uebergangsboegen. Einbau-Rotationen wie
;; in vfs-vf-koerper; Bogen-dz_eff = -dx*sin(rot) + dz_roh*cos(rot) (am Log
;; verifiziert). So ist die Hoehe rein rechnerisch bestimmt -> Kletterlaenge folgt.
(defun vfl3-einheit-fix-dz (w n-climb n-hor gf1 richtung /
(defun vfl-zsolve-einheit-fix-dz (w n-climb n-hor gf1 richtung /
pi180 rad3 s3 tot m1 m2 r1 r2 dz1 dz2)
(setq pi180 (/ pi 180.0) rad3 (* *vfk-gefaelle-winkel* pi180) s3 (sin rad3) tot 0.0)
;; feste 3-Grad-Teile senken immer ab (GF1 + Separator + Umlenk + Motor)
@@ -10420,7 +10518,7 @@
;; + je Kletterer die zwei Boegen + je Horizontal-Mitte-Koerper die zwei
;; 3-Grad-Boegen. dx_eff = dx*cos(rot) + dz_roh*sin(rot) (am Log verifiziert).
;; Dient der Winkelwahl: Fussabdruck + Kletter-Planlaenge soll die Lauflaenge treffen.
(defun vfl3-einheit-fix-dx (w n-climb n-hor gf1 richtung /
(defun vfl-zsolve-einheit-fix-dx (w n-climb n-hor gf1 richtung /
pi180 rad3 tot m1 m2 r1 r2 dx1 dx2)
(setq pi180 (/ pi 180.0) rad3 (* *vfk-gefaelle-winkel* pi180) tot 0.0)
(setq tot (* (+ (float gf1) *vfk-separator-laenge* *vfk-stations-laenge* *vfk-stations-laenge*) (cos rad3))) ; Stationen planar
@@ -10440,13 +10538,13 @@
;; Uebergang von der 3-Grad-Kletterbasis in die FLACHE Zone (0 Grad): EIN auf_3-Bogen
;; (Rotation 3 Grad). Rueckgabe: neuer Punkt.
(defun vfl3-flach-ein (pt hz / m)
(defun vfl-zsolve-flach-ein (pt hz / m)
(setq m (get-bogen-mass bogen-auf 3))
(insert-rotated-block-with-ks "Vario_Bogen_auf_3_TEF_rechts" pt 3 (car m) (caddr m) hz))
;; Uebergang aus der flachen Zone (0 Grad) zurueck auf 3-Grad-Basis: EIN ab_3-Bogen
;; (Rotation 0 Grad). Rueckgabe: neuer Punkt.
(defun vfl3-flach-aus (pt hz / m)
(defun vfl-zsolve-flach-aus (pt hz / m)
(setq m (get-bogen-mass bogen-ab 3))
(insert-rotated-block-with-ks "Vario_Bogen_ab_3_TEF_rechts" pt 0 (car m) (caddr m) hz))
@@ -10454,7 +10552,7 @@
;; Mittige Bruecke -> KEIN terminales AS/ES (Masse auf 0, Save/Restore). feste = fester
;; Horizontal-Anteil des Kletter-Segments (Umlenk+Separator = 800; Motor sitzt spaeter
;; am Kettenende). Rueckgabe: (winkel L_GF L_VF richtung) oder nil.
(defun vfl3-waehle-winkel (span dH-signiert feste /
(defun vfl-zsolve-winkel-waehlen (span dH-signiert feste /
o-adx o-adz o-eix o-eiz richtung res wahl)
(setq richtung (if (< dH-signiert 0) "Auf" "Ab"))
(setq o-adx aus-dx o-adz aus-dz o-eix ein-dx o-eiz ein-dz)
@@ -10466,14 +10564,14 @@
;; ES-Element-Fussabdruck ENTLANG/QUER zu einer Fahrtrichtung seg-hz (Welt-
;; Komponenten), aus der REALEN Blockgeometrie (vf-element-ks-info) berechnet.
;; Kernstueck von vfl3-es-fueller, hier ausgelagert, damit AUCH die feste-vf-
;; Kernstueck von vfl-zsolve-es-fueller, hier ausgelagert, damit AUCH die feste-vf-
;; Berechnung in vf-linienzug-modus2 (Fall hor-pairs=0, OHNE flache
;; Zone/vfl3-es-fueller) denselben Wert nutzen kann, statt den ES-Laengsanteil
;; Zone/vfl-zsolve-es-fueller) denselben Wert nutzen kann, statt den ES-Laengsanteil
;; im Laengenbudget des Solvers stillschweigend zu vergessen (realer Bug:
;; Kette ueberschoss um ungefaehr along-es+Separator, siehe dortiger Kommentar).
;; Rueckgabe: (list along-es perp-es) oder nil, wenn der Block nicht lesbar
;; ist (Aufrufer faellt dann auf eine grobe Naeherung zurueck).
(defun vfl3-es-vektor (es-block seg-hz / info theta dpx dpy npx npy phi vx vy vwx vwy)
(defun vfl-zsolve-es-vektor (es-block seg-hz / info theta dpx dpy npx npy phi vx vy vwx vwy)
(setq info (vf-element-ks-info es-block))
(if (null info)
nil
@@ -10497,10 +10595,10 @@
;; KS_AUS = pt + (fill + C)*dp + perp-es*np
;; C = 196 (ab_3) + (Motor 500 + GF2 + Sep 300)*cos3 + along-es
;; Achse u_a = seg-hz + (KS_AUS.xu - KS_EIN.xu)_Block ; (Endpunkt-KS_AUS)*n_a=0 -> fill
(defun vfl3-es-fueller (pt endp seg-hz es-block gf2 rad3 /
(defun vfl-zsolve-es-fueller (pt endp seg-hz es-block gf2 rad3 /
vek theta dpx dpy npx npy info
along-es perp-es ua na-x na-y cc dp-na np-na ep-na)
(setq vek (vfl3-es-vektor es-block seg-hz))
(setq vek (vfl-zsolve-es-vektor es-block seg-hz))
(if (null vek)
(- (+ (* (- (car endp) (car pt)) (cos (* seg-hz (/ pi 180.0))))
(* (- (cadr endp) (cadr pt)) (sin (* seg-hz (/ pi 180.0)))))
@@ -10785,14 +10883,14 @@
;; Vorwaerts: Start-Z durch alle Front-Segmente (Index < vf-start).
(setq z-front (float start-hoehe) i 0)
(while (< i vf-start)
(setq dz (vfl3-seg-dz (nth i plan)))
(setq dz (vfl-zsolve-seg-dz (nth i plan)))
(if dz (setq z-front (+ z-front dz)))
(setq i (1+ i)))
;; Rueckwaerts: Ziel-Z durch alle Back-Segmente (Index > vf-ende).
;; Vorwaerts gilt Z_next = Z_prev + dz -> rueckwaerts Z_prev = Z_next - dz.
(setq z-junction (float end-hoehe) i (1- n))
(while (> i vf-ende)
(setq dz (vfl3-seg-dz (nth i plan)))
(setq dz (vfl-zsolve-seg-dz (nth i plan)))
(if dz (setq z-junction (- z-junction dz)))
(setq i (1- i)))
@@ -10862,7 +10960,7 @@
;; Back-Lauf danach) - sonst bleibt die Steuerung komplett unveraendert
;; (es-vorhanden aus der Upfront-Frage, Motorstation immer gebaut). "Ohne
;; Motorstation" muss VOR der Phase-B2-Zielhoehen-Berechnung feststehen,
;; da sie die Separator/ES-Naeherung in vfl3-dback wieder aufhebt.
;; da sie die Separator/ES-Naeherung in vfl-zsolve-rueckanker wieder aufhebt.
(setq m2-offenes-ende nil)
(if (= vf-ende (1- n))
(progn
@@ -10947,7 +11045,7 @@
;; (car frame) bereits gleich startpunkt ist.
(if (null frame)
(setq frame (if as-vorhanden
(vfl2-insert-as-gf startpunkt hz winkel as-seite)
(vfl-pfad-as-gf-einfuegen startpunkt hz winkel as-seite)
(make-frame-from-dir startpunkt (hz-winkel->xu hz 0.0)))))
(setq deltaL (caddr seg))
;; erste Gerade: AS-Fussabdruck ENTLANG des Pfads exakt aus KS_AUS abziehen
@@ -10995,17 +11093,17 @@
(setq hz (cadr (nth vf-start plan)))
(if (null frame)
(setq frame (if as-vorhanden
(vfl2-insert-as-vf startpunkt hz as-seite)
(vfl-pfad-as-vf-einfuegen startpunkt hz as-seite)
(make-frame-from-dir startpunkt (hz-winkel->xu hz 0.0)))))
(setq z-front (caddr (car frame)))
;; --- B2: Back-Abstieg -> Junction; Kletterer + Horizontal-Mitte bestimmen ---
(setq z-junction (+ (float end-hoehe)
(vfl3-dback plan (1+ vf-ende) n last-line ein)))
(vfl-zsolve-rueckanker plan (1+ vf-ende) n last-line ein)))
;; Offenes Ende (kein Motor/Separator/ES danach) ODER Motor+GF2 OHNE ES
;; danach (Lauf-Abschluss "Motorstation", es-vorhanden nil - Motor selbst
;; ist bereits ueber dH-adj/*vfk-bruecke-korrektur* separat eingerechnet,
;; siehe Phase B weiter unten): vfl3-dback's Tail-Naeherung (reserviert
;; siehe Phase B weiter unten): vfl-zsolve-rueckanker's Tail-Naeherung (reserviert
;; IMMER einen Separator+ES-Fussabdruck) ist in BEIDEN Faellen falsch - die
;; Zielhoehe fuer den Loeser ist exakt end-hoehe. Realer Bug, bis dahin nur
;; fuer m2-offenes-ende behandelt: "Motorstation ohne ES" fiel durch dieselbe
@@ -11077,7 +11175,7 @@
(setq feste-vf entry-fixed
dH-adj (- dH-bruecke (+ (* *vfk-stations-laenge* (sin rad3v)) *vfk-bruecke-korrektur*))) ; Motor + auf_3/ab_3
(progn
;; OHNE flache Zone gibt es KEIN vfl3-es-fueller, das den Separator+ES-
;; OHNE flache Zone gibt es KEIN vfl-zsolve-es-fueller, das den Separator+ES-
;; Laengsanteil selbststaendig gegen den echten Zielpunkt korrigiert -
;; beide fehlten hier bislang komplett im Laengenbudget des Solvers
;; (realer Bug: die Kette ueberschoss um ungefaehr Separator+along-es,
@@ -11085,7 +11183,7 @@
;; WICHTIG: NUR die Laenge korrigieren, NICHT ueber feste-vf/dH-adj -
;; feste-vf fliesst in berechne-alle-winkel per sin(3°) AUCH in die
;; Hoehenbilanz ein, waehrend die reale Hoehe von Separator+ES bereits
;; VOLLSTAENDIG ueber z-junction/vfl3-dback erfasst ist (Separator dort
;; VOLLSTAENDIG ueber z-junction/vfl-zsolve-rueckanker erfasst ist (Separator dort
;; per tan(3°), ES-Eigenhoehe per ein-dz - siehe dortiger Kommentar).
;; Ueber feste-vf haette das die Hoehe ein zweites Mal (und fuer
;; along-es sogar physikalisch falsch, da along-es keinen sin(3°)-
@@ -11095,7 +11193,7 @@
dH-adj dH-bruecke)
(if es-vorhanden
(progn
(setq es-vek-fix (vfl3-es-vektor
(setq es-vek-fix (vfl-zsolve-es-vektor
(strcat "ES_Element_" (vfl-es-winkel) "_" es-seite)
(cadr (nth vf-ende plan))))
(setq es-along-fix
@@ -11107,7 +11205,7 @@
;; es-along-fix (reine Laenge, siehe oben) NUR aus dem Laengenbudget (span)
;; abziehen - NICHT aus feste-vf, sonst floss es faelschlich ueber sin(3°)
;; auch in die Hoehenbilanz ein (siehe Kommentar oben).
(setq wahl3 (vfl3-waehle-winkel (- climber-span es-along-fix) dH-adj feste-vf))
(setq wahl3 (vfl-zsolve-winkel-waehlen (- climber-span es-along-fix) dH-adj feste-vf))
(if (null wahl3)
(progn
;; Stufe 2: KEIN Tabellenwinkel passt (der/die Kletterer sind bei JEDER
@@ -11186,7 +11284,7 @@
(cond
;; --- Kletterer (geneigt, 3-Grad-Basis) ---
((and (= (car seg) "Linie") (member i climbers))
(if flat-p (progn (setq pt (vfl3-flach-aus pt seg-hz)) (setq flat-p nil)))
(if flat-p (progn (setq pt (vfl-zsolve-flach-aus pt seg-hz)) (setq flat-p nil)))
(setq this-lvf (max *vfk-restlaenge-min-clamp* (* br-lvf (/ (caddr seg) climber-span))))
(setq pt (vfs-vf-koerper pt br-richtn br-winkel this-lvf seg-hz))
(vfl-acc-vf-seg br-richtn br-winkel this-lvf)
@@ -11195,7 +11293,7 @@
((= (car seg) "Linie")
(setq flat-zone-erste (not flat-p))
(if (not flat-p)
(progn (setq pt (vfl3-flach-ein pt letzt-koerper-hz)) (setq flat-p t)
(progn (setq pt (vfl-zsolve-flach-ein pt letzt-koerper-hz)) (setq flat-p t)
;; Fueller-A: der im Kletter-Segment durch 1/2 GF1 frei werdende Platz
(if (and (> filler-A-len 0.1) (not filler-a-done))
(progn (vf-vfhoehe-merke pt)
@@ -11215,7 +11313,7 @@
;; nichts mehr gebaut wird - Fueller zielt direkt auf den Endpunkt.
(setq fill-len
(if es-vorhanden
(vfl3-es-fueller pt endpunkt seg-hz
(vfl-zsolve-es-fueller pt endpunkt seg-hz
(strcat "ES_Element_" (vfl-es-winkel) "_" es-seite)
br-gf2-exp rad3v)
(- (+ (* (- (car endpunkt) (car pt)) (cos (* seg-hz (/ pi 180.0))))
@@ -11242,7 +11340,7 @@
;; --- Vario-Kurve (flach, direkt bei 0 Grad) ---
((= (car seg) "Bogen")
(if (not flat-p)
(progn (setq pt (vfl3-flach-ein pt letzt-koerper-hz)) (setq flat-p t)
(progn (setq pt (vfl-zsolve-flach-ein pt letzt-koerper-hz)) (setq flat-p t)
;; Fueller-A: der im Kletter-Segment durch 1/2 GF1 frei werdende Platz
(if (and (> filler-A-len 0.1) (not filler-a-done))
(progn (vf-vfhoehe-merke pt)
@@ -11264,13 +11362,14 @@
;; Rueck-Uebergang auf 3 Grad, KEINE Motorstation/GF2.
(setq frame (make-frame-from-dir pt (hz-winkel->xu letzt-koerper-hz 0.0)))
(progn
(if flat-p (progn (setq pt (vfl3-flach-aus pt letzt-koerper-hz)) (setq flat-p nil))) ; zurueck 3 Grad
;; Motorstation + GF2 in EINEM Schritt mit dem im Solve (vfl3-waehle-
;; winkel/GF-Verteilung, siehe br-gf2-exp oben) bereits geplanten Wert -
(if flat-p (progn (setq pt (vfl-zsolve-flach-aus pt letzt-koerper-hz)) (setq flat-p nil))) ; zurueck 3 Grad
;; Motorstation + GF2 in EINEM Schritt mit dem im Solve
;; (vfl-zsolve-winkel-waehlen/GF-Verteilung, siehe br-gf2-exp oben)
;; bereits geplanten Wert -
;; NICHT mehr unabhaengig aus der gemessenen Resthoehe neu herleiten.
;; Die alte Zwei-Loesungen-Variante (Motor mit L_GF2=0.0, GF2 danach aus
;; z-aftermotor/z-junction gemessen) widersprach der Fueller-Position
;; (fill-len, vfl3-es-fueller), die bereits br-gf2-exp als GF2-Laenge
;; (fill-len, vfl-zsolve-es-fueller), die bereits br-gf2-exp als GF2-Laenge
;; angenommen hatte - jede Differenz zwischen den beiden GF2-Werten
;; verschob das Kettenende entlang der Fahrtrichtung (realer Bug:
;; "letztes Segment baut zu lang"). Identische Fehlerklasse wie der