feat: Linienzug Modus 1 kann rueckwaerts bauen (ES als exakter Anker)
Bisher war der geklickte Punkt immer der KettenANFANG. Oft steht aber das
andere Ende fest - typisch der Anschluss an einen Kreisel. Modus 1 fragt
deshalb jetzt ganz am Anfang nach der Bau-Richtung:
vorwaerts = geklickter Punkt ist der Kettenanfang (alles wie bisher)
rueckwaerts= er ist das Kettenende, genauer das KS_AUS des ES-Elements
ABLAUF RUECKWAERTS
ES setzen? + Winkel + Seite + Fahrtrichtung (2. Punkt) in EINEM Dialog, dann
"Neue Linie: GF" und die Laengenfrage mit drei Optionen:
1 Kettenanfang klicken -> + Neigungswinkel, Anfangshoehe wird gerechnet
2 Anfangshoehe vorgeben -> + Neigungswinkel, Laenge wird gerechnet
3 Parallel zum Kreisel -> nur Abstand (nur mit ES waehlbar)
Danach ist die Kette fertig. Die Spiegelfrage "Ist das der Kettenanfang?"
mit optionalem AS und Weiterbauen ist bewusst NICHT enthalten (Nutzer-
Entscheidung: vorerst kleiner Umfang).
ANKER
Das ES wird mit insert-block-mixed-to-ks und xy-ref/z-ref="KS_AUS" EXAKT auf
den geklickten Punkt gesetzt; alles Weitere wird daraus zurueckgerechnet
(vfl-rueck-punkt) und dann normal vorwaerts platziert. Gleiches Ergebnis,
aber mit den erprobten Einfuege-Primitiven. Eine Rest-Ungenauigkeit landet
am Kettenanfang statt am Kreisel-Anschluss.
Option 3 spiegelt die Vorwaerts-Variante: Bogenseite = Gegenseite der
ES-Seite, Bogenwinkel hebt den gemessenen ES-Schwenk auf. Dafuer werden die
bereits getesteten Kerne vfl-par-bogen-waehlen/vfl-par-gf-laenge
wiederverwendet - die Zuordnung (ES 30 links -> 30-Grad-Bogen rechts usw.)
faellt daraus heraus, statt gepflegt zu werden.
ZWEI FEHLER, DIE DER ERSTE BRICSCAD-TEST GEFUNDEN HAT
1. vflw-gruppe-as-impl legt VIER Werte in die Pending-Queue (ja/nein,
Winkel, Seite, Punkt). Die Ja/Nein-Antwort wurde nicht konsumiert, alles
rutschte eine Position weiter - der Winkel bekam die "1" der Ja/Nein-
Frage, Blockname "ES_Element_1_links". Die Frage wird jetzt gestellt und
ausgewertet; "Nein" ist dabei sinnvoll belegt (Kette endet direkt am
geklickten Punkt, Richtung ueber vfl-frage-fahrtrichtung-p2).
2. insert-block-ks-to-ks richtet immer den EINGANG eines Blocks aus.
Rueckwaerts sitzt der Bogen VOR dem GF-Stueck, sein AUSGANG muss also auf
der GF-Achse liegen - der Eingang ist um den Bogenschwenk vorzudrehen
(hz-bogen = hz-ein - bogen-turn). Ohne das zeigte der Ausgang um genau den
Bogenwinkel daneben, KS_AUS und KS_EIN trafen sich nicht. Gilt fuer den
Probe-Bau UND das echte Einfuegen - waere nur eins gedreht, saesse der
Bogen richtig orientiert an der falschen Stelle.
BEDIENUNG
- Kurze Entscheidungen im Rueckwaerts-Ablauf als Radio-Buttons statt
Auswahlliste (vfl-menu-radio).
- Beim Klick auf den Kettenanfang haengt der Cursor auf der Kettenachse
(neuer Wrapper vfl-in-point-achse ueber das vorhandene
vfl-getpoint-achse): ab dort kann nur noch entlang dieser Achse gebaut
werden, ein frei schwenkendes Gummiband gaukelte eine Freiheit vor.
- Zahlen-Dialoge belegen ihr Feld jetzt mit der Vorgabe vor. Die Prompts
tragen sie ohnehin in Klammern ("[3]", "<2154.0>"), der Dialog hat sie nur
ignoriert - bewusst generisch geloest (vflw-vorgabe-aus-prompt), die
Luecke hatten alle Zahlen-Dialoge des Assistenten.
- vflw_as beschriftet Seite und Achse neutral, weil derselbe Dialog jetzt
AS (vorwaerts) und ES (rueckwaerts) bedient; die Punkt-Anzeige heisst beim
ES "Endpunkt".
ABWAERTSKOMPATIBILITAET
Die Richtungsfrage steht VOR der AS-Frage und journalisiert ihren WERT
("vor"/"rueck") statt eines Index - ein Index "1"/"2" waere von der
nachfolgenden AS-Ja/Nein-Antwort nicht zu unterscheiden. Fehlt der Eintrag,
ist es ein Alt-Journal und es wird vorwaerts gebaut. Dafuer der neue Wrapper
vfl-menu-strwert.
TESTS
12 neue file- und zeichnungsfreie Unit-Tests: vfl-rueck-punkt (Rundlauf
vorwaerts/rueckwaerts) und vflw-vorgabe-aus-prompt (gegen die echten
Prompt-Texte, inkl. der Stolperstellen "0 < Winkel <= 3" ohne schliessendes
">" und dem Nachsatz in runden Klammern). TEST_UNIT konnte noch nicht
laufen - BricsCAD war die ganze Sitzung geoeffnet. Der Rueckwaerts-Bau
selbst ist vom Nutzer in BricsCAD geprueft und bestaetigt.
SSG_LIB.cui ist das von BricsCAD neu erzeugte Kompilat zur Menue-Aenderung
des vorigen Commits.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+451
-3
@@ -683,13 +683,47 @@
|
||||
;; Generischer Zahlen-Dialog (ersetzt getreal). Rueckgabe: reale Zahl oder
|
||||
;; nil (Abbruch/leer - wie getreal bei blossem Enter, von den Aufrufern
|
||||
;; bereits ueberall mit einem Default abgefangen).
|
||||
(defun vflw-zahl-impl (prompt / dat dcl-pfad dlg-wert ergebnis)
|
||||
;; Vorgabewert aus einem Prompt ziehen, damit der Zahlen-Dialog sein Eingabe-
|
||||
;; feld damit vorbelegen kann. Die Konsolen-Prompts tragen ihn seit jeher in
|
||||
;; Klammern ("[3]", "<2154.0>"); im Dialog blieb das Feld trotzdem leer und
|
||||
;; der Wert war nur in der Kopfzeile ablesbar (Nutzer-Wunsch 2026-10-22:
|
||||
;; gleich eintragen). Bewusst GENERISCH statt an einer Aufrufstelle: dieselbe
|
||||
;; Luecke hatten alle Zahlen-Dialoge des Assistenten.
|
||||
;;
|
||||
;; Gesucht wird die LETZTE Klammergruppe, deren Inhalt eine Zahl ist - damit
|
||||
;; stoeren Klammern im uebrigen Text nicht. Beispiel:
|
||||
;; "Neigungswinkel (Grad, 0 < Winkel <= 3) [3]: " -> "3"
|
||||
;; Das "<" in "0 < Winkel" hat kein schliessendes ">" und faellt weg, "(Grad,
|
||||
;; ...)" ist keine Zahl. Rueckgabe: Zahl als String oder nil.
|
||||
(defun vflw-vorgabe-aus-prompt (prompt / i n c zu inhalt ergebnis)
|
||||
(setq n (strlen prompt) i 1 ergebnis nil)
|
||||
(while (<= i n)
|
||||
(setq c (substr prompt i 1))
|
||||
(if (or (= c "[") (= c "<"))
|
||||
(progn
|
||||
;; vl-string-search zaehlt 0-basiert, substr 1-basiert: die 1-basierte
|
||||
;; Position i der oeffnenden Klammer ist als 0-basierter Startwert
|
||||
;; genau die Stelle DAHINTER.
|
||||
(setq zu (vl-string-search (if (= c "[") "]" ">") prompt i))
|
||||
(if zu
|
||||
(progn
|
||||
(setq inhalt (substr prompt (1+ i) (- zu i)))
|
||||
(if (distof inhalt 2) (setq ergebnis inhalt))))))
|
||||
(setq i (1+ i)))
|
||||
ergebnis)
|
||||
|
||||
(defun vflw-zahl-impl (prompt / dat dcl-pfad dlg-wert ergebnis vorgabe)
|
||||
(setq dcl-pfad (vflw-dcl-pfad))
|
||||
(setq dat (load_dialog dcl-pfad))
|
||||
(if (not (new_dialog "vflw_zahl" dat))
|
||||
(progn (if (and dat (>= dat 0)) (unload_dialog dat)) nil)
|
||||
(progn
|
||||
(set_tile "kopf" (vflw-clean prompt))
|
||||
;; Vorgabe ins Feld schreiben - "Ok Weiter" ohne Aenderung liefert damit
|
||||
;; denselben Wert wie frueher das leere Feld (dort griff der Default
|
||||
;; beim Aufrufer).
|
||||
(setq vorgabe (vflw-vorgabe-aus-prompt prompt))
|
||||
(if vorgabe (set_tile "wert" vorgabe))
|
||||
(action_tile "accept" "(setq dlg-wert (get_tile \"wert\")) (done_dialog 1)")
|
||||
(action_tile "cancel" "(done_dialog 0)")
|
||||
(action_tile "back" "(done_dialog 2)")
|
||||
@@ -914,8 +948,11 @@
|
||||
;; meldet dann vfl-frage-as-winkel-seite-oder-achse auf der Konsole.
|
||||
(if mit-achse
|
||||
(progn
|
||||
;; Beim AS ist der Punkt der Kettenanfang, beim ES (Rueckwaerts-
|
||||
;; Bau) das Kettenende - die Beschriftung folgt der Kopfzeile.
|
||||
(set_tile "start_anzeige"
|
||||
(strcat "Startpunkt: " (vflw-g-punkt-str achse-basis)))
|
||||
(strcat (if (= kopf-key "vfl-es-setzen-frage") "Endpunkt: " "Startpunkt: ")
|
||||
(vflw-g-punkt-str achse-basis)))
|
||||
(if gpunkt
|
||||
(set_tile "pt_anzeige"
|
||||
(strcat (vflw-g-punkt-str gpunkt)
|
||||
@@ -2032,6 +2069,16 @@
|
||||
(defun vfl-in-point (base prompt)
|
||||
(vfl-in-value "PT" (function (lambda () (vfl-getpoint base prompt)))))
|
||||
|
||||
;; Wie vfl-in-point, aber der Cursor haengt auf einer ACHSE (SNAPANG+ORTHO,
|
||||
;; siehe vfl-getpoint-achse) statt frei in der Ebene. Fuer Punkte, deren
|
||||
;; Richtung bereits feststeht und bei denen der Klick nur noch die LAENGE
|
||||
;; beisteuert - dort fuehrt ein frei schwenkendes Gummiband sonst in die Irre
|
||||
;; (Nutzer-Befund 2026-10-22 beim Rueckwaerts-Bau: der Kettenanfang kann nur
|
||||
;; auf der Achse des Separator-KS_EIN liegen, die Gummiband-Linie zeigte aber
|
||||
;; schraeg daneben). Journal-Format unveraendert: ein "PT"-Eintrag.
|
||||
(defun vfl-in-point-achse (base prompt hz)
|
||||
(vfl-in-value "PT" (function (lambda () (vfl-getpoint-achse base prompt hz)))))
|
||||
|
||||
;; Objektauswahl (Modus 2: Pfad-Objekte). Wie die anderen Wrapper repliziert
|
||||
;; bzw. journalisiert - aber KEIN Wizard-Dialog (Objektwahl braucht wie
|
||||
;; Punktwahl die Zeichnung, kein DCL-Fenster wuerde hier helfen). Beim Replay
|
||||
@@ -2147,6 +2194,33 @@
|
||||
(setq *vflw-menu-radio* T)
|
||||
(vfl-in-string prompt))
|
||||
|
||||
;; Wie vfl-menu-radio, journalisiert aber den WERT der Auswahl statt des
|
||||
;; Auswahl-Index ("vor"/"rueck" statt "1"/"2"). Gebraucht, wo eine NEUE Frage
|
||||
;; VOR eine bestehende gesetzt wird: mit einem Index waere beim Replay nicht
|
||||
;; mehr unterscheidbar, ob der naechste Eintrag zur neuen oder zur alten Frage
|
||||
;; gehoert - die Folgefrage benutzt ja dieselben "1"/"2". Mit einem eigenen
|
||||
;; Wertevorrat sind die Mengen disjunkt und ein Blick per
|
||||
;; vfl-replay-peek-wert genuegt. Entspricht der Konvention "Werte
|
||||
;; selbstbeschreibend fuehren, nicht als positionsabhaengige Indizes"
|
||||
;; (CLAUDE.md) - dieselbe Begruendung wie bei vfl-menu-winkel.
|
||||
;; werte: Rueckgabewerte in der Reihenfolge der Labels.
|
||||
(defun vfl-menu-strwert (prompt option-labels werte default-idx frage-key /
|
||||
*vflw-menu-optionen* *vflw-menu-default* *vflw-menu-frage*
|
||||
*vflw-menu-radio*)
|
||||
(setq *vflw-menu-optionen* option-labels)
|
||||
(setq *vflw-menu-default* default-idx)
|
||||
(setq *vflw-menu-frage* frage-key)
|
||||
(setq *vflw-menu-radio* T)
|
||||
(vfl-in-value "STR"
|
||||
(function (lambda ( / i k)
|
||||
(setq i (if (and (vfl-wizard-mode-p) *vflw-menu-optionen*)
|
||||
(vflw-radio-wahl (ssg-text frage-key) *vflw-menu-optionen* *vflw-menu-default*)
|
||||
(getstring prompt)))
|
||||
(setq k (if (and i (/= i "")) (atoi i) *vflw-menu-default*))
|
||||
(if (and (>= k 1) (<= k (length werte)))
|
||||
(nth (1- k) werte)
|
||||
(nth (1- *vflw-menu-default*) werte))))))
|
||||
|
||||
(defun vfl-menu-int (prompt option-labels default-idx frage-key /
|
||||
*vflw-menu-optionen* *vflw-menu-default* *vflw-menu-frage*)
|
||||
(setq *vflw-menu-optionen* option-labels)
|
||||
@@ -5032,6 +5106,350 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; ==================================================================
|
||||
;; RUECKWAERTS BAUEN (Modus 1, Nutzer-Vorgabe 2026-10-22)
|
||||
;; ==================================================================
|
||||
;; Normalfall ist "vorwaerts": der geklickte Punkt ist der KettenANFANG, dort
|
||||
;; sitzt das AS-Element, und die Kette waechst von da weg - die Endhoehe ist
|
||||
;; das Ergebnis. Manchmal steht aber das ANDERE Ende fest (typisch: der
|
||||
;; Anschluss an einen Kreisel). Dann ist der geklickte Punkt das KS_AUS des
|
||||
;; ES-Elements, und gebaut wird von dort nach hinten.
|
||||
;;
|
||||
;; ANKER-ENTSCHEIDUNG (Nutzer, 2026-10-22): das ES wird ZUERST mit seinem
|
||||
;; KS_AUS exakt auf den geklickten Punkt gesetzt; alles Weitere wird daraus
|
||||
;; rueckwaerts bestimmt. Eine Rest-Ungenauigkeit landet damit am
|
||||
;; Kettenanfang, wo sie nicht stoert - genau spiegelbildlich zum
|
||||
;; Vorwaerts-Fall, wo das AS-KS_EIN der exakte Anker ist.
|
||||
;;
|
||||
;; Umfang zunaechst bewusst klein (Nutzer-Vorgabe): ES + EIN GF-Stueck
|
||||
;; (+ bei "Parallel zum Kreisel" ein Bogen davor), danach ist die Kette
|
||||
;; fertig. Die Spiegel-Frage "Ist das der Kettenanfang?" (mit optionalem
|
||||
;; AS-Element und Weiterbauen) ist bewusst NICHT eingebaut.
|
||||
;;
|
||||
;; Alle Bausteine sind GERADE bzw. haben einen messbaren Fussabdruck, darum
|
||||
;; wird nirgends "rueckwaerts eingefuegt": es wird der jeweilige
|
||||
;; START-Punkt ausgerechnet und dann ganz normal vorwaerts platziert. Das
|
||||
;; Ergebnis ist identisch, nutzt aber die erprobten Einfuege-Primitiven.
|
||||
|
||||
;; ES-Winkel + Seite + Abfuehrungsachse (2. Punkt) - Spiegelbild von
|
||||
;; vfl-frage-as-winkel-seite-oder-achse. Die Achse ist hier NICHT optional:
|
||||
;; ohne Richtung laesst sich das ES gar nicht platzieren (vorwaerts kommt die
|
||||
;; Richtung aus dem ersten Streckenklick, den es hier nicht gibt).
|
||||
;; Setzt *vfl-es-winkel*, die ES-Masse (vf-set-es-masse) und
|
||||
;; *vfl-es-basis-hz*. Rueckgabe: Seite ("links"/"rechts") oder nil, wenn
|
||||
;; keine brauchbare Achse gewaehlt wurde.
|
||||
;; Fahrtrichtung ueber einen zweiten Punkt. Rueckgabe: hz in Grad oder nil.
|
||||
;; Eigene kleine Schale, weil sie an ZWEI Stellen gebraucht wird: mit
|
||||
;; ES-Element (dort bestimmt sie zugleich dessen Lage) und ohne ES-Element
|
||||
;; (dann endet die Kette direkt am geklickten Punkt, die Richtung muss aber
|
||||
;; trotzdem bekannt sein - vorwaerts kaeme sie aus dem ersten Streckenklick,
|
||||
;; den es rueckwaerts nicht gibt).
|
||||
(defun vfl-frage-fahrtrichtung-p2 (basis / p2)
|
||||
(setq p2 (vfl-in-point basis (ssg-text "vfl-prompt-es-achse-p2")))
|
||||
(if (and p2
|
||||
(or (> (abs (- (car p2) (car basis))) 1e-9)
|
||||
(> (abs (- (cadr p2) (cadr basis))) 1e-9)))
|
||||
(progn
|
||||
(setq *vfl-es-basis-hz* (vfl-planar-hz basis p2))
|
||||
(princ (ssg-textf "vfl-es-basislinie-info" (list (rtos *vfl-es-basis-hz* 2 1))))
|
||||
*vfl-es-basis-hz*)
|
||||
(progn (princ (ssg-text "vfl-basislinie-punkte-gleich")) nil)))
|
||||
|
||||
(defun vfl-frage-es-winkel-seite-achse (startpunkt / antwort es-seite)
|
||||
(setq *vfl-es-basis-hz* nil)
|
||||
(princ (ssg-text "vf-winkel-ein-header"))
|
||||
(princ (ssg-text "vf-winkel-90"))
|
||||
(princ (ssg-text "vf-winkel-30"))
|
||||
(setq *vfl-es-winkel*
|
||||
(vfl-in-value "STR"
|
||||
(function (lambda () (vf-frage-element-winkel "vf-winkel-ein-header")))))
|
||||
(princ (ssg-text "gf-seite-ein-header"))
|
||||
(princ (ssg-text "gf-seite-links"))
|
||||
(princ (ssg-text "gf-seite-rechts"))
|
||||
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
|
||||
(list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1
|
||||
"gf-seite-ein-header"))
|
||||
(setq es-seite (if (= antwort "2") "rechts" "links"))
|
||||
(vf-set-es-masse *vfl-es-winkel* es-seite)
|
||||
(if (vfl-frage-fahrtrichtung-p2 startpunkt) es-seite nil))
|
||||
|
||||
;; Punkt um eine Strecke ENTGEGEN der Fahrtrichtung zurueckversetzen.
|
||||
;; laenge-planar = horizontale (Grundriss-)Laenge, winkel = Gefaelle in Grad
|
||||
;; (positiv = fallend). Vorwaerts gilt
|
||||
;; P_next = P + (L*cos(hz), L*sin(hz), -L*tan(winkel))
|
||||
;; - rueckwaerts also mit umgekehrtem Vorzeichen. Damit liegt der Startpunkt
|
||||
;; eines Bausteins fest, der an einem BEKANNTEN Endpunkt ankommen soll.
|
||||
(defun vfl-rueck-punkt (p hz laenge-planar winkel / rad radw)
|
||||
(setq rad (* (float hz) (/ pi 180.0)))
|
||||
(setq radw (* (float winkel) (/ pi 180.0)))
|
||||
(list (- (car p) (* laenge-planar (cos rad)))
|
||||
(- (cadr p) (* laenge-planar (sin rad)))
|
||||
(+ (caddr p) (* laenge-planar (/ (sin radw) (cos radw))))))
|
||||
|
||||
;; ES-Element so platzieren, dass sein KS_AUS EXAKT auf p-ende liegt und von
|
||||
;; dort in Richtung hz-achse weiterzeigt; davor den Separator, den das
|
||||
;; ES-Element im Vorwaerts-Bau immer mitbringt (vfl-insert-es-element).
|
||||
;;
|
||||
;; insert-block-mixed-to-ks dreht den Block IMMER so, dass sein KS_EIN auf den
|
||||
;; uebergebenen Rahmen passt - xy-ref/z-ref steuern nur die Verschiebung.
|
||||
;; Fuer "KS_AUS zeigt entlang hz-achse" muss der Rahmen also um den
|
||||
;; Grundriss-Schwenk des ES zurueckgedreht werden: hz-ein = hz-achse - es-turn
|
||||
;; (dieselbe Rechnung wie in vfl-insert-as-element, nur andersherum gebraucht).
|
||||
;; Rueckgabe: (frame-vor-dem-Separator hz-ein) - der Punkt, an dem das
|
||||
;; GF-Stueck ankommen muss.
|
||||
(defun vfl-rueck-es-setzen (es-w es-s p-ende hz-achse /
|
||||
es-turn hz-ein w rad-w frame-ziel p-ein p-sep)
|
||||
(setq es-turn (vf-element-plan-turn (strcat "ES_Element_" es-w "_" es-s)))
|
||||
(setq hz-ein (- hz-achse es-turn))
|
||||
(setq w (fix *vfk-gefaelle-winkel*))
|
||||
;; Zielrahmen traegt die Standard-Fallneigung: so kommt die Kette am ES an
|
||||
;; (vgl. vfl-insert-es-element, das den ES ebenfalls auf den geneigten
|
||||
;; Rahmen setzt).
|
||||
(setq frame-ziel (make-frame-from-dir p-ende (hz-winkel->xu hz-ein w)))
|
||||
(insert-block-mixed-to-ks (strcat "ES_Element_" es-w "_" es-s)
|
||||
frame-ziel (caddr p-ende) "KS_AUS" "KS_AUS")
|
||||
;; KS_EIN des GERADE gesetzten ES messen - der Versatz KS_EIN->KS_AUS ist
|
||||
;; beim ES seitlich und laesst sich nicht aus dem Klickpunkt ableiten.
|
||||
(setq p-ein (vfl-block-ks-punkt (vlax-ename->vla-object (entlast)) "KS_EIN"))
|
||||
(if (null p-ein) (setq p-ein p-ende))
|
||||
;; Separator rueckwaerts davor: sein KS_AUS trifft das ES-KS_EIN.
|
||||
(setq p-sep (vfl-rueck-punkt p-ein hz-ein
|
||||
(* *vfk-separator-laenge* (cos (* (float w) (/ pi 180.0)))) w))
|
||||
(gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm" p-sep hz-ein w
|
||||
*vfk-separator-laenge* 0)
|
||||
(if (boundp '*vfl-acc-separator*)
|
||||
(setq *vfl-acc-separator* (1+ *vfl-acc-separator*)))
|
||||
(list p-sep hz-ein))
|
||||
|
||||
;; GF-Gerade rueckwaerts: sie soll an p-ziel ANKOMMEN, Grundrisslaenge
|
||||
;; laenge-planar, Neigung winkel. Rueckgabe: ihr Startpunkt (= neues
|
||||
;; Kettenende nach hinten).
|
||||
(defun vfl-rueck-gf (p-ziel hz laenge-planar winkel / p-start)
|
||||
(setq p-start (vfl-rueck-punkt p-ziel hz laenge-planar winkel))
|
||||
(vfl-insert-gf-segment p-start hz laenge-planar winkel)
|
||||
(vfl-acc-gf-seg (/ laenge-planar (cos (* (float winkel) (/ pi 180.0)))) winkel)
|
||||
p-start)
|
||||
|
||||
;; ------------------------------------------------------------------
|
||||
;; Rueckwaerts-Ablauf (Treiber). Nutzt - wie die uebrigen Bau-Zweige - die
|
||||
;; dynamisch sichtbaren Lokalvariablen des Aufrufers vf-linienzug-modus
|
||||
;; (startpunkt = geklicktes KETTENENDE, frame, p-aktuell, anzahl-gf,
|
||||
;; es-seite, fertig, letzter-typ).
|
||||
(defun vfl-rueckwaerts-bauen ( / es-w es-s es-vorhanden hz-achse erg p-ziel hz-ein
|
||||
modus gf-max-winkel winkel laenge deltaH
|
||||
p-pick p-start h-start rad
|
||||
bogen-seite bogen-winkel bogen-turn hz-bogen
|
||||
kandidaten tol w
|
||||
abstand perp-ziel perp-bogen nx ny rad-a bw
|
||||
acc-gfbogen-save acc-sep-save probe-checkpoint
|
||||
probe-frame basis-frame dxp dyp dzp p-bogen-start)
|
||||
;; --- ES-Element: "setzen?" + Winkel + Seite + Fahrtrichtung ---
|
||||
;; WICHTIG: vflw-gruppe-as-impl legt VIER Werte in die Pending-Queue
|
||||
;; (ja/nein, Winkel, Seite, Punkt). Die Ja/Nein-Antwort MUSS hier zuerst
|
||||
;; konsumiert werden - fehlt dieser Schritt, rutscht alles um eine Position
|
||||
;; und der Winkel bekommt die "1" der Ja/Nein-Frage (realer Fehler
|
||||
;; 2026-10-22: Blockname "ES_Element_1_links").
|
||||
(if (vfl-wizard-aktiv)
|
||||
(vl-catch-all-apply 'vflw-gruppe-as-impl (list "vfl-es-setzen-frage" startpunkt)))
|
||||
(princ (ssg-text "vfl-es-setzen-frage"))
|
||||
(princ (ssg-text "vfl-ja"))
|
||||
(princ (ssg-text "vfl-nein"))
|
||||
(setq es-vorhanden
|
||||
(/= "2" (vfl-menu-radio (ssg-text "prompt-wahl-1-2")
|
||||
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 1
|
||||
"vfl-es-setzen-frage")))
|
||||
(setq w (fix *vfk-gefaelle-winkel*))
|
||||
(if es-vorhanden
|
||||
(progn
|
||||
(setq es-s (vfl-frage-es-winkel-seite-achse startpunkt))
|
||||
(if (null es-s) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
|
||||
(setq es-seite es-s)
|
||||
(setq es-w (vfl-es-winkel))
|
||||
(setq hz-achse *vfl-es-basis-hz*)
|
||||
;; ES + Separator setzen - Anker: KS_AUS exakt auf dem geklickten Punkt.
|
||||
(setq erg (vfl-rueck-es-setzen es-w es-s startpunkt hz-achse))
|
||||
(setq p-ziel (nth 0 erg) hz-ein (nth 1 erg)))
|
||||
(progn
|
||||
;; Ohne ES endet die Kette direkt am geklickten Punkt; die Richtung
|
||||
;; muss trotzdem bekannt sein.
|
||||
(setq hz-achse (vfl-frage-fahrtrichtung-p2 startpunkt))
|
||||
(if (null hz-achse) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
|
||||
(setq p-ziel startpunkt hz-ein hz-achse)))
|
||||
(setq gf-max-winkel (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)))
|
||||
;; --- Nur EINE Element-Option (Nutzer-Vorgabe): Neue Linie GF ---
|
||||
;; vfl-menu-radio statt vfl-menu: Radio-Buttons statt Auswahlliste, wie bei
|
||||
;; den uebrigen kurzen Entscheidungen im Assistenten (Nutzer-Wunsch
|
||||
;; 2026-10-22). Journal-Semantik ist identisch - derselbe STR-Eintrag.
|
||||
(princ (ssg-text "vfl-naechstes-element-header"))
|
||||
(princ (ssg-text "vfl-rueck-menu-linie-gf"))
|
||||
(vfl-menu-radio (ssg-text "prompt-wahl-1-2")
|
||||
(list (ssg-text "vfl-rueck-menu-linie-gf")) 1 "vfl-naechstes-element-header")
|
||||
;; --- Laengen-Frage: drei Optionen (Nutzer-Vorgabe) ---
|
||||
;; "Parallel zum Kreisel" braucht das ES als Bezug (sein Grundriss-Schwenk
|
||||
;; bestimmt den Bogen) - ohne ES-Element entfaellt die Option.
|
||||
(princ (ssg-text "vfl-gf-laenge-modus-header"))
|
||||
(princ (ssg-text "vfl-rueck-laenge-opt-anfangspunkt"))
|
||||
(princ (ssg-text "vfl-rueck-laenge-opt-anfangshoehe"))
|
||||
(if es-vorhanden (princ (ssg-text "vfl-gf-laenge-modus-opt-parallel")))
|
||||
(setq modus (vfl-menu-radio (ssg-text "prompt-wahl-1-2")
|
||||
(if es-vorhanden
|
||||
(list (ssg-text "vfl-rueck-laenge-opt-anfangspunkt")
|
||||
(ssg-text "vfl-rueck-laenge-opt-anfangshoehe")
|
||||
(ssg-text "vfl-gf-laenge-modus-opt-parallel"))
|
||||
(list (ssg-text "vfl-rueck-laenge-opt-anfangspunkt")
|
||||
(ssg-text "vfl-rueck-laenge-opt-anfangshoehe"))) 1
|
||||
"vfl-gf-laenge-modus-header"))
|
||||
(cond
|
||||
;; --- Option 3: Parallel zum Kreisel (Spiegelbild von
|
||||
;; vfl-gf-parallel-kreisel) ---
|
||||
((= modus "3")
|
||||
;; Bogen zur GEGENSEITE der ES-Seite, Winkel so, dass er den
|
||||
;; Grundriss-Schwenk des ES aufhebt - identische Regel wie vorwaerts,
|
||||
;; nur mit es-turn statt as-turn. Ergibt mit den vorhandenen Bausteinen
|
||||
;; die Nutzer-Tabelle: ES 30 links -> 30-Grad-Bogen rechts usw.
|
||||
(setq bogen-seite (if (= es-s "rechts") "links" "rechts"))
|
||||
(setq kandidaten '())
|
||||
(foreach bw *vfk-gf-bogen-winkel*
|
||||
(setq kandidaten
|
||||
(cons (cons bw (vf-element-plan-turn (gf-bogen-blockname bw bogen-seite)))
|
||||
kandidaten)))
|
||||
(setq tol 2.0) ; wie in vfl-qc-linie-mit-gf
|
||||
(setq bogen-winkel
|
||||
(vfl-par-bogen-waehlen
|
||||
(vf-element-plan-turn (strcat "ES_Element_" es-w "_" es-s))
|
||||
(reverse kandidaten) tol))
|
||||
(if (null bogen-winkel)
|
||||
(vfl-meldung (ssg-textf "vfl-par-kein-bogen"
|
||||
(list (rtos (vf-element-plan-turn
|
||||
(strcat "ES_Element_" es-w "_" es-s)) 2 1)
|
||||
bogen-seite)))
|
||||
(progn
|
||||
(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
|
||||
;; Normale zur Abfuehrungsachse; alle Querversatz-Werte beziehen
|
||||
;; sich auf den geklickten Punkt (= ES-KS_AUS, perp = 0).
|
||||
(setq rad-a (* (float hz-achse) (/ pi 180.0)))
|
||||
(setq nx (- (sin rad-a)) ny (cos rad-a))
|
||||
(setq perp-ziel (+ (* (- (car p-ziel) (car startpunkt)) nx)
|
||||
(* (- (cadr p-ziel) (cadr startpunkt)) ny)))
|
||||
;; Bogen-Querversatz messen - Probe auf GENAU dem Rahmen, auf dem
|
||||
;; der echte Bogen spaeter sitzt (idealisierter Gefaelle-Rahmen,
|
||||
;; siehe gleichlautende Begruendung in vfl-gf-parallel-kreisel).
|
||||
;;
|
||||
;; RUECKWAERTS-BESONDERHEIT: der Bogen sitzt VOR dem GF-Stueck,
|
||||
;; sein AUSGANG muss also auf der GF-Richtung hz-ein liegen.
|
||||
;; insert-block-ks-to-ks richtet aber immer den EINGANG am
|
||||
;; uebergebenen Rahmen aus - der Eingang muss daher um den
|
||||
;; Bogenschwenk vorgedreht werden: hz-bogen = hz-ein - bogen-turn.
|
||||
;; Ohne diese Drehung zeigt der Bogen-Ausgang um genau den
|
||||
;; Bogenwinkel neben der GF-Achse und die KS treffen sich nicht
|
||||
;; (realer Fehler 2026-10-22). Vorwaerts (vfl-gf-parallel-kreisel)
|
||||
;; tritt das nicht auf: dort folgt der Bogen dem GF-Stueck, sein
|
||||
;; Eingang liegt also ohnehin schon auf der GF-Richtung.
|
||||
(setq bogen-turn (cdr (assoc bogen-winkel kandidaten)))
|
||||
(setq hz-bogen (- hz-ein bogen-turn))
|
||||
(setq acc-gfbogen-save *vfl-acc-gfbogen* acc-sep-save *vfl-acc-separator*)
|
||||
(setq probe-checkpoint (ssg-lastent-ohne-attribute))
|
||||
(setq basis-frame (make-frame-from-dir p-ziel (hz-winkel->xu hz-bogen w)))
|
||||
(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))))
|
||||
(setq dzp (- (caddr (car probe-frame)) (caddr (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)))
|
||||
;; Querversatz des GF-Stuecks je Laengeneinheit: es laeuft auf
|
||||
;; hz-ein, die Achse auf hz-achse - die Differenz ist genau der
|
||||
;; ES-Schwenk. Rueckwaerts gezaehlt kehrt sich das Vorzeichen um.
|
||||
(setq laenge (vfl-par-gf-laenge abstand perp-ziel (- perp-bogen)
|
||||
(sin (* (- hz-achse hz-ein) (/ pi 180.0)))))
|
||||
(cond
|
||||
((null laenge) (vfl-meldung (ssg-text "vfl-par-kein-versatz")))
|
||||
((< laenge 1.0)
|
||||
(vfl-meldung (ssg-textf "vfl-par-zu-klein"
|
||||
(list (rtos (abs abstand) 2 1)
|
||||
(rtos (abs (- perp-ziel perp-bogen)) 2 1)))))
|
||||
(t
|
||||
(setq p-start (vfl-rueck-gf p-ziel hz-ein laenge w))
|
||||
(setq anzahl-gf (1+ anzahl-gf))
|
||||
;; Bogen rueckwaerts: sein KS_AUS muss den GF-Start treffen -
|
||||
;; also um den GEMESSENEN Versatz (inkl. Hoehe) zurueckgesetzt.
|
||||
(setq p-bogen-start (list (- (car p-start) dxp)
|
||||
(- (cadr p-start) dyp)
|
||||
(- (caddr p-start) dzp)))
|
||||
;; Gleiche Eingangsdrehung wie bei der Probe (s.o.) - nur so
|
||||
;; passt der gemessene Versatz und der Ausgang landet auf der
|
||||
;; GF-Achse.
|
||||
(setq basis-frame (make-frame-from-dir p-bogen-start (hz-winkel->xu hz-bogen w)))
|
||||
(setq frame (vfl-insert-gf-bogen-block basis-frame bogen-winkel bogen-seite))
|
||||
(setq p-aktuell p-bogen-start)
|
||||
(princ (ssg-textf "vfl-rueck-erfolg-parallel"
|
||||
(list (rtos laenge 2 1) (itoa bogen-winkel) bogen-seite)))
|
||||
(setq fertig t))))))))
|
||||
;; --- Option 2: Anfangshoehe vorgeben, Laenge ergibt sich ---
|
||||
((= modus "2")
|
||||
(setq h-start (vfl-in-real (ssg-textf "vfl-rueck-prompt-anfangshoehe"
|
||||
(list (rtos (caddr p-ziel) 2 1)))))
|
||||
(if (null h-start) (setq h-start (caddr p-ziel)))
|
||||
(setq deltaH (- h-start (caddr p-ziel)))
|
||||
(setq winkel (vfl-in-real (ssg-textf "vfl-prompt-neigungswinkel-laenge-berechnet"
|
||||
(list (rtos gf-max-winkel 2 1)))))
|
||||
(if (null winkel) (setq winkel gf-max-winkel))
|
||||
(cond
|
||||
((<= deltaH 0.0) (vfl-meldung (ssg-text "vfl-rueck-alert-hoehe-zu-klein")))
|
||||
((or (<= winkel 0.0) (> winkel gf-max-winkel))
|
||||
(vfl-meldung (ssg-textf "vfl-alert-winkel-ungueltig"
|
||||
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1)))))
|
||||
(t
|
||||
(setq laenge (/ deltaH (/ (sin (* winkel (/ pi 180.0)))
|
||||
(cos (* winkel (/ pi 180.0))))))
|
||||
(if (< laenge 1.0)
|
||||
(princ (ssg-text "vfl-fehler-linie-zu-kurz"))
|
||||
(progn
|
||||
(setq p-start (vfl-rueck-gf p-ziel hz-ein laenge winkel))
|
||||
(setq anzahl-gf (1+ anzahl-gf))
|
||||
(setq frame (make-frame-from-dir p-start (hz-winkel->xu hz-ein winkel)))
|
||||
(setq p-aktuell p-start)
|
||||
(princ (ssg-textf "vfl-rueck-erfolg-hoehe"
|
||||
(list (rtos laenge 2 1) (rtos (caddr p-start) 2 1))))
|
||||
(setq fertig t))))))
|
||||
;; --- Option 1: Kettenanfang klicken, Anfangshoehe ergibt sich ---
|
||||
(t
|
||||
;; Cursor auf die Kettenachse zwingen: ab hier kann nur noch ENTLANG
|
||||
;; hz-ein gebaut werden (der Separator gibt die Richtung vor), ein frei
|
||||
;; schwenkendes Gummiband wuerde eine Freiheit vorgaukeln, die es nicht
|
||||
;; gibt. ORTHO laesst beide Richtungen der Achse zu - gemessen wird
|
||||
;; ohnehin die Projektion.
|
||||
(setq p-pick (vfl-in-point-achse p-ziel (ssg-text "vfl-rueck-prompt-anfangspunkt") hz-ein))
|
||||
(if (null p-pick)
|
||||
(progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
|
||||
;; Der Klick gibt nur die LAENGE her - die Richtung steht ueber die
|
||||
;; Abfuehrungsachse schon fest. Darum auf hz-ein zurueckprojizieren
|
||||
;; (entgegen der Fahrtrichtung gemessen).
|
||||
(setq rad (* (float hz-ein) (/ pi 180.0)))
|
||||
(setq laenge (+ (* (- (car p-ziel) (car p-pick)) (cos rad))
|
||||
(* (- (cadr p-ziel) (cadr p-pick)) (sin rad))))
|
||||
(setq winkel (vfl-in-real (ssg-textf "vfl-prompt-neigungswinkel"
|
||||
(list (rtos gf-max-winkel 2 1)))))
|
||||
(if (null winkel) (setq winkel gf-max-winkel))
|
||||
(cond
|
||||
((< laenge 1.0) (princ (ssg-text "vfl-fehler-linie-zu-kurz")))
|
||||
((or (<= winkel 0.0) (> winkel gf-max-winkel))
|
||||
(vfl-meldung (ssg-textf "vfl-alert-winkel-ungueltig"
|
||||
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1)))))
|
||||
(t
|
||||
(setq p-start (vfl-rueck-gf p-ziel hz-ein laenge winkel))
|
||||
(setq anzahl-gf (1+ anzahl-gf))
|
||||
(setq frame (make-frame-from-dir p-start (hz-winkel->xu hz-ein winkel)))
|
||||
(setq p-aktuell p-start)
|
||||
(princ (ssg-textf "vfl-rueck-erfolg-punkt"
|
||||
(list (rtos laenge 2 1) (rtos (caddr p-start) 2 1))))
|
||||
(setq fertig t)))))
|
||||
(setq letzter-typ "GF")
|
||||
(princ))
|
||||
|
||||
;; ------------------------------------------------------------------
|
||||
;; "QC Linie mit GF und U-Form" (Option 5 der gf-laenge-modus-Frage,
|
||||
;; Nutzer-Vorgabe 2026-09-13, nach mehreren Praezisierungsrunden - inkl. echter
|
||||
@@ -7397,7 +7815,8 @@
|
||||
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 gf-parallel-moeglich)
|
||||
rad3-sep sep-eindx-add sep-eindz-add gf-parallel-moeglich
|
||||
bau-richtung)
|
||||
;; "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-*-
|
||||
@@ -7489,6 +7908,30 @@
|
||||
;; faellt dann auf einen neutralen Text zurueck (kein falsches "GF"/"VF").
|
||||
;; Nur wenn GAR KEIN KS_AUS in Toleranz gefunden wurde, wird wie bisher
|
||||
;; gefragt.
|
||||
;; --- Bau-Richtung (Nutzer-Vorgabe 2026-10-22) ---
|
||||
;; "vor" = der geklickte Punkt ist der KettenANFANG (alles wie bisher).
|
||||
;; "rueck"= er ist das KettenENDE, genauer das KS_AUS des ES-Elements;
|
||||
;; gebaut wird von dort nach hinten (vfl-rueckwaerts-bauen).
|
||||
;; Journalisiert wird der WERT, nicht der Auswahl-Index (vfl-menu-strwert):
|
||||
;; die Frage steht VOR der AS-Ja/Nein-Frage, die ebenfalls "1"/"2" schreibt -
|
||||
;; mit einem Index waere ein Alt-Journal (ohne diese Frage) beim Replay nicht
|
||||
;; mehr davon zu unterscheiden. So genuegt ein Blick auf den Wert.
|
||||
(setq bau-richtung
|
||||
(if (and *vfl-replay-queue*
|
||||
(not (member (vfl-replay-peek-wert) '("vor" "rueck"))))
|
||||
"vor" ; Altbestand ohne diese Frage
|
||||
(progn
|
||||
(princ (ssg-text "vfl-richtung-header"))
|
||||
(princ (ssg-text "vfl-richtung-opt-vor"))
|
||||
(princ (ssg-text "vfl-richtung-opt-rueck"))
|
||||
(vfl-menu-strwert (ssg-text "prompt-wahl-1-2")
|
||||
(list (ssg-text "vfl-richtung-opt-vor") (ssg-text "vfl-richtung-opt-rueck"))
|
||||
(list "vor" "rueck") 1 "vfl-richtung-header"))))
|
||||
;; Startpunkt-Erkennung und AS-Praeambel gelten NUR vorwaerts - rueckwaerts
|
||||
;; gibt es kein AS-Element am geklickten Punkt, sondern ein ES.
|
||||
(if (= bau-richtung "rueck")
|
||||
(setq as-vorhanden nil startpunkt-familie nil)
|
||||
(progn
|
||||
(setq erg (vfl-ks-aus-hz-an-punkt startpunkt))
|
||||
(setq startpunkt-familie (if erg (vfl-kette-typ-familie (cdr erg)) nil))
|
||||
(if erg
|
||||
@@ -7524,11 +7967,16 @@
|
||||
(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),
|
||||
;; da das ES-Element erst dort eingefuegt wird.
|
||||
))
|
||||
|
||||
(setq p-aktuell startpunkt)
|
||||
(setq letzter-typ nil fertig nil frame nil)
|
||||
(setq anzahl-gf 0 anzahl-vf 0)
|
||||
(vfl-acc-reset)
|
||||
;; Rueckwaerts: ES + GF (+ Bogen) komplett hier bauen und fertig=t setzen -
|
||||
;; die Hauptschleife unten laeuft dann gar nicht erst an, der gemeinsame
|
||||
;; Abschluss (Block, Attribute, Journal-XDATA) danach aber ganz normal.
|
||||
(if (= bau-richtung "rueck") (vfl-rueckwaerts-bauen))
|
||||
;; *vfl-journal*-Reset steht jetzt GANZ AM ANFANG der Funktion (vor der
|
||||
;; Startpunkt/Hoehe/AS-Praeambel) - siehe Kommentar dort. Hier NICHT mehr
|
||||
;; noetig (und schaedlich: wuerde die bereits aufgezeichnete Praeambel
|
||||
|
||||
Reference in New Issue
Block a user