feat: AS-Zufuehrungsachse fuer beide Winkel + GF-Option "Parallel zum Kreisel"

Die AS-Praeambel in Modus 1 fragt jetzt Winkel, Seite UND eine optionale
Zufuehrungsachse (2. Punkt) - im Assistenten alles in einem Fenster
(vflw_as_achse). Beide Angaben zusammen legen das AS vollstaendig fest: der
2. Punkt gibt die KS_EIN-Achse (Zufuehrung/Fahrtrichtung), links/rechts die
AS-Variante und damit die KS_AUS-Richtung der Kette. Bisher galt die Achse
nur beim 30-Grad-Element und ERSETZTE dort die Seitenfrage.

Darauf setzt die neue 5. Option der gf-laenge-modus-Frage auf, intern
"parallel" (vfl-gf-parallel-kreisel): AS -> GF-Stueck -> GF-Bogen aus EINEM
Abstandswert, gemessen senkrecht zur Zufuehrungsachse von AS-KS_EIN bis
Bogen-KS_AUS. Kein Separator dazwischen, damit der Abstand exakt getroffen
wird; die Kettenende-Frage stellt der Zweig selbst NACH dem Bau. Waehlbar
nur am Kettenanfang mit AS UND Achse.

Der Bogen ist nicht verdrahtet: gewaehlt wird der, der den gemessenen
Grundriss-Schwenk des AS-Blocks aufhebt (vf-element-plan-turn) - mit den
vorhandenen Bausteinen AS 30 -> 30-Grad-Bogen, AS 90 -> 90-Grad-Bogen,
immer zur Gegenseite. Querversatz von AS und Bogen wird gemessen statt
gerechnet; die Bogen-Probe laeuft dabei auf dem IDEALISIERTEN 3-Grad-Rahmen,
auf dem der echte Bogen spaeter sitzt (auf dem AS-Rahmen gemessen lag der
Abstand im BricsCAD-Test um 0.5 mm daneben).

Journal: [STR winkel][STR seite][PT p2 | NIL]. Welche Fragen gestellt wurden,
wird nicht journalisiert, sondern beim Replay an der Eintragsart erkannt -
alte Ketten bleiben ohne Sonderbehandlung editierbar. Die Journal-Werkzeuge
(vf_linienzug_grammatik.py, vf_spec_export.py, vf_journal_export.py,
vf_spec.lsp, test_hm_recformat.lsp) kennen das optionale Achsen-Token.

Tests: 11 neue Unit-Tests fuer die reinen Kerne vfl-par-bogen-waehlen und
vfl-par-gf-laenge (tu-tests-vfl-parallel), TEST_UNIT 701/701 gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 11:04:16 +02:00
parent cccd362bba
commit 1cc163dc11
12 changed files with 815 additions and 150 deletions
+506 -141
View File
@@ -853,57 +853,144 @@
;; im Aufrufer-Code) - beides braucht daher keinen eigenen Parameter.
;; Original-Reihenfolge/-Journal: vfl-in-string (Ja/Nein), dann bei Ja
;; vfl-in-value (Winkel) + vfl-in-string (Seite) - bleibt unveraendert.
(defun vflw-gruppe-as-impl (kopf-key / dat dcl-pfad ergebnis gsetzen gwinkel gseite)
(setq dat (load_dialog (vflw-dcl-pfad)))
(if (not (new_dialog "vflw_as" dat))
(if (and dat (>= dat 0)) (unload_dialog dat))
(progn
(set_tile "kopf" (vflw-clean (ssg-text kopf-key)))
(set_tile "setzen" "1")
(set_tile "winkel_90" "1")
(set_tile "seite_links" "1")
;; Seitenauswahl ist nur beim 90-Grad-Element wirksam: beim 30-Grad-
;; Element folgt die Seite aus der Zufuehrungsachse, die direkt nach
;; diesem Dialog ueber einen zweiten Punkt abgefragt wird (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.
(action_tile "setzen"
"(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))")
(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))")
(action_tile "accept"
"(setq gsetzen (get_tile \"setzen\"))
(setq gwinkel (if (= (get_tile \"winkel_30\") \"1\") \"30\" \"90\"))
(setq gseite (if (= (get_tile \"seite_rechts\") \"1\") \"2\" \"1\"))
(done_dialog 1)")
(action_tile "cancel" "(done_dialog 0)")
(action_tile "back" "(done_dialog 2)")
(setq ergebnis (start_dialog))
(unload_dialog dat)
(cond
((= ergebnis 1)
(if (= gsetzen "1")
;; 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.
;; Beim 30-Grad-Element wird die Seite NICHT mitgegeben: sie ist im
;; Dialog ausgegraut und folgt aus der Zufuehrungsachse. Ein hier
;; abgelegter Wert wuerde von der nachfolgenden Punktfrage
;; faelschlich als Punkt konsumiert.
(if (= gwinkel "30")
(vflw-pending-push-all (list "1" gwinkel))
(vflw-pending-push-all (list "1" gwinkel gseite)))
(vflw-pending-push-all (list "2"))))
((= ergebnis 2) (setq *vfl-wizard-abbruch-grund* "zurueck"))
(t (setq *vfl-wizard-abbruch-grund* "abbrechen"))
;;
;; achse-basis (NUR das AS-Element am Kettenanfang von Modus 1, Nutzer-Vorgabe
;; 2026-10-21; nil = wie bisher, sonst der Kettenstartpunkt): zeigt
;; zusaetzlich die Zufuehrungsachse (2. Punkt) im SELBEN
;; Bildschirm (Dialog vflw_as_achse) und legt deren Punkt als VIERTEN Wert in
;; die Pending-Queue - Reihenfolge wie im Bau-Ablauf: ja/nein, Winkel, Seite,
;; Achsen-Punkt. Beide Angaben zusammen legen das AS vollstaendig fest: der
;; 2. Punkt gibt die KS_EIN-Achse (Zufuehrung/Fahrtrichtung), links/rechts die
;; AS-Variante und damit die KS_AUS-Richtung der Kette. Wird KEIN Punkt
;; gewaehlt, geht ein nil in die Queue (Journal-Eintrag "NIL") und die Kette
;; laeuft wie bisher ueber die Richtung des naechsten Klicks.
;; Ohne achse-basis (ES-Element, Modi 2/3) bleibt alles unveraendert -
;; inklusive der ausgegrauten Seite beim 30-Grad-Element.
;;
;; Der Punkt-Button schliesst den Dialog (done_dialog 2) - (getpoint) direkt
;; aus einem action_tile-Callback funktioniert in BricsCAD nicht (siehe
;; vflw-g-punkt-str). Picken laeuft danach auf der Kommandozeile, und MIT
;; Punkt ist der Dialog fertig: er wird NICHT erneut gezeigt (Nutzer-Wunsch
;; 2026-10-22), anders als bei vflw-gruppe-punkt-hoehe-impl, wo nach dem Punkt
;; noch ein Zahlenfeld zu fuellen ist. Nur ein mit ESC abgebrochener Pick
;; zeigt den Dialog wieder. Fuer genau diesen Fall liest schon der Pick-Button
;; die Auswahl aus und der Dialog stellt sie beim Neuaufbau wieder her -
;; new_dialog setzt sonst alle Kacheln auf die DCL-Vorgaben (90/Links) zurueck.
(defun vflw-gruppe-as-impl (kopf-key achse-basis /
mit-achse dat dlg-name ergebnis
gsetzen gwinkel gseite gpunkt fertig)
;; achse-basis traegt BEIDES: nil = kein Achsen-Dialog (ES, Modi 2/3), sonst
;; der Kettenstartpunkt als Basis fuers Gummiband der Punktwahl - genau der
;; Punkt, von dem aus die Zufuehrungsachse gemessen wird.
(setq mit-achse (if achse-basis t nil))
(setq dlg-name (if mit-achse "vflw_as_achse" "vflw_as"))
(setq gsetzen "1" gwinkel "90" gseite "1" gpunkt nil fertig nil)
(while (not fertig)
(setq dat (load_dialog (vflw-dcl-pfad)))
(if (not (new_dialog dlg-name dat))
(progn (if (and dat (>= dat 0)) (unload_dialog dat)) (setq fertig t))
(progn
(set_tile "kopf" (vflw-clean (ssg-text kopf-key)))
(set_tile "setzen" gsetzen)
(set_tile (if (= gwinkel "30") "winkel_30" "winkel_90") "1")
(set_tile (if (= gseite "2") "seite_rechts" "seite_links") "1")
;; Startpunkt IMMER zeigen - er ist der Ausgangspunkt der Achse und
;; haengt waehrend der Punktwahl am Gummiband. Der 2. Punkt samt
;; daraus entstehender Achsrichtung erscheint nur, wenn der Dialog
;; ueberhaupt noch einmal kommt (abgebrochene Punktwahl, s.u.); nach
;; einem erfolgreichen Pick schliesst der Dialog direkt, die Richtung
;; meldet dann vfl-frage-as-winkel-seite-oder-achse auf der Konsole.
(if mit-achse
(progn
(set_tile "start_anzeige"
(strcat "Startpunkt: " (vflw-g-punkt-str achse-basis)))
(if gpunkt
(set_tile "pt_anzeige"
(strcat (vflw-g-punkt-str gpunkt)
" -> 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.
(action_tile "setzen"
(if mit-achse
"(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))")))
(if mit-achse
(action_tile "pick"
"(setq gsetzen (get_tile \"setzen\"))
(setq gwinkel (if (= (get_tile \"winkel_30\") \"1\") \"30\" \"90\"))
(setq gseite (if (= (get_tile \"seite_rechts\") \"1\") \"2\" \"1\"))
(done_dialog 2)"))
(action_tile "accept"
"(setq gsetzen (get_tile \"setzen\"))
(setq gwinkel (if (= (get_tile \"winkel_30\") \"1\") \"30\" \"90\"))
(setq gseite (if (= (get_tile \"seite_rechts\") \"1\") \"2\" \"1\"))
(done_dialog 1)")
(action_tile "cancel" "(done_dialog 0)")
;; MIT Punkt-Button ist Code 2 belegt -> "Zurueck" nutzt 3 (wie in
;; vflw_punkt_zahl/vflw-gruppe-punkt-hoehe-impl).
(action_tile "back" (if mit-achse "(done_dialog 3)" "(done_dialog 2)"))
(setq ergebnis (start_dialog))
(unload_dialog dat)
;; Punkt-Button: Dialog ist zu, die Zeichnung hat den Fokus, getpoint
;; laeuft normal. MIT Punkt ist danach alles beisammen - der Ablauf
;; geht direkt in den "Ok Weiter"-Zweig, der Dialog wird NICHT noch
;; einmal gezeigt (Nutzer-Wunsch 2026-10-22: er stand vorher zweimal
;; da, einmal vor und einmal nach der Punktwahl - alle uebrigen
;; Angaben stehen beim Druck auf den Button ja schon fest, der Punkt
;; ist die letzte offene). OHNE Punkt (Abbruch mit ESC) wird der
;; Dialog dagegen erneut gezeigt, sonst waere nach einem Fehlklick
;; die ganze Eingabe weg.
(if (and mit-achse (= ergebnis 2))
(progn
(setq gpunkt (vfl-getpoint achse-basis (ssg-text "vfl-prompt-as-achse-p2")))
(if gpunkt (setq ergebnis 1))))
(cond
((= ergebnis 1)
(setq fertig t)
(if (= gsetzen "1")
;; 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.
(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 "2"))))
((= ergebnis (if mit-achse 3 2))
(setq fertig t) (setq *vfl-wizard-abbruch-grund* "zurueck"))
;; Code 2 steht hier NUR noch fuer "Punktwahl mit ESC abgebrochen"
;; (mit Punkt ist ergebnis oben auf 1 gesetzt worden): fertig bleibt
;; nil, die Schleife zeigt den Dialog mit den gemerkten Werten
;; erneut - das ist KEIN Abbruch der ganzen Kette.
((and mit-achse (= ergebnis 2)) nil)
(t (setq fertig t) (setq *vfl-wizard-abbruch-grund* "abbrechen"))
)
)
)
)
@@ -1110,16 +1197,22 @@
;; separaten Dialog (vflw-gruppe-gf-steigend-untermodus-impl, weiter unten) -
;; der 3-Optionen-Dialog hier waere dort irrefuehrend (dritte Kachel
;; "Steigend" existiert in diesem Kontext gar nicht mehr).
(defun vflw-gruppe-gf-laenge-modus-impl ( / dat ergebnis gwahl)
;; mit-parallel: T, wenn "Parallel zum Kreisel" hier ueberhaupt baubar ist
;; (Kettenanfang MIT AS-Element UND festgelegter Zufuehrungsachse - ohne
;; Bezugsachse gibt es keinen Abstand). Sonst wird die Kachel deaktiviert,
;; statt sie anzubieten und die Antwort hinterher abzulehnen.
(defun vflw-gruppe-gf-laenge-modus-impl (mit-parallel / dat ergebnis gwahl)
(setq dat (load_dialog (vflw-dcl-pfad)))
(if (not (new_dialog "vflw_gf_laenge_modus" dat))
(if (and dat (>= dat 0)) (unload_dialog dat))
(progn
(set_tile "kopf" (vflw-clean (ssg-text "vfl-gf-laenge-modus-header")))
(if (not mit-parallel) (mode_tile "modus_parallel" 1))
(action_tile "accept"
"(setq gwahl (cond ((= (get_tile \"modus_hoehe\") \"1\") \"2\")
((= (get_tile \"modus_steigend\") \"1\") \"3\")
((= (get_tile \"modus_qc\") \"1\") \"4\")
((= (get_tile \"modus_parallel\") \"1\") \"5\")
(t \"1\")))
(done_dialog 1)")
(action_tile "cancel" "(done_dialog 0)")
@@ -4717,6 +4810,227 @@
)
)
;; ------------------------------------------------------------------
;; "Parallel zum Kreisel weiterbauen" (5. Option der gf-laenge-modus-Frage,
;; intern "parallel"; Nutzer-Vorgabe 2026-10-21): der Regelfall an einer
;; Kreisel-Ausschleusung. Gebaut wird AS -> kurzes GF-Stueck (Standard-
;; Gefaelle) -> GF-Bogen, sodass die Kette danach PARALLEL zur
;; Zufuehrungsachse (= Kreisel-Laufrichtung) weiterlaeuft.
;;
;; EINZIGE Eingabe ist der ABSTAND, gemessen SENKRECHT zur Zufuehrungsachse
;; von KS_EIN des AS-Elements bis KS_AUS des Bogens. AS- und Bogen-Queranteil
;; sind durch die Bausteine fest; die Laenge des GF-Stuecks dazwischen ist die
;; einzige Unbekannte und folgt aus dem Abstand:
;; abstand = quer(AS) + L * sin(hz0 - hz_achse) + quer(Bogen)
;; (L = horizontale Laenge des GF-Stuecks wie bei vfl-insert-gf-segment).
;;
;; Warum hier KEIN Endpunkt geklickt wird: Zufuehrungsachse (2. Punkt) UND
;; AS-Seite stehen seit 2026-10-21 beide schon aus dem AS-Dialog fest (siehe
;; vfl-frage-as-winkel-seite-oder-achse) - die Achse gibt die KS_EIN-Richtung,
;; die Seite die AS-Variante und damit die KS_AUS-Richtung. Die Fahrtrichtung
;; nach dem AS ist dadurch bekannt, bevor irgendetwas gebaut ist.
;;
;; Bogenwahl NICHT fest verdrahtet: gesucht ist der Bogen, der den real
;; gemessenen Grundriss-Schwenk des AS-Blocks GENAU aufhebt
;; (as-turn + bogen-turn = 0, vf-element-plan-turn wie in vfl-qc-linie-mit-gf),
;; immer zur GEGENSEITE der AS-Seite. Mit den vorhandenen Bausteinen ergibt
;; das genau die Vorgabe-Tabelle: AS 30 Grad -> 30-Grad-Bogen, AS 90 Grad ->
;; 90-Grad-Bogen (AS links -> Bogen rechts und umgekehrt). Genau DARUM wird
;; gemessen statt eine Tabelle gepflegt: die Zuordnung folgt den Bloecken,
;; auch wenn ein Element anders vermessen ist als erwartet. Passt kein Bogen
;; in die Toleranz, wird das gemeldet statt schief gebaut.
;;
;; Quer-Anteile werden GEMESSEN, nicht gerechnet: AS ueber sein echtes KS_EIN
;; (der Klickpunkt ist der Block-URSPRUNG, KS_EIN liegt beim Drehteller-
;; Anschluss seitlich versetzt - siehe vfl-block-ks-punkt), der Bogen ueber
;; einen Probe-Bau, der danach wieder geloescht wird (dasselbe Prinzip und
;; derselbe Grund wie in vfl-qc-linie-mit-gf: die Bogen-Bausteine haengen an
;; einem bereits geneigten Rahmen, Messen ist zuverlaessiger als eine weitere
;; Rotationsformel). Der Probe-Bau haengt nur an der ANKUNFTS-Orientierung,
;; nicht an der Position - er liefert daher denselben Versatz wie der spaetere
;; echte Bau hinter dem GF-Stueck.
;;
;; KEIN Separator zwischen GF-Stueck und Bogen (Nutzer-Entscheidung
;; 2026-10-21) - der Abstand wird dadurch exakt getroffen.
;;
;; Die Kettenende-Frage stellt dieser Zweig SELBST, und zwar NACH dem Bau
;; (anders als die Zweige A/B/C, die sie vorab brauchen, um den ES-Fussabdruck
;; von der gepickten Laenge abzuziehen): hier gibt es keine gepickte Laenge,
;; von der etwas abzuziehen waere, und erst nach dem Bogen steht fest, wo die
;; Kette weiterlaeuft.
;;
;; Nutzt - wie vfl-qc-linie-mit-gf - die dynamisch sichtbaren Lokalvariablen
;; des Aufrufers vf-linienzug-modus (frame, p-aktuell, as-seite, as-vorhanden,
;; kettenanfang, fertig, es-seite, anzahl-gf, letzter-typ,
;; gf-kettenende-antwort).
;;
;; Die beiden Rechenkerne stehen bewusst als eigene, zeichnungsfreie
;; Funktionen davor (vfl-par-bogen-waehlen / vfl-par-gf-laenge) - so sind sie
;; in tests/test_unit.lsp ohne Zeichnung pruefbar (tu-tests-vfl-parallel).
;; Welcher Bogen hebt den AS-Schwenk auf? kandidaten ist eine Liste
;; (winkel . plan-turn) der real vorhandenen Bogen-Varianten EINER Seite;
;; gesucht ist der mit dem kleinsten |as-turn + plan-turn| innerhalb von tol.
;; Rueckgabe: Winkel (Int) oder nil, wenn keiner passt.
(defun vfl-par-bogen-waehlen (as-turn kandidaten tol / best best-diff diff kand)
(setq best nil best-diff nil)
(foreach kand kandidaten
(setq diff (abs (+ as-turn (cdr kand))))
(if (or (null best-diff) (< diff best-diff))
(setq best-diff diff best (car kand))))
(if (and best (<= best-diff tol)) best nil))
;; Horizontale Laenge des GF-Stuecks zwischen AS und Bogen.
;; abstand = gewuenschter Abstand (Betrag, senkrecht zur Zufuehrungsachse)
;; perp-as = Querversatz AS-KS_EIN -> AS-KS_AUS (vorzeichenbehaftet)
;; perp-bog = Querversatz des Bogens (KS_EIN -> KS_AUS, vorzeichenbehaftet)
;; k = sin(hz0 - hz-achse), Querversatz je Laengeneinheit GF
;; Die Seite steht ueber die AS-Variante schon fest - sie steckt im Vorzeichen
;; von k. Der Nutzer tippt deshalb einen reinen Betrag, das Ziel-Vorzeichen
;; wird daraus abgeleitet. Rueckgabe: Laenge (kann <= 0 sein, wenn der Abstand
;; kleiner ist als der feste Anteil von AS + Bogen) oder nil, wenn das AS gar
;; nicht aus der Achse schwenkt (k ~ 0, keine Loesung).
(defun vfl-par-gf-laenge (abstand perp-as perp-bog k / s)
(if (< (abs k) 1e-6)
nil
(progn
(setq s (if (> k 0.0) 1.0 -1.0))
(/ (- (* (abs abstand) s) perp-as perp-bog) k))))
(defun vfl-gf-parallel-kreisel ( / as-w as-turn bogen-seite bogen-winkel
kandidaten tol w
abstand abstand-min
checkpoint as-ent p-ein hz-a rad-a nx ny hz0 k
perp-as perp-bogen l-gf perp-ist rad3
acc-gfbogen-save acc-sep-save basis-frame
probe-checkpoint probe-frame dxp dyp
hz-nach erg-bogen)
(if (or (not kettenanfang) (not as-vorhanden) (null *vfl-as-basis-hz*))
(vfl-meldung (ssg-text "vfl-par-nur-kettenanfang"))
(progn
(setq as-w (vfl-as-winkel))
(setq as-turn (vf-element-plan-turn (strcat "AS_Element_" as-w "_" as-seite)))
;; Bogen zur Gegenseite der AS-Seite; Winkel aus den real vorhandenen
;; Varianten, der den AS-Schwenk am besten aufhebt.
(setq bogen-seite (if (= as-seite "rechts") "links" "rechts"))
(setq kandidaten '())
(foreach w *vfk-gf-bogen-winkel*
(setq kandidaten
(cons (cons w (vf-element-plan-turn (gf-bogen-blockname w bogen-seite)))
kandidaten)))
(setq tol 2.0) ; gleiche Toleranz wie in vfl-qc-linie-mit-gf
(setq bogen-winkel (vfl-par-bogen-waehlen as-turn (reverse kandidaten) tol))
(if (null bogen-winkel)
(vfl-meldung (ssg-textf "vfl-par-kein-bogen"
(list (rtos as-turn 2 1) bogen-seite)))
(progn
;; --- Abstand abfragen (VOR dem Bau: ein Abbruch laesst dann
;; garantiert nichts Halbfertiges stehen) ---
(setq abstand (vfl-in-real (ssg-text "vfl-par-prompt-abstand")))
(if (or (null abstand) (<= (abs abstand) 0.0))
(princ (ssg-text "vfl-abgebrochen"))
(progn
(setq checkpoint (ssg-lastent-ohne-attribute))
;; --- AS bauen: KS_EIN-Achse auf die Zufuehrungsachse ---
(setq frame (vfl-insert-as-element-auf-achse "GF" p-aktuell
*vfl-as-basis-hz* as-seite))
(setq as-ent (entlast))
(setq p-ein (if as-ent
(vfl-block-ks-punkt (vlax-ename->vla-object as-ent) "KS_EIN")
nil))
(if (null p-ein) (setq p-ein p-aktuell))
(setq hz-a *vfl-as-basis-hz*)
(setq rad-a (* (float hz-a) (/ pi 180.0)))
(setq nx (- (sin rad-a)) ny (cos rad-a)) ; Normale zur Zufuehrungsachse
(setq hz0 (car (frame->hz-winkel frame))) ; Fahrtrichtung nach dem AS
(setq perp-as (+ (* (- (car (car frame)) (car p-ein)) nx)
(* (- (cadr (car frame)) (cadr p-ein)) ny)))
;; --- Bogen probeweise bauen, Querversatz messen, wieder weg ---
;; WICHTIG - die Probe muss auf GENAU dem Rahmen sitzen, auf dem
;; der echte Bogen spaeter steht, sonst misst sie eine andere
;; Lage. Das ist NICHT der Ausgangsrahmen des AS-Elements: der
;; traegt die reale Neigung des AS-KS_AUS, waehrend der Bogen
;; hinter dem GF-Stueck auf dessen IDEALISIERTEM Rahmen sitzt
;; (make-frame-from-dir + hz-winkel->xu mit genau hz0 und dem
;; Standard-Gefaellewinkel, siehe vfl-insert-gf-segment). Realer
;; Befund 2026-10-22 (BricsCAD): auf dem AS-Rahmen gemessen fiel
;; der Probe-Bogen 31.3 mm statt 22.6 mm - die daraus falsche
;; Verdrehung liess den Abstand um 0.5 mm danebenliegen. Mit dem
;; idealisierten Rahmen sind Probe und echter Bau per
;; Konstruktion identisch.
;; vfl-insert-gf-bogen-block zaehlt intern mit (GF_Bogen_L/R_xx) -
;; die Probe darf diese Zaehler nicht verfaelschen.
(setq basis-frame
(make-frame-from-dir (car frame)
(hz-winkel->xu hz0 (fix *vfk-gefaelle-winkel*))))
(setq acc-gfbogen-save *vfl-acc-gfbogen* acc-sep-save *vfl-acc-separator*)
(setq probe-checkpoint (ssg-lastent-ohne-attribute))
(setq probe-frame (vfl-insert-gf-bogen-block basis-frame bogen-winkel bogen-seite))
(setq dxp (- (car (car probe-frame)) (car (car basis-frame))))
(setq dyp (- (cadr (car probe-frame)) (cadr (car basis-frame))))
(vfl-loesche-seit probe-checkpoint)
(setq *vfl-acc-gfbogen* acc-gfbogen-save *vfl-acc-separator* acc-sep-save)
(setq perp-bogen (+ (* dxp nx) (* dyp ny)))
;; --- GF-Laenge aus dem Abstand loesen (reiner Kern) ---
(setq k (sin (* (- hz0 hz-a) (/ pi 180.0))))
(setq l-gf (vfl-par-gf-laenge abstand perp-as perp-bogen k))
(if (null l-gf)
(progn
(vfl-meldung (ssg-text "vfl-par-kein-versatz"))
(vfl-loesche-seit checkpoint))
(progn
;; Kleinster erreichbarer Abstand = der von AS und Bogen
;; allein (GF-Stueck = 0).
(setq abstand-min (abs (+ perp-as perp-bogen)))
(if (< l-gf 1.0)
(progn
(vfl-meldung (ssg-textf "vfl-par-zu-klein"
(list (rtos (abs abstand) 2 1) (rtos abstand-min 2 1))))
(vfl-loesche-seit checkpoint))
(progn
(setq rad3 (* (float (fix *vfk-gefaelle-winkel*)) (/ pi 180.0)))
(setq frame (vfl-insert-gf-segment (car frame) hz0 l-gf
(fix *vfk-gefaelle-winkel*)))
(vfl-acc-gf-seg (/ l-gf (cos rad3)) (fix *vfk-gefaelle-winkel*))
(setq anzahl-gf (1+ anzahl-gf))
(setq frame (vfl-insert-gf-bogen-block frame bogen-winkel bogen-seite))
(setq p-aktuell (car frame))
(setq letzter-typ "GF")
(setq perp-ist (abs (+ (* (- (car p-aktuell) (car p-ein)) nx)
(* (- (cadr p-aktuell) (cadr p-ein)) ny))))
(princ (ssg-textf "vfl-par-erfolg"
(list (rtos l-gf 2 1) (itoa bogen-winkel) bogen-seite
(rtos perp-ist 2 1))))
;; --- Kettenende/ES (siehe Kopfkommentar: hier NACH dem Bau) ---
(setq hz-nach (car (frame->hz-winkel frame)))
(setq gf-kettenende-antwort (vfl-frage-ist-kettenende))
(cond
((= gf-kettenende-antwort "1")
(setq es-seite (vfl-frage-es-seite nil))
;; Uebergangsbogen pruefen (No-Op, wenn die Kette hier
;; ohnehin auf der Fallend-Basis ankommt) - dieselbe
;; Absicherung wie im gemeinsamen Abschluss der Zweige
;; A/B/C.
(setq erg-bogen (vfl-gf-bogen-uebergang frame *vfk-gefaelle-winkel*))
(setq frame (car erg-bogen))
(setq frame (vfl-insert-es-element "GF" frame hz-nach
*vfk-gefaelle-winkel* (caddr (car frame)) es-seite))
(setq p-aktuell (car frame))
(setq fertig t))
((= gf-kettenende-antwort "2") (setq fertig t))
)
)
)
)
)
)
)
)
)
)
)
)
;; ------------------------------------------------------------------
;; "QC Linie mit GF und U-Form" (Option 5 der gf-laenge-modus-Frage,
;; Nutzer-Vorgabe 2026-09-13, nach mehreren Praezisierungsrunden - inkl. echter
@@ -5227,7 +5541,7 @@
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-es-nein-zielpunkt"))
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-es-setzen-frage")))
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-es-setzen-frage" nil)))
(setq es-antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-ja") (ssg-text "vfl-es-nein-zielpunkt")) 1
"vfl-es-setzen-frage"))
@@ -6589,25 +6903,33 @@
(progn
;; BASISLINIEN-Modus: das AS gehoert mit seinem KS_EIN auf die
;; Zufuehrungsachse (*vfl-as-basis-hz*), nicht mit dem KS_AUS auf die
;; Kettenrichtung. Die Seite ergibt sich aus der Lage des naechsten
;; Kettenpunkts zur Basislinie (Nutzer-Vorgabe 2026-10-14): der liegt in
;; Richtung hz-neu, deltaL entfernt.
;; Kettenrichtung.
;;
;; Die Seite kommt seit 2026-10-21 normalerweise AUS DEM DIALOG
;; (*vfl-as-seite-explizit*) - sie ist die AS-Variante und darf dann
;; nicht mehr ueberschrieben werden. Nur beim Replay eines
;; Alt-Achsen-Journals (30 Grad, 2026-10-14..10-20) wurde sie gar nicht
;; gefragt; dort ergibt sie sich wie bisher aus der Lage des naechsten
;; Kettenpunkts zur Basislinie: der liegt in Richtung hz-neu, deltaL
;; entfernt.
(if *vfl-as-basis-hz*
(progn
(setq rad (* (float hz-neu) (/ pi 180.0)))
(setq ziel (list (+ (car p-aktuell) (* deltaL (cos rad)))
(+ (cadr p-aktuell) (* deltaL (sin rad)))
(caddr p-aktuell)))
(setq as-seite (vfl-seite-aus-lage *vfl-as-basis-p1*
(list (+ (car *vfl-as-basis-p1*)
(cos (* *vfl-as-basis-hz* (/ pi 180.0))))
(+ (cadr *vfl-as-basis-p1*)
(sin (* *vfl-as-basis-hz* (/ pi 180.0))))
0.0)
ziel))
;; Masse auf die jetzt bekannte Seite umstellen (Winkel steht schon).
(vf-set-as-masse (vfl-as-winkel) as-seite)
(princ (ssg-textf "vfl-as-seite-ermittelt" (list as-seite)))
(if (not *vfl-as-seite-explizit*)
(progn
(setq rad (* (float hz-neu) (/ pi 180.0)))
(setq ziel (list (+ (car p-aktuell) (* deltaL (cos rad)))
(+ (cadr p-aktuell) (* deltaL (sin rad)))
(caddr p-aktuell)))
(setq as-seite (vfl-seite-aus-lage *vfl-as-basis-p1*
(list (+ (car *vfl-as-basis-p1*)
(cos (* *vfl-as-basis-hz* (/ pi 180.0))))
(+ (cadr *vfl-as-basis-p1*)
(sin (* *vfl-as-basis-hz* (/ pi 180.0))))
0.0)
ziel))
;; Masse auf die jetzt bekannte Seite umstellen (Winkel steht schon).
(vf-set-as-masse (vfl-as-winkel) as-seite)
(princ (ssg-textf "vfl-as-seite-ermittelt" (list as-seite)))))
;; KS_EIN auf die Zufuehrungsachse legen - NICHT KS_AUS auf hz-neu.
(setq frame (vfl-insert-as-element-auf-achse "GF" p-aktuell
*vfl-as-basis-hz* as-seite)))
@@ -6695,72 +7017,57 @@
(vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante
as-seite)
;; AS-Winkel abfragen und danach ENTWEDER die Seite ODER - nur beim 30-Grad-
;; Element - eine Zufuehrungsachse (Nutzer-Vorgabe 2026-10-14: "bitte erstmal
;; nur fuer 30 Grad").
;; AS-Winkel abfragen, danach die Seite UND - fuer BEIDE Winkel - eine
;; optionale Zufuehrungsachse (Nutzer-Vorgabe 2026-10-21; bis dahin galt die
;; Achse nur fuer das 30-Grad-Element und ERSETZTE dort die Seitenfrage).
;;
;; Warum erst NACH dem Winkel: die Achsenfrage gilt nur fuer AS_Element_30, der
;; Winkel muss also schon feststehen. Der Kettenstartpunkt bleibt davon
;; unberuehrt - er ist und bleibt der echte Sitz des AS-Elements; die Achse
;; entsteht aus ihm plus EINEM weiteren Punkt, der nur die Richtung gibt.
;; Warum beides und warum in dieser Reihenfolge: die zwei Angaben beschreiben
;; verschiedene Dinge und legen das AS erst ZUSAMMEN vollstaendig fest -
;; 2. Punkt -> Richtung der KS_EIN-Achse (die Zufuehrung, z.B. die
;; Kreisel-Laufrichtung); der Startpunkt bleibt der Sitz des
;; AS-Elements, der zweite Punkt gibt nur die Richtung.
;; links/rechts -> AS-Variante und damit die KS_AUS-Richtung, also wohin die
;; Kette nach dem Ausschleusen laeuft.
;; Damit steht die Kettenrichtung schon vor dem ersten Streckenklick fest -
;; Voraussetzung fuer "Parallel zum Kreisel" (vfl-gf-parallel-kreisel), das
;; gar keinen Endpunkt mehr klickt, sondern nur noch einen Abstand bekommt.
;; Die Achse ist optional: ohne Punkt laeuft alles wie frueher ueber die
;; Richtung des naechsten Klicks.
;;
;; JOURNAL - deswegen ist die Reihenfolge so gewaehlt:
;; 90 Grad : [STR winkel] [STR seite] <- Eintrag fuer Eintrag wie bisher
;; 30 + Seite : [STR winkel] [STR seite] <- ebenfalls wie bisher
;; 30 + Achse : [STR winkel] [PT p2]
;; Die Modus-Wahl selbst wird BEWUSST NICHT journalisiert: sie ist aus der Art
;; des naechsten Eintrags eindeutig rekonstruierbar (PT = Achse, STR = Seite).
;; Dadurch bleiben alle aelteren Ketten ohne Sonderbehandlung editierbar.
;; JOURNAL - Eintragsfolge an dieser Stelle:
;; neu : [STR winkel] [STR seite] [PT p2] (ohne Achse: [NIL])
;; 30 + Achse alt : [STR winkel] [PT p2] (2026-10-14..10-20)
;; ganz alt : [STR winkel] [STR seite]
;; Welche Fragen gestellt werden, wird BEWUSST NICHT journalisiert: es ist aus
;; der Art des jeweils naechsten Eintrags eindeutig rekonstruierbar (PT direkt
;; nach dem Winkel = Alt-Achse ohne Seite; PT/NIL nach der Seite = neue Achse;
;; alles andere = Altbestand ohne Achse). Dadurch bleiben alle aelteren Ketten
;; ohne Sonderbehandlung editierbar. Ein neues Glied beginnt immer mit seiner
;; Menue-Antwort (STR) bzw. - im erzwungenen QC-Modus - mit einer Richtung
;; (REAL); ein PT/NIL an dieser Stelle kann daher nur die Achse sein.
;;
;; Setzt *vfl-as-basis-hz*/-p1 (nil, wenn keine Achse gewaehlt wurde).
;; Rueckgabe: Seite. Im Achsen-Modus vorlaeufig "links" - die endgueltige Seite
;; folgt aus der Lage des naechsten Kettenpunkts und wird in
;; vfl-kettenanfang-baustein gesetzt (Laengsverbrauch ist seitengleich, nur das
;; Vorzeichen des Querversatzes dreht).
;; Setzt *vfl-as-basis-hz*/-p1 (nil, wenn keine Achse gewaehlt wurde) und
;; *vfl-as-seite-explizit* (T, wenn die Seite wirklich gefragt wurde - nur
;; dann darf vfl-kettenanfang-baustein sie NICHT mehr aus der Lage des
;; naechsten Punkts ueberschreiben).
;; Rueckgabe: Seite. Beim Alt-Achsen-Journal vorlaeufig "links" - die
;; endgueltige Seite folgt dort aus der Lage des naechsten Kettenpunkts
;; (Laengsverbrauch ist seitengleich, nur das Vorzeichen des Querversatzes
;; dreht).
(defun vfl-frage-as-winkel-seite-oder-achse (seiten-headerkey startpunkt
/ antwort as-seite modus p2)
(setq *vfl-as-basis-hz* nil *vfl-as-basis-p1* nil)
/ antwort as-seite p2 mit-seite mit-achse)
(setq *vfl-as-basis-hz* nil *vfl-as-basis-p1* nil *vfl-as-seite-explizit* nil)
(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")))))
;; Beim 30-Grad-Element wird die Seite NICHT gefragt (im Gruppendialog
;; ausgegraut) - stattdessen direkt die Zufuehrungsachse ueber einen zweiten
;; Punkt. Es gibt also keine Modus-Frage: 30 Grad = Achse, 90 Grad = Seite.
;;
;; AUSNAHME Replay: aeltere 30-Grad-Ketten wurden noch MIT Seitenfrage
;; gebaut, ihr Journal hat an dieser Stelle ein "STR" statt eines "PT".
;; Steht dort ein STR, ist es ein Altbestand und es wird wie frueher die
;; Seite gelesen - sonst liefe die Queue aus dem Takt.
(setq modus (if (= *vfl-as-winkel* "30") "1" "2"))
(if (and (= modus "1") *vfl-replay-queue* (/= (vfl-replay-peek-art) "PT"))
(setq modus "2"))
(if (= modus "1")
(progn
;; Der AS-Gruppendialog legt beim 30-Grad-Element bewusst KEINE Seite in
;; die Pending-Queue (siehe vflw-gruppe-as-impl) - die Queue ist hier
;; also leer und der folgende vfl-in-point fragt wirklich nach dem Punkt.
;; Zweiter Punkt gibt NUR die Richtung der Zufuehrung; der Startpunkt
;; bleibt der Sitz des AS-Elements.
(setq p2 (vfl-in-point startpunkt (ssg-text "vfl-prompt-as-achse-p2")))
(if (or (null p2)
(and (< (abs (- (car p2) (car startpunkt))) 1e-9)
(< (abs (- (cadr p2) (cadr startpunkt))) 1e-9)))
(progn
;; Keine brauchbare Richtung -> auf die Seitenfrage zurueckfallen.
;; (Der PT-Eintrag steht dann trotzdem im Journal und wird beim
;; Replay wieder als "Achse" gelesen - deshalb hier KEIN stiller
;; Rueckfall, sondern Abbruch.)
(princ (ssg-text "vfl-basislinie-punkte-gleich"))
(exit))
(progn
(setq *vfl-as-basis-p1* startpunkt)
(setq *vfl-as-basis-hz* (vfl-planar-hz startpunkt p2))
(princ (ssg-textf "vfl-basislinie-info" (list (rtos *vfl-as-basis-hz* 2 1))))
(setq as-seite "links"))) ; vorlaeufig, s.o.
)
;; --- Seite (links/rechts) ---
;; Uebersprungen NUR beim Replay eines Alt-Achsen-Journals (30 Grad,
;; 2026-10-14..10-20): dort steht direkt hinter dem Winkel der Achsenpunkt.
(setq mit-seite (not (and *vfl-replay-queue* (= (vfl-replay-peek-art) "PT"))))
(if mit-seite
(progn
(princ (ssg-text seiten-headerkey))
(princ (ssg-text "gf-seite-links"))
@@ -6768,7 +7075,34 @@
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1
seiten-headerkey))
(setq as-seite (if (= antwort "2") "rechts" "links"))))
(setq as-seite (if (= antwort "2") "rechts" "links"))
(setq *vfl-as-seite-explizit* t))
(setq as-seite "links")) ; vorlaeufig, s.o.
;; --- Zufuehrungsachse (2. Punkt, optional) ---
;; Beim Replay nur lesen, wenn an dieser Stelle wirklich ein Achsen-Eintrag
;; steht (PT = Punkt, NIL = bewusst ohne Achse gebaut) - sonst ist es ein
;; Altbestand ohne diese Frage und der naechste Eintrag gehoert schon zum
;; ersten Glied.
(setq mit-achse
(if *vfl-replay-queue*
(if (member (vfl-replay-peek-art) '("PT" "NIL")) t nil)
t))
(if mit-achse
(progn
(setq p2 (vfl-in-point startpunkt (ssg-text "vfl-prompt-as-achse-p2")))
(if (and p2
(or (> (abs (- (car p2) (car startpunkt))) 1e-9)
(> (abs (- (cadr p2) (cadr startpunkt))) 1e-9)))
(progn
(setq *vfl-as-basis-p1* startpunkt)
(setq *vfl-as-basis-hz* (vfl-planar-hz startpunkt p2))
(princ (ssg-textf "vfl-basislinie-info" (list (rtos *vfl-as-basis-hz* 2 1)))))
;; Kein (brauchbarer) Punkt -> keine Achse. KEIN Abbruch mehr: die
;; Achse ist seit 2026-10-21 optional, die Kette laeuft dann wie
;; frueher ueber die Richtung des naechsten Klicks. Der Journal-
;; Eintrag (PT bzw. NIL) steht trotzdem und wird beim Replay wieder
;; genauso gelesen.
(princ (ssg-text "vfl-basislinie-ohne")))))
(vf-set-as-masse *vfl-as-winkel* as-seite)
as-seite)
@@ -7062,7 +7396,7 @@
startpunkt-familie gf-kettenende-antwort
vorab-antwort vorab-es-gewuenscht vorab-es-seite
save-eindx-vorab save-eindz-vorab erg-es vorab-ziel-punkt
rad3-sep sep-eindx-add sep-eindz-add)
rad3-sep sep-eindx-add sep-eindz-add gf-parallel-moeglich)
;; "Zurueck"/"Abbrechen"-Flag ganz am Anfang zuruecksetzen (auch schon vor
;; der ersten Praeambel-Frage) - sonst wuerde ein von einem VORHERIGEN Lauf
;; noch gesetztes Flag (z.B. nach "Abbrechen") den allerersten vfl-in-*-
@@ -7162,7 +7496,10 @@
(setq as-vorhanden nil))
(progn
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-as-setzen-frage")))
;; startpunkt = Zufuehrungsachse (2. Punkt) gehoert hier in DENSELBEN
;; Dialog wie Winkel/Seite (Nutzer-Vorgabe 2026-10-21); der Punkt ist
;; zugleich die Gummiband-Basis der Punktwahl.
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-as-setzen-frage" startpunkt)))
(princ (ssg-text "vfl-as-setzen-frage"))
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-as-nein"))
@@ -7175,10 +7512,13 @@
;; werden - sonst wuerde ein WINKEL_AS-Attribut den Wert der VORHERIGEN Kette
;; tragen, wenn diesmal kein AS gewaehlt wird.
(setq *vfl-as-winkel* nil *vfl-es-winkel* nil)
;; Winkel, dann Seite ODER (nur bei 30 Grad) eine Zufuehrungsachse - siehe
;; vfl-frage-as-winkel-seite-oder-achse. Im Achsen-Modus entfaellt die
;; Seitenfrage; die Seite folgt aus der Lage des naechsten Kettenpunkts und
;; wird in vfl-kettenanfang-baustein bestimmt.
;; Gilt genauso fuer die Achsen-Globals: ohne AS-Element bleibt sonst die
;; Zufuehrungsachse der VORHERIGEN Kette stehen.
(setq *vfl-as-basis-hz* nil *vfl-as-basis-p1* nil *vfl-as-seite-explizit* nil)
;; Winkel, dann Seite UND eine optionale Zufuehrungsachse (2. Punkt) - siehe
;; vfl-frage-as-winkel-seite-oder-achse. Beide zusammen legen AS-Lage und
;; Kettenrichtung fest; ohne Achse laeuft alles wie frueher ueber die
;; Richtung des naechsten Klicks.
(if as-vorhanden
(setq as-seite (vfl-frage-as-winkel-seite-oder-achse "vfl-aus-seite-header" startpunkt)))
;; ES-Seite wird erst am Kettenende abgefragt (siehe vfl-frage-es-seite),
@@ -7474,18 +7814,38 @@
(setq *vfl-forciere-qc-modus* nil)
)
(progn
(if (vfl-wizard-aktiv) (vl-catch-all-apply 'vflw-gruppe-gf-laenge-modus-impl nil))
;; "Parallel zum Kreisel" braucht AS-Element UND Zufuehrungsachse
;; als Bezug - sonst gibt es keinen Abstand, zu dem parallel
;; gebaut werden koennte.
(setq gf-parallel-moeglich
(and kettenanfang as-vorhanden *vfl-as-basis-hz*))
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-gf-laenge-modus-impl (list gf-parallel-moeglich)))
(princ (ssg-text "vfl-gf-laenge-modus-header"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-endpunkt"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-hoehe"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-steigend"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-qc"))
(if gf-parallel-moeglich (princ (ssg-text "vfl-gf-laenge-modus-opt-parallel")))
(setq gf-laenge-modus (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-gf-laenge-modus-opt-endpunkt")
(ssg-text "vfl-gf-laenge-modus-opt-hoehe")
(ssg-text "vfl-gf-laenge-modus-opt-steigend")
(ssg-text "vfl-gf-laenge-modus-opt-qc")) 1
(if gf-parallel-moeglich
(list (ssg-text "vfl-gf-laenge-modus-opt-endpunkt")
(ssg-text "vfl-gf-laenge-modus-opt-hoehe")
(ssg-text "vfl-gf-laenge-modus-opt-steigend")
(ssg-text "vfl-gf-laenge-modus-opt-qc")
(ssg-text "vfl-gf-laenge-modus-opt-parallel"))
(list (ssg-text "vfl-gf-laenge-modus-opt-endpunkt")
(ssg-text "vfl-gf-laenge-modus-opt-hoehe")
(ssg-text "vfl-gf-laenge-modus-opt-steigend")
(ssg-text "vfl-gf-laenge-modus-opt-qc"))) 1
"vfl-gf-laenge-modus-header"))
;; Menue-Antwort "5" (die neue 5. Option) auf einen SPRECHENDEN
;; internen Wert abbilden: "5" ist intern bereits durch die
;; erzwungene U-Form belegt (*vfl-forciere-qc-modus*, Linienzug-
;; Modus 5), die diese Frage ueberspringt und daher nie eine "5"
;; ins Journal schreibt. Journalisiert wird weiterhin die rohe
;; Menue-Antwort; die Abbildung passiert beim Replay identisch.
(if (= gf-laenge-modus "5") (setq gf-laenge-modus "parallel"))
)
)
;; Option 4 ("QC Linie mit GF"): eigenstaendiger, vollstaendig
@@ -7500,6 +7860,11 @@
(cond
((= gf-laenge-modus "4") (vfl-qc-linie-mit-gf))
((= gf-laenge-modus "5") (vfl-qc-linie-mit-gf-uform))
;; "Parallel zum Kreisel": ebenfalls eigenstaendig - baut AS + GF +
;; Bogen aus EINEM Abstandswert und stellt die Kettenende-Frage
;; selbst (sie kommt hier NACH dem Bau, nicht davor: erst nach dem
;; Bogen steht fest, wo die Kette weiterlaeuft).
((= gf-laenge-modus "parallel") (vfl-gf-parallel-kreisel))
(t
(progn
;; NEU: Kettenende/ES JETZT fragen - VOR der Laengen-/Gefaelle-
@@ -8250,7 +8615,7 @@
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-es-nein-zielpunkt"))
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-es-setzen-frage")))
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-es-setzen-frage" nil)))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-ja") (ssg-text "vfl-es-nein-zielpunkt")) 1
"vfl-es-setzen-frage"))
@@ -10236,7 +10601,7 @@
"vfl-prompt-hoehe-startpunkt" "gf-status-abgebrochen" "Starthoehe (Z, mm):"))
(setq start-hoehe (caddr startpunkt))
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-as-setzen-frage")))
(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"))
@@ -10257,7 +10622,7 @@
"vfl-m3-prompt-zielhoehe" "gf-status-abgebrochen" "Zielhoehe (Z, mm):"))
(setq end-hoehe (caddr endpunkt))
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-es-setzen-frage")))
(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-es-nein-zielpunkt"))
+19 -2
View File
@@ -274,7 +274,18 @@
((and (= (type v) 'STR) (= (strcase v) "AUTO")) (vsp-raus "NIL" nil))
(T (vsp-zahl "REAL" v "eingang_hoehe"))))
(defun vsp-praeambel (kopf / punkt)
;; Praeambel-Tokens: PT(Startpunkt) REAL(Starthoehe) STR(AS ja/nein)
;; [STR(Winkel) STR(Seite) (PT|NIL)(Zufuehrungsachse)].
;;
;; Das Achsen-Feld "as_achse" ist OPTIONAL (Gegenstueck zu
;; vfl-frage-as-winkel-seite-oder-achse und zu praeambel() in
;; lib/vf_linienzug_grammatik.py):
;; [x, y, z] -> PT (Zufuehrungsachse ueber einen zweiten Punkt)
;; "ohne" -> NIL (bewusst ohne Achse gebaut)
;; fehlt -> gar kein Token = Journalformat VOR 2026-10-21. Das replayt
;; unveraendert; die Kettenrichtung kommt dann wie frueher aus
;; dem naechsten Klick.
(defun vsp-praeambel (kopf / punkt achse)
(setq *vsp-wo* "start")
(setq punkt (ssg-val kopf "start_punkt"))
(if (and punkt (listp punkt) (>= (length punkt) 3))
@@ -287,7 +298,13 @@
(if (vsp-ja-p (ssg-val kopf "as"))
(progn
(vsp-wert-str (ssg-val kopf "as_winkel") "as_winkel")
(vsp-code-raus *vsp-tab-seite* (ssg-val kopf "as_seite") "as_seite"))))
(vsp-code-raus *vsp-tab-seite* (ssg-val kopf "as_seite") "as_seite")
(setq achse (ssg-val kopf "as_achse"))
(cond
((and achse (listp achse) (>= (length achse) 3))
(vsp-raus "PT" (mapcar 'float achse)))
((equal achse "ohne") (vsp-raus "NIL" nil))
))))
;; naechste: Felder der NAECHSTEN Top-Level-Sektion (oder nil) - wird nur fuer
;; "Linie-GF" gebraucht (siehe dort). *vsp-es-verbraucht* (Ausgabe-Flag, vom