diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp index 285987b..525b97c 100644 --- a/Lisp/vf_linienzug.lsp +++ b/Lisp/vf_linienzug.lsp @@ -1055,20 +1055,24 @@ ;; faelschlich eine hier moegliche Punktwahl suggerieren wuerde). ;; hoehe-default: entweder eine Zahl (aktuelle Kettenhoehe, alte Vorbelegung - ;; die bei direkter Uebernahme immer als deltaH=0 abgelehnt wird), ODER eine -;; Liste (hoehe-ab hoehe-auf) mit zwei baubaren Vorschlaegen (siehe -;; vfl-vf-hoehe-vorschlaege/vfl-segment-hoehe-vorschlaege) - dann zeigt der -;; Kopftext beide Richtungen, vorbelegt wird die Ab-Variante (haeufigerer -;; Fall, siehe vfl-segment-entscheidung). Ist hoehe-ab nil (kein baubarer -;; Wert in der sondierten Zone gefunden), faellt die Vorbelegung auf die -;; unveraenderte Kettenhoehe zurueck (altes Verhalten) plus Warnhinweis. +;; Liste (hoehe-ab hoehe-auf hoehe-kette [hoehe-pick]) mit zwei baubaren +;; Vorschlaegen (siehe vfl-vf-hoehe-vorschlaege/vfl-segment-hoehe-vorschlaege) +;; - dann zeigt der Kopftext beide Richtungen. Vorbelegt wird, in dieser +;; Reihenfolge: hoehe-pick (die tatsaechliche Z-Hoehe des zuvor per Objektfang +;; gepickten Endpunkts, siehe *vfl-letzter-pick-z*/vfl-hoehe-endpunkt-abfragen +;; - der Nutzer hat damit meist schon die gewuenschte Zielhoehe angeklickt), +;; sonst die Ab-Variante (haeufigerer Fall, siehe vfl-segment-entscheidung). +;; Ist weder hoehe-pick noch hoehe-ab vorhanden, faellt die Vorbelegung auf +;; die unveraenderte Kettenhoehe zurueck (altes Verhalten) plus Warnhinweis. (defun vflw-gruppe-ziel-hoehe-impl (hoehe-default / dat dcl-pfad ergebnis dlg-wert - zwei-werte hoehe-ab hoehe-auf hoehe-kette vorbelegt) + zwei-werte hoehe-ab hoehe-auf hoehe-kette + hoehe-pick vorbelegt) (setq zwei-werte (listp hoehe-default)) (if zwei-werte (progn (setq hoehe-ab (nth 0 hoehe-default) hoehe-auf (nth 1 hoehe-default)) - (setq hoehe-kette (nth 2 hoehe-default)) - (setq vorbelegt (if hoehe-ab hoehe-ab hoehe-kette))) + (setq hoehe-kette (nth 2 hoehe-default) hoehe-pick (nth 3 hoehe-default)) + (setq vorbelegt (cond (hoehe-pick hoehe-pick) (hoehe-ab hoehe-ab) (t hoehe-kette)))) (setq vorbelegt hoehe-default)) (setq dat (load_dialog (vflw-dcl-pfad))) (if (not (new_dialog "vflw_zahl" dat)) @@ -1076,10 +1080,14 @@ (progn (set_tile "kopf" (vflw-clean - (if (and zwei-werte hoehe-ab hoehe-auf) - (ssg-textf "vfl-prompt-hoehe-endpunkt-2" - (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1))) - (ssg-textf "vfl-prompt-hoehe-endpunkt" (list (rtos vorbelegt 2 1)))))) + (cond + ((and zwei-werte hoehe-ab hoehe-auf hoehe-pick) + (ssg-textf "vfl-prompt-hoehe-endpunkt-3" + (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1) (rtos hoehe-pick 2 1)))) + ((and zwei-werte hoehe-ab hoehe-auf) + (ssg-textf "vfl-prompt-hoehe-endpunkt-2" + (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1)))) + (t (ssg-textf "vfl-prompt-hoehe-endpunkt" (list (rtos vorbelegt 2 1))))))) (set_tile "wert" (rtos vorbelegt 2 1)) (action_tile "accept" "(setq dlg-wert (get_tile \"wert\")) (done_dialog 1)") (action_tile "cancel" "(done_dialog 0)") @@ -2789,6 +2797,45 @@ (princ) ) +;; Gemeinsame Frage-Schale "Zielhoehe nach Endpunkt-Pick" - buendelt das an +;; mehreren Stellen (vfl-body-abschluss/Kettenende, VF-Fortsetzung Auf/Ab, +;; Horizontal-VF-Eingang, "Linie" GF-Familie) fast identisch wiederholte +;; Muster (Wizard-Dialog + Konsolen-Fallback + leere Eingabe -> Vorschlag). +;; *vfl-letzter-pick-z* (von vfl-in-abstand gesetzt, siehe dort) hat Vorrang +;; vor dem rein rechnerischen Ab-Vorschlag: wurde der Endpunkt per Objektfang +;; auf einem echten 3D-Punkt (z.B. einem vorhandenen Block) gepickt, ist genau +;; diese Hoehe die vom Nutzer gemeinte Zielhoehe - nicht der naechstgelegene +;; nur rechnerisch baubare Wert. Der Vorschlag bleibt im Eingabefeld/Dialog +;; jederzeit ueberschreibbar; der final gewaehlte Wert (hn) fliesst dabei +;; UNVERAENDERT in den Bau ein (siehe Aufrufer) - eine manuelle Korrektur wird +;; also korrekt weiterverrechnet, nicht verworfen. +;; schluessel-basis waehlt die passenden lang-Keys ("endpunkt"/"kettenende"/ +;; "linienendpunkt", siehe lang/de_DE.json) - vor dem Anlegen weiterer +;; Varianten immer erst pruefen, ob eine der drei bestehenden passt. +;; Rueckgabe: die gewaehlte Zielhoehe, NIE nil (leere Eingabe -> Vorschlag). +(defun vfl-hoehe-endpunkt-abfragen (hoehe-ab hoehe-auf ist-hoehe schluessel-basis / hoehe-fallback hn) + (setq hoehe-fallback + (cond (*vfl-letzter-pick-z* *vfl-letzter-pick-z*) (hoehe-ab hoehe-ab) (t ist-hoehe))) + (vfl-hoehe-hinweis-falls-noetig hoehe-ab hoehe-auf) + (if (vfl-wizard-aktiv) + (vl-catch-all-apply 'vflw-gruppe-ziel-hoehe-impl + (list (list hoehe-ab hoehe-auf ist-hoehe *vfl-letzter-pick-z*)))) + (setq hn + (vfl-in-real + (cond + ((and hoehe-ab hoehe-auf *vfl-letzter-pick-z*) + (ssg-textf (strcat "vfl-prompt-hoehe-" schluessel-basis "-3") + (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1) (rtos *vfl-letzter-pick-z* 2 1)))) + ((and hoehe-ab hoehe-auf) + (ssg-textf (strcat "vfl-prompt-hoehe-" schluessel-basis "-2") + (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1)))) + (t + (ssg-textf (strcat "vfl-prompt-hoehe-" schluessel-basis) + (list (rtos hoehe-fallback 2 1))))))) + (if (null hn) (setq hn hoehe-fallback)) + hn +) + ;; Konsolen-Echo nach Endpunkt-Pick + Zielhoehe-Eingabe: zeigt den absoluten ;; Endpunkt (X/Y, aus p-aktuell + deltaL entlang hz-neu zurueckgerechnet - nur ;; deltaL/hz werden journalisiert, siehe vfl-in-abstand) sowie die eingegebene @@ -3032,6 +3079,10 @@ ;; ohnehin immer die des Vorgaengers, ein Journal-Eintrag dafuer waere ;; redundant. Rueckgabe: (deltaL . hz) oder nil bei Abbruch/leerer Eingabe. (defun vfl-in-abstand (p-akt hz-vorgabe prompt / p2 rad ux uy deltaL hz-aktuell popped) + ;; *vfl-letzter-pick-z* IMMER zuerst zuruecksetzen (auch im Replay-Zweig, + ;; siehe unten) - ein Wert aus einem fruehreren Live-Pick derselben Sitzung + ;; darf niemals in einen Replay-/Headless-Nachbau durchsickern. + (setq *vfl-letzter-pick-z* nil) ;; Zentraler Abbruch-Trigger (siehe Kommentar in vfl-in-value): vfl-in-abstand ;; hat eine EIGENE Pending-/Replay-Logik und laeuft NICHT ueber vfl-in-value/ ;; -value-p - ohne diesen Check wuerde ein per Wizard-Dialog gesetztes @@ -3075,6 +3126,20 @@ ;; journalisiert. (setq popped (vflw-pending-pop)) (setq p2 (if popped (car popped) (vfl-getpoint p-akt prompt))) + ;; *vfl-letzter-pick-z*: die Z-Hoehe DIESES Picks, fuer eine unmittelbar + ;; nachfolgende Zielhoehe-Frage (siehe vfl-hoehe-endpunkt-abfragen) als + ;; Vorschlag verfuegbar machen - vorher bei JEDEM Endpunkt-Pick + ;; ersatzlos verworfen (nur X/Y gingen in deltaL ein), obwohl gerade ein + ;; Snap auf einen vorhandenen 3D-Block (Objektfang) die vom Nutzer + ;; gemeinte Zielhoehe meist schon exakt liefert. Nur uebernehmen, wenn + ;; die Z-Koordinate spuerbar von der aktuellen Konstruktionsebene + ;; (ELEVATION) abweicht - sonst ist es nur ein flacher Freihand-Klick + ;; ohne echte 3D-Information, kein echter Objektfang. Analog zu + ;; *vfl-stufe2-minl-vorschlag* ein transientes globales Vorschlags-Feld, + ;; nicht Teil des Rueckgabewerts (der bleibt unveraendert deltaL/hz); + ;; oben bereits auf nil zurueckgesetzt. + (if (and p2 (> (abs (- (caddr p2) (getvar "ELEVATION"))) 0.01)) + (setq *vfl-letzter-pick-z* (caddr p2))) (if (null p2) (setq deltaL nil) (progn @@ -3162,6 +3227,50 @@ (+ (* (- (car ziel-punkt) (car neu-basis)) (cos rad)) (* (- (cadr ziel-punkt) (cadr neu-basis)) (sin rad)))) +;; Fahrtrichtung + Vorlauflaenge FUER EIN DREHENDES AS-/ES-ELEMENT so +;; korrigieren, dass dessen KS_AUS exakt den absoluten Zielpunkt trifft - real +;; gemessener Bug: die bisherige Reservierung zog nur den Laengsanteil +;; (ein-dx/aus-dx, hz-unabhaengig IMMER exakt der Laengsanteil des rohen +;; Block-Fussabdrucks) vom Budget ab, nie den Queranteil (ein-dy/aus-dy), der +;; bei jedem Winkel <>0 Grad entsteht (0 bei 0 Grad, maximal bei 90 Grad) - +;; die Kette landete dadurch seitlich exakt um raw-dy versetzt neben dem Ziel +;; (BricsCAD-bestaetigt: Modus 1 UND Modus 2, ES_Element_90, ca. 638mm Versatz, +;; Hoehe und Laengsrichtung dabei beide exakt getroffen). +;; Herleitung: Sei D = ziel - start (2D), raw-dx/raw-dy der ROHE, ungedrehte +;; Fussabdruck (vf-element-masse, KS_AUS-KS_EIN bei Rotation 0). Bei +;; Einfuegewinkel hz gilt IMMER (Rotationsinvariante: die Projektion eines +;; gedrehten Vektors auf seine eigene Drehachse aendert sich durch die Drehung +;; nicht): Laengsanteil (entlang hz) = raw-dx, Queranteil (quer zu hz) = +;; raw-dy - unabhaengig vom konkreten hz. Mit L = Vorlauflaenge + raw-dx +;; (effektive Gesamt-Laengsstrecke inkl. Element-Vorschub) folgt daraus ein +;; rechtwinkliges Dreieck: |D|^2 = L^2 + raw-dy^2 (D, L-Anteil und raw-dy- +;; Anteil bilden ein orthonormales Paar), also L = sqrt(|D|^2 - raw-dy^2), und +;; die gesuchte Richtung hz' = Winkel von D MINUS dem Winkel, den raw-dy quer +;; dazu beitraegt (atan2(raw-dy, L)). +;; Rueckgabe: (hz-korrigiert L) - L ist die korrigierte RAW-Distanz zum +;; Zielpunkt entlang hz-korrigiert (Laengsanteil von D), NICHT bereits um +;; raw-dx reduziert - genau wie das bisherige deltaL (vfl-neue-linie-messen/ +;; vfl-projiziere-distanz) wird sie unveraendert an die bestehenden Solver +;; (vfl-body-zerlegung/vfl-vf-entscheidung -> berechne-alle-winkel) weiter- +;; gereicht, die raw-dx (ein-dx/aus-dx) SELBST intern abziehen (siehe deren +;; A-Formel) - hier zusaetzlich abzuziehen waere eine Doppel-Reservierung. +;; nil, wenn |D| < |raw-dy| (Ziel liegt naeher als der reine Seitenversatz - +;; geometrisch unerreichbar, unabhaengig von jeder Winkelwahl). +(defun vfl-richtung-mit-drehendem-fussabdruck (start ziel raw-dx raw-dy / + dx dy dsq l hz-ziel hz-korr) + (setq dx (- (car ziel) (car start)) dy (- (cadr ziel) (cadr start))) + (setq dsq (- (+ (* dx dx) (* dy dy)) (* raw-dy raw-dy))) + (if (< dsq 0.0) + nil + (progn + (setq l (sqrt dsq)) + (setq hz-ziel (* (atan dy dx) (/ 180.0 pi))) + (setq hz-korr (- hz-ziel (* (atan raw-dy l) (/ 180.0 pi)))) + (list hz-korr l) + ) + ) +) + ;; Rahmen am Ende eines vfs-*-Bausteins: die Bausteine (Entry/Koerper/Exit) ;; enden IMMER auf der 3-Grad-Basisneigung (siehe Prinzipien-Dok Abschnitt 6). (defun vfl-frame-3grad (punkt hz) @@ -4427,6 +4536,33 @@ "vfl-ist-kettenende-header")) antwort) +;; Frage-Schale "ES-Element setzen?" (+ falls ja: Winkel/Seite) - ausgelagert +;; aus vfl-vf-einheit (Option 3 "Kettenende"), damit sie auch VOR einem +;; Winkel-Solve aufgerufen werden kann: der tatsaechliche ES-Fussabdruck +;; (ein-dx/ein-dz, per vfl-es-masse-setzen INNERHALB von vfl-frage-es-seite +;; gesetzt) muss dafuer VOR dem Solve feststehen, sonst waehlt dieser einen zu +;; flachen/langen Winkel und der spaeter noetige Schwanz (Motorstation+ +;; Separator+ES) passt nicht mehr in die Reststrecke (siehe vfl-vf-einheit, +;; vorab-antwort-Parameter). Bei "Nein" werden ein-dx/ein-dz genullt (kein +;; ES-Fussabdruck reserviert) und der alte Wert als 3./4. Rueckgabewert +;; mitgegeben - der Aufrufer muss ihn nach getaner Zerlegung wiederherstellen. +;; Rueckgabe: (es-gewuenscht es-seite save-eindx save-eindz). +(defun vfl-es-element-abfragen ( / es-antwort es-gewuenscht es-seite save-eindx save-eindz) + (princ (ssg-text "vfl-es-setzen-frage")) + (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"))) + (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")) + (setq es-gewuenscht (/= es-antwort "2")) + (if es-gewuenscht + (setq es-seite (vfl-frage-es-seite t)) + (progn (setq save-eindx ein-dx save-eindz ein-dz) + (setq ein-dx 0.0 ein-dz 0.0))) + (list es-gewuenscht es-seite save-eindx save-eindz)) + ;; --- Horizontaler Koerper: Frage-Schale (Name unveraendert) --- ;; Fragt Separator vor/nach und - im Ziel-Modus - "Ist der Endpunkt der ;; Foerderer?", danach baut vfl-hor-koerper-bauen. Keine der drei Fragen @@ -4563,6 +4699,71 @@ ) (princ)) +;; Stufe-3-Zusatzfrage: Position des horizontalen Fuellstuecks (vor ODER nach +;; dem S-Bogen) - wird NUR gestellt, wenn Stufe 3 tatsaechlich einen Treffer +;; liefert (seltener Fallback, siehe vfl-winkel-stufe-entscheidung). Generischer +;; vflw_wahl-Popup-Dialog ueber vfl-menu (Muster wie die AUS-Element-Seite- +;; Frage) - keine neue DCL-Sonderloesung noetig. Selbstbeschreibender String +;; "vor"/"nach" statt roher Index-Antwort (Projektkonvention). +(defun vfl-frage-stufe3-position (/ antwort) + (princ (ssg-text "vfl-frage-stufe3-position")) + (princ (ssg-text "vfl-opt-stufe3-vor")) + (princ (ssg-text "vfl-opt-stufe3-nach")) + (setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2") + (list (ssg-text "vfl-opt-stufe3-vor") + (ssg-text "vfl-opt-stufe3-nach")) 1 + "vfl-frage-stufe3-position")) + (if (= antwort "2") "nach" "vor")) + +;; Gemeinsamer Bau-Helfer fuer EINEN VF-Koerper, optional MIT zusaetzlichem +;; horizontalem Fuellstueck (Stufe 3, siehe vfl-winkel-stufe-entscheidung/ +;; vfl-stufe3-winkel-horizontal in vf_standard.lsp). Ersetzt den bis dahin an +;; zwei Stellen identisch duplizierten Aufruf (vfl-body-abschluss-bauen UND +;; vfl-vf-einheit): (vfl-frame-3grad (vfs-vf-koerper ... hzn) hzn) + +;; vfl-acc-vf-seg + Zaehler-Erhoehung. +;; L_hor/position nil oder L_hor<=0: unveraendertes Verhalten (ein einzelner +;; vfs-vf-koerper-Aufruf) - reiner Drop-in-Ersatz fuer Stufe 1/2, KEINE +;; Verhaltensaenderung dort. +;; Sonst: zwei vfs-vf-koerper-Aufrufe (S-Bogen bei winkel/L_VF, Fuellstueck +;; bei winkel=0/L_hor - baut sein eigenes auf_3/ab_3-Uebergangsbogenpaar +;; SELBST, siehe vfs-vf-koerper best-winkel=0-Sonderfall in vf_standard.lsp), +;; Reihenfolge nach position ("vor"/"nach" dem S-Bogen, vom Anwender gewaehlt). +;; Frame wird zwischen den Teilschritten jedesmal per vfl-frame-3grad neu +;; aufgebaut, jedes Teilstueck einzeln journalisiert (vfl-acc-vf-seg) - sonst +;; fehlt es im Export/Report. +;; vfl-nach-3grad (falls noetig) bleibt Sache des AUFRUFERS, wie bisher. +;; Rueckgabe: (list frame cnt-erhoehung), cnt-erhoehung = Anzahl eingefuegter +;; Koerper (1 ohne Fuellstueck, 2 mit). +(defun vfl-baue-vf-koerper-mit-fuellstueck (frame richtn winkel L_VF L_hor position hzn / cnt) + (setq cnt 0) + (if (and L_hor (> L_hor 1.0) position) + (if (= position "vor") + (progn + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn 0 L_hor hzn) hzn)) + (vfl-acc-vf-seg "horizontal" 0 L_hor) + (setq cnt (1+ cnt)) + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn winkel L_VF hzn) hzn)) + (vfl-acc-vf-seg richtn winkel L_VF) + (setq cnt (1+ cnt)) + ) + (progn + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn winkel L_VF hzn) hzn)) + (vfl-acc-vf-seg richtn winkel L_VF) + (setq cnt (1+ cnt)) + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn 0 L_hor hzn) hzn)) + (vfl-acc-vf-seg "horizontal" 0 L_hor) + (setq cnt (1+ cnt)) + ) + ) + (progn + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn winkel L_VF hzn) hzn)) + (vfl-acc-vf-seg richtn winkel L_VF) + (setq cnt (1+ cnt)) + ) + ) + (list frame cnt) +) + ;; Vereinheitlichte Stufe-1(Winkel-Tabelle)/Stufe-2(horizontal+GF)-Entscheidung. ;; Genutzt von ZWEI Aufrufern mit unterschiedlichem Fest-Budget: ;; - vfl-body-zerlegung (Kettenende innerhalb einer laufenden VF-Einheit @@ -4579,12 +4780,17 @@ ;; dort schon flach sein, siehe vfl-baue-horizontal-koerper) - am Eingang ;; einer frischen VF-Einheit immer nil (Frame kommt frisch von der ;; Umlenkstation bei 3 Grad). -;; Rueckgabe: (typ winkel L_GF L_VF), typ="GF"/"VF"/nil (winkel=0 bei "VF" -;; heisst horizontales Stufe-2-Ergebnis). KEINE eigene Mindestlaengen-Pruefung -;; - die bleibt Sache der Aufrufer (unterschiedliche Schwellwerte: 800/1000). +;; Rueckgabe: (typ winkel L_GF L_VF) bzw. bei einem Stufe-3-Treffer (typ +;; winkel L_GF L_VF L_hor position), typ="GF"/"VF"/nil (winkel=0 bei "VF" +;; heisst horizontales Stufe-2-Ergebnis). L_hor/position sind nur bei Stufe 3 +;; belegt (siehe vfl-stufe3-winkel-horizontal, vf_standard.lsp) - fehlende +;; Listenelemente liefern ueber (nth ...) automatisch nil, alle Konsumenten +;; lesen ausschliesslich per nth. KEINE eigene Mindestlaengen-Pruefung - die +;; bleibt Sache der Aufrufer (unterschiedliche Schwellwerte: 800/1000). (defun vfl-winkel-stufe-entscheidung (deltaL deltaH richtung schon-flach feste-hz / save-ausdx save-ausdz save-feste - winkel-natuerlich wahl horizontal-info res) + winkel-natuerlich wahl horizontal-info + stufe3-erg res) (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) ;; Gesammelten Stufe-2-Diagnosetext + Alert-Vorschlagswerte aus einem evtl. ;; vorherigen Aufruf verwerfen - der Aufrufer haengt/zeigt sie nur an, wenn @@ -4624,7 +4830,18 @@ (vfl-stufe2-diagnose deltaL deltaH horizontal-info schon-flach) (if (and horizontal-info (caddr horizontal-info)) (list "VF" 0 (car horizontal-info) (cadr horizontal-info)) - (list nil nil nil nil))))) + (progn + ;; Stufe 1 UND Stufe 2 gescheitert: Stufe 3 (steiler Tabellen- + ;; winkel + horizontales Fuellstueck, vfl-stufe3-winkel- + ;; horizontal in vf_standard.lsp) als letzter Fallback vor der + ;; Ablehnung. Position (vor/nach dem S-Bogen) fragt der + ;; Anwender NUR, wenn Stufe 3 tatsaechlich einen Treffer liefert. + (princ (ssg-text "vfl-stufe3-start")) + (setq stufe3-erg (vfl-stufe3-winkel-horizontal deltaL deltaH "Auf" feste-hz schon-flach)) + (if (nth 4 stufe3-erg) + (list "VF" (nth 0 stufe3-erg) (nth 1 stufe3-erg) (nth 2 stufe3-erg) + (nth 3 stufe3-erg) (vfl-frage-stufe3-position)) + (list nil nil nil nil))))))) ;; fallend: natuerlichen Neigungswinkel gegen die 3-Grad-Eigenneigung pruefen (t (setq winkel-natuerlich (* (atan (/ deltaH deltaL)) (/ 180.0 pi))) @@ -4704,7 +4921,8 @@ ;; ein-dx/ein-dz um den Aufruf herum nullt (kein ES-Fussabdruck reservieren); ;; letzt-hz wurde nur einer wirkungslosen Zuweisung zugefuehrt (siehe unten). (defun vfl-body-abschluss-bauen (frame p-umlenk dL hzn hn schon-flach es-gewuenscht / - dH richtn rad3 dec typ w lgf lvf gf2 cnt + dH richtn rad3 dec typ w lgf lvf lhor position + bau-erg gf2 cnt p0 tx ty letzt-hz antwort-gf2) (setq dH (- hn (caddr (car frame)))) (setq richtn (if (>= dH 0.0) "Auf" "Ab")) @@ -4713,7 +4931,8 @@ (princ (ssg-textf "vfl-kettenende-modus-ziel" (list (rtos dL 2 0) (rtos dH 2 0) richtn))) (setq dec (vfl-body-zerlegung dL dH richtn schon-flach es-gewuenscht)) - (setq typ (nth 0 dec) w (nth 1 dec) lgf (nth 2 dec) lvf (nth 3 dec)) + (setq typ (nth 0 dec) w (nth 1 dec) lgf (nth 2 dec) lvf (nth 3 dec) + lhor (nth 4 dec) position (nth 5 dec)) (if (null typ) (progn (princ (ssg-text "vfl-fehler-rest-nicht-baubar")) @@ -4768,9 +4987,8 @@ ;; flachen Fall ab (siehe berechne-alle-winkel/entry-dx/entry-dz). ((and (= typ "VF") lvf (> lvf 1.0)) (setq frame (vfl-nach-3grad frame)) - (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn w lvf hzn) hzn)) - (vfl-acc-vf-seg richtn w lvf) - (setq cnt (1+ cnt))) + (setq bau-erg (vfl-baue-vf-koerper-mit-fuellstueck frame richtn w lvf lhor position hzn)) + (setq frame (car bau-erg) cnt (+ cnt (cadr bau-erg)))) ) ;; ohne Wirkung: letzt-hz ist lokal und wird nicht mehr gelesen - ;; die Fahrtrichtung verlaesst die Funktion ueber die Rueckgabe. @@ -4816,9 +5034,18 @@ ;; die gemessene Laenge und die aktuelle Kettenhoehe. Darum in dieser ;; Reihenfolge: flache Zone abschliessen -> Laenge messen -> Vorschlaege ;; berechnen -> Zielhoehe fragen -> bauen. -(defun vfl-body-abschluss (frame letzt-hz p-umlenk es-gewuenscht / +;; vorab-ziel-punkt (optional, Default nil): der ABSOLUTE Zielpunkt (Liste +;; x y z) steht bereits fest - z.B. weil er schon VOR dem Winkel-Solve der +;; VF-Einheit gepickt wurde (siehe vfl-vf-einheit, vorab-antwort="3"). Dann +;; NICHT nochmal per vfl-neue-linie-messen/vfl-hoehe-endpunkt-abfragen fragen +;; (der sonst noetige, fehleranfaellige zweite Klick auf denselben Punkt - +;; Nutzer-Wunsch), sondern Restlaenge/-hoehe direkt aus der aktuellen +;; Frame-Position zu diesem Punkt herleiten (vfl-projiziere-distanz, dieselbe +;; Technik wie bei der AS-Restlaengen-Korrektur). letzt-hz bleibt dabei die +;; Fahrtrichtung (unveraendert seit dem urspruenglichen Pick). +(defun vfl-body-abschluss (frame letzt-hz p-umlenk es-gewuenscht vorab-ziel-punkt / linie-mess dL hzn hn schon-flach ergebnis dH richtn - hoehe-vorschlaege hoehe-ab hoehe-auf hoehe-fallback) + hoehe-vorschlaege hoehe-ab hoehe-auf gueltig) ;; Schon-flach VOR jeder Konvertierung feststellen - NICHT mehr blind auf ;; 3 Grad zwingen, bevor feststeht, ob die Zerlegung ohnehin ein ;; horizontales Mittelstueck ergibt (dann waere die Konvertierung + der @@ -4826,29 +5053,41 @@ ;; auf_3/ab_3-Bogenpaar - siehe Kommentar bei vfl-winkel-stufe-entscheidung). ;; Die Positions-Messung selbst funktioniert unabhaengig von der Neigung. (setq schon-flach (vfl-frame-flach-p frame)) - (setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz)) - (if (null linie-mess) + (setq gueltig t) + (if vorab-ziel-punkt + (setq dL (vfl-projiziere-distanz (car frame) vorab-ziel-punkt letzt-hz) + hzn letzt-hz + hn (caddr vorab-ziel-punkt)) + (progn + (setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz)) + (if (null linie-mess) + (setq gueltig nil) + (progn + (setq dL (car linie-mess) hzn (cadr linie-mess)) + ;; Baubare Vorschlaege (Ab/Auf) statt der unveraenderten Kettenhoehe + ;; als Default - sonst waere dH=0 ("Auf", da (>= 0.0 0.0)) und der + ;; Rest wird von vfl-body-zerlegung sofort als "nicht baubar" + ;; abgelehnt (kleineres Budget als vfl-vf-entscheidung: nur + ;; Motor+Separator, 800mm statt 1300mm) - siehe + ;; vfl-body-hoehe-vorschlaege/vfl-body-machbar-p. + (setq hoehe-vorschlaege (vfl-body-hoehe-vorschlaege (caddr (car frame)) dL es-gewuenscht schon-flach)) + (setq hoehe-ab (nth 0 hoehe-vorschlaege) hoehe-auf (nth 1 hoehe-vorschlaege)) + (setq hn (vfl-hoehe-endpunkt-abfragen hoehe-ab hoehe-auf (caddr (car frame)) "kettenende")) + ) + ) + ) + ) + ;; Nutzer-Entscheidung (2026-09-15): dL/hzn bleiben unveraendert entlang der + ;; urspruenglich gemessenen/vorgegebenen Fahrtrichtung - KEINE Drehung mehr, + ;; um den seitlichen Fussabdruck eines drehenden ES-Elements (ein-dy) + ;; auszugleichen (siehe vfl-richtung-mit-drehendem-fussabdruck, jetzt nicht + ;; mehr hier verwendet). Ein drehendes ES-Element (30/90 Grad) landet + ;; dadurch mit einem natuerlichen, unvermeidbaren seitlichen Versatz neben + ;; dem Zielpunkt - erwartetes Verhalten (Hauptstrang bleibt gerade), keine + ;; zu behebende Abweichung. + (if (not gueltig) (progn (princ (ssg-text "vfl-abgebrochen-kein-ziel")) nil) (progn - (setq dL (car linie-mess) hzn (cadr linie-mess)) - ;; Baubare Vorschlaege (Ab/Auf) statt der unveraenderten Kettenhoehe als - ;; Default - sonst waere dH=0 ("Auf", da (>= 0.0 0.0)) und der Rest wird - ;; von vfl-body-zerlegung sofort als "nicht baubar" abgelehnt (kleineres - ;; Budget als vfl-vf-entscheidung: nur Motor+Separator, 800mm statt - ;; 1300mm) - siehe vfl-body-hoehe-vorschlaege/vfl-body-machbar-p. - (setq hoehe-vorschlaege (vfl-body-hoehe-vorschlaege (caddr (car frame)) dL es-gewuenscht schon-flach)) - (setq hoehe-ab (nth 0 hoehe-vorschlaege) hoehe-auf (nth 1 hoehe-vorschlaege)) - (setq hoehe-fallback (if hoehe-ab hoehe-ab (caddr (car frame)))) - (vfl-hoehe-hinweis-falls-noetig hoehe-ab hoehe-auf) - (if (vfl-wizard-aktiv) - (vl-catch-all-apply 'vflw-gruppe-ziel-hoehe-impl - (list (list hoehe-ab hoehe-auf (caddr (car frame)))))) - (setq hn (vfl-in-real - (if (and hoehe-ab hoehe-auf) - (ssg-textf "vfl-prompt-hoehe-kettenende-2" - (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1))) - (ssg-textf "vfl-prompt-hoehe-kettenende" (list (rtos hoehe-fallback 2 1)))))) - (if (null hn) (setq hn hoehe-fallback)) (setq ergebnis (vfl-body-abschluss-bauen frame p-umlenk dL hzn hn schon-flach es-gewuenscht)) ;; Klare Rueckmeldung statt nur der einfachen Bau-Fehlermeldung (die ;; steht schon in vfl-body-abschluss-bauen): die tatsaechlich gewaehlte @@ -4931,12 +5170,30 @@ frame) ;; Rueckgabe: (frame anzahl-koerper ziel-ende) -(defun vfl-vf-einheit (frame hz1 richtung1 winkel1 L_GF1 L_VF1 gf-am-ausgang eingang-modus / +;; vorab-antwort/vorab-es-gewuenscht/vorab-es-seite (optional, Default nil): +;; wurde "Ist der Endpunkt der Foerderer?" (+ ggf. "ES-Element setzen?") vom +;; Aufrufer bereits VOR diesem Aufruf gestellt (siehe vf-linienzug-modus, +;; "Linie-VF"-Zweig) - dann NICHT in der Fortsetzungsschleife nochmal fragen, +;; sondern die Antwort direkt fuer die ERSTE Runde uebernehmen. Nur fuer den +;; geneigten Erstkoerper relevant (winkel1<>0) - der horizontale Fall +;; (winkel1=0) hat sein eigenes vor-antwort ueber vfl-baue-horizontal-koerper. +;; vorab-ziel-punkt (nur bei vorab-antwort="3" relevant): der bereits VOR dem +;; Winkel-Solve absolut gepickte Zielpunkt (x y z) - wird 1:1 an +;; vfl-body-abschluss durchgereicht, damit dort NICHT nochmal ein zweiter +;; Endpunkt abgefragt wird (Nutzer-Wunsch: "der zweite Klick ist unnoetig"). +;; L_hor1/L_hor1-position: nur bei einem Stufe-3-Treffer fuer das ERSTE +;; Koerper-Sub-Segment belegt (siehe vfl-vf-einheit-abschluss), sonst nil. +;; Die interne "weiterbauen"-Fortsetzung (ent aus vfl-segment-entscheidung, +;; s.u.) hat ihr EIGENES, unabhaengiges lhor/position - andere Entscheidung, +;; anderer Solve-Aufruf. +(defun vfl-vf-einheit (frame hz1 richtung1 winkel1 L_GF1 L_VF1 L_hor1 L_hor1-position + gf-am-ausgang eingang-modus + vorab-antwort vorab-es-gewuenscht vorab-es-seite vorab-ziel-punkt / p-umlenk vf-count antwort L_GF-eff L_GF1-bau L_GF2-bau - linie-mess dL dH hn hzn richtn ent typ w lgf lvf + linie-mess dL dH hn hzn richtn ent typ w lgf lvf lhor position bau-erg letzt-hz fertig ziel-ende ziel-gf2 res3 gf2-eff entry-start - res-h vor-antwort es-gewuenscht es-antwort save-eindx save-eindz - hoehe-vorschlaege hoehe-ab hoehe-auf hoehe-fallback + res-h vor-antwort es-gewuenscht erg-es save-eindx save-eindz + hoehe-vorschlaege hoehe-ab hoehe-auf schon-flach winkel-hoehen-erg es-seite) ;; Vorbelegung: ES gewuenscht (Default) - nur bei "3 - Kettenende definieren" ;; wird explizit gefragt und ggf. auf nil gesetzt (siehe dort). @@ -4989,7 +5246,7 @@ (setq res-h (vfl-baue-horizontal-koerper frame hz1 (max *vfk-restlaenge-min-clamp* (- L_VF1 (vfl-projiziere-distanz entry-start p-umlenk hz1))) t L_GF2-bau)) - (setq frame (car res-h) vor-antwort (cadr res-h)) + (setq frame (car res-h) vor-antwort (cadr res-h) vf-count 1) ) (progn ;; vfs-vf-koerper braucht die 3-Grad-Basis als Eintritt - im Normalfall @@ -4997,11 +5254,12 @@ ;; bei eingang-modus "keine" kann frame noch flach sein (z.B. direkt ;; nach einer Vario-Kurve) und muss hier erst konvertiert werden. (setq frame (vfl-nach-3grad frame)) - (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtung1 winkel1 L_VF1 hz1) hz1)) - (vfl-acc-vf-seg richtung1 winkel1 L_VF1) + (setq bau-erg (vfl-baue-vf-koerper-mit-fuellstueck frame richtung1 winkel1 L_VF1 L_hor1 L_hor1-position hz1)) + (setq frame (car bau-erg) vf-count (cadr bau-erg)) + (setq vor-antwort vorab-antwort) ) ) - (setq vf-count 1 letzt-hz hz1) + (setq letzt-hz hz1) (vfl-20m-check p-umlenk (car frame)) ;; --- Fortsetzungs-Schleife bis Foerderer-Ende --- @@ -5030,31 +5288,37 @@ ;; ein-dz waehrend der Zerlegung temporaer nullen, damit KEIN ;; ES-Fussabdruck reserviert wird - die Kette endet dann direkt am ;; Zielpunkt ohne Separator+ES. - (princ (ssg-text "vfl-es-setzen-frage")) - (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"))) - (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")) - (setq es-gewuenscht (/= es-antwort "2")) - (if es-gewuenscht - ;; dialog-schon-gezeigt=T: Winkel+Seite kamen bereits aus dem - ;; kombinierten vflw-gruppe-as-impl-Dialog oben mit. - (setq es-seite (vfl-frage-es-seite t)) - (progn (setq save-eindx ein-dx save-eindz ein-dz) - (setq ein-dx 0.0 ein-dz 0.0))) - (setq res3 (vfl-body-abschluss frame letzt-hz p-umlenk es-gewuenscht)) - (if (not es-gewuenscht) - (setq ein-dx save-eindx ein-dz save-eindz)) - (if res3 - (setq frame (nth 0 res3) - vf-count (+ vf-count (nth 1 res3)) - letzt-hz (nth 2 res3) - ziel-gf2 (nth 3 res3) - ziel-ende t - fertig t))) + ;; War das schon VOR dem Winkel-Solve entschieden (vorab-antwort="3", + ;; siehe Funktionskopf) - dann NICHT nochmal fragen (waere doppelt + ;; journalisiert), ein-dx/ein-dz NICHT anfassen (stehen bereits korrekt, + ;; der Aufrufer hat sie vor dem Solve gesetzt) UND vfl-body-abschluss + ;; GAR NICHT mehr aufrufen: die schon VOR diesem Solve bekannte + ;; Fahrtrichtungs-Korrektur (vfl-richtung-mit-drehendem-fussabdruck im + ;; Aufrufer) macht den ersten Winkel-Solve bereits vollstaendig ES- + ;; bewusst (laengs UND quer) - die dabei schon berechnete GF2-Laenge + ;; (L_GF2-bau, aus der GF-Verteilungs-Frage) trifft den Zielpunkt daher + ;; bereits exakt. Eine zweite, unabhaengige Loesung (vfl-body-abschluss) + ;; war nur noetig, SOLANGE der erste Solve das ES-Element noch nicht + ;; kannte - real beobachteter Bug: genau diese zweite, unabhaengige + ;; Loesung konnte scheitern (z.B. "kein passender Winkel"), obwohl die + ;; ERSTE Wahl bereits eine vollstaendig gueltige Gesamtloesung war. + (if (equal vorab-antwort "3") + (setq es-gewuenscht vorab-es-gewuenscht es-seite vorab-es-seite + ziel-gf2 L_GF2-bau ziel-ende t fertig t) + (progn + (setq erg-es (vfl-es-element-abfragen)) + (setq es-gewuenscht (nth 0 erg-es) es-seite (nth 1 erg-es) + save-eindx (nth 2 erg-es) save-eindz (nth 3 erg-es)) + (setq res3 (vfl-body-abschluss frame letzt-hz p-umlenk es-gewuenscht nil)) + (if (not es-gewuenscht) (setq ein-dx save-eindx ein-dz save-eindz)) + (if res3 + (setq frame (nth 0 res3) + vf-count (+ vf-count (nth 1 res3)) + letzt-hz (nth 2 res3) + ziel-gf2 (nth 3 res3) + ziel-ende t + fertig t)))) + ) (progn (princ (ssg-text "vfl-naechstes-element-vf")) (princ (ssg-text "vfl-opt-horizontaler-foerderer")) @@ -5155,28 +5419,18 @@ ;; (3-Grad-Formel) waere hier also gerade NICHT baubar. (setq hoehe-vorschlaege (vfl-vf-hoehe-vorschlaege (caddr (car frame)) dL)) (setq hoehe-ab (nth 0 hoehe-vorschlaege) hoehe-auf (nth 1 hoehe-vorschlaege)) - (setq hoehe-fallback (if hoehe-ab hoehe-ab (caddr (car frame)))) - (vfl-hoehe-hinweis-falls-noetig hoehe-ab hoehe-auf) - (if (vfl-wizard-aktiv) - (vl-catch-all-apply 'vflw-gruppe-ziel-hoehe-impl - (list (list hoehe-ab hoehe-auf (caddr (car frame)))))) - (setq hn (vfl-in-real - (if (and hoehe-ab hoehe-auf) - (ssg-textf "vfl-prompt-hoehe-endpunkt-2" - (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1))) - (ssg-textf "vfl-prompt-hoehe-endpunkt" - (list (rtos hoehe-fallback 2 1)))))) - (if (null hn) (setq hn hoehe-fallback)) + (setq hn (vfl-hoehe-endpunkt-abfragen hoehe-ab hoehe-auf (caddr (car frame)) "endpunkt")) (setq dH (- hn (caddr (car frame)))) (setq richtn (if (>= dH 0.0) "Auf" "Ab")) (setq dH (abs dH)) (setq ent (vfl-segment-entscheidung dL dH richtn)) - (setq typ (nth 0 ent) w (nth 1 ent) lgf (nth 2 ent) lvf (nth 3 ent)) + (setq typ (nth 0 ent) w (nth 1 ent) lgf (nth 2 ent) lvf (nth 3 ent) + lhor (nth 4 ent) position (nth 5 ent)) (if (and typ (= typ "VF")) (progn - (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn w lvf hzn) hzn)) - (setq vf-count (1+ vf-count) letzt-hz hzn) - (vfl-acc-vf-seg richtn w lvf) + (setq bau-erg (vfl-baue-vf-koerper-mit-fuellstueck frame richtn w lvf lhor position hzn)) + (setq frame (car bau-erg)) + (setq vf-count (+ vf-count (cadr bau-erg)) letzt-hz hzn) (vfl-20m-check p-umlenk (car frame)) ) (princ (ssg-text "vfl-segment-gf-nicht-erlaubt")) @@ -5869,7 +6123,15 @@ (setq *vfl-ziel-punkt* nil))) (princ)) -(defun vfl-vf-einheit-abschluss (frame hz richtung winkel L_GF L_VF auto-ende gf-verteilung-fest eingang-modus / +;; vorab-antwort/vorab-es-gewuenscht/vorab-es-seite/vorab-ziel-punkt: siehe +;; vfl-vf-einheit (Default nil - unveraendertes Verhalten fuer alle Aufrufer, +;; die diese Vorab-Entscheidung nicht kennen). +;; L_hor/L_hor-position: nur bei einem Stufe-3-Treffer belegt (siehe +;; vfl-winkel-stufe-entscheidung/vfl-stufe3-winkel-horizontal), sonst nil - +;; unveraendertes Verhalten fuer alle anderen Aufrufer. +(defun vfl-vf-einheit-abschluss (frame hz richtung winkel L_GF L_VF L_hor L_hor-position + auto-ende gf-verteilung-fest eingang-modus + vorab-antwort vorab-es-gewuenscht vorab-es-seite vorab-ziel-punkt / antwort res es-s ende es-gewuenscht es-seite-vorab) ;; GF-Verteilung: halbe Staustrecke am Ausgang (GF2) oder alles am Einlauf. ;; gf-verteilung-fest: Aufrufer erzwingt die Antwort (z.B. "2", wenn L_GF @@ -5886,7 +6148,8 @@ (list (ssg-text "vfl-gf-verteilung-haelfte") (ssg-text "vfl-gf-verteilung-ganz-einlauf")) 2 "vfl-gf-verteilung-header")))) - (setq res (vfl-vf-einheit frame hz richtung winkel L_GF L_VF (= antwort "1") eingang-modus)) + (setq res (vfl-vf-einheit frame hz richtung winkel L_GF L_VF L_hor L_hor-position (= antwort "1") eingang-modus + vorab-antwort vorab-es-gewuenscht vorab-es-seite vorab-ziel-punkt)) (setq frame (nth 0 res) es-gewuenscht (nth 3 res) es-seite-vorab (nth 4 res)) ;; Kettenende? Bei auto-ende (Kettenende-Modus) ohne Frage direkt ES setzen. ;; ziel-ende (Option 3 IN der VF-Einheit) OHNE ES-Wunsch (dort abgefragt, @@ -5951,19 +6214,22 @@ ;; ============================================================ (defun vf-linienzug-modus ( / startpunkt start-hoehe as-seite es-seite antwort wahl p-aktuell linie-mess hoehe-neu hoehe-bis deltaL deltaH richtung hz-neu - entscheidung typ winkel L_GF L_VF vf-einheit-res + entscheidung typ winkel L_GF L_VF lhor stufe3-position vf-einheit-res frame letzter-typ fertig linie-ende-modus rad3 anzahl-gf anzahl-vf vfl-nummer lastEnt seg-lastEnt gf-max-winkel gf-ok kettenanfang as-vorhanden erg old-error vfl-ins dbg-an - hoehe-vorschlaege hoehe-ab hoehe-auf hoehe-fallback + hoehe-vorschlaege hoehe-ab hoehe-auf gf-laenge-modus gf-steigend-modus sep-vor-gf sep-nach-gf sep-anzahl erg-bogen vf-eingang-lgf vf-eingang-modus eingang-laenge winkel-hoehen-erg bogen-mass fest-drop fest-laenge eingang-fertig linie-ende-familie linie-ende-untermodus res3 save-eindx save-eindz es-gewuenscht - startpunkt-familie gf-kettenende-antwort) + 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) ;; "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-*- @@ -6377,7 +6643,8 @@ ;; Eingang, das Ausgangs-Ende regelt allein die mit/ohne- ;; Motorstation-Frage (siehe vfl-baue-horizontal-koerper). (setq vf-einheit-res - (vfl-vf-einheit-abschluss frame hz-neu "Ab" 0 vf-eingang-lgf deltaL nil "2" vf-eingang-modus)) + (vfl-vf-einheit-abschluss frame hz-neu "Ab" 0 vf-eingang-lgf deltaL nil nil nil "2" vf-eingang-modus + nil nil nil nil)) (setq frame (nth 0 vf-einheit-res)) (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) (if (nth 2 vf-einheit-res) (setq fertig t)) @@ -6957,7 +7224,8 @@ " L_VF=" (rtos L_VF 2 0) " eingang-modus=" vf-eingang-modus ")"))) (setq vf-einheit-res - (vfl-vf-einheit-abschluss frame hz-neu richtung winkel L_GF L_VF nil "2" vf-eingang-modus)) + (vfl-vf-einheit-abschluss frame hz-neu richtung winkel L_GF L_VF nil nil nil "2" vf-eingang-modus + nil nil nil nil)) (setq frame (nth 0 vf-einheit-res)) (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) (if (nth 2 vf-einheit-res) (setq fertig t)) @@ -6971,27 +7239,80 @@ ) ) (progn + ;; Erst fragen, ob DIESER Endpunkt schon das Kettenende ist (+ + ;; falls ja: ES-Element setzen), DANN erst Zielhoehe/Winkel + ;; berechnen - analog zu vfl-baue-horizontal-koerper (horizontaler + ;; Erstkoerper). Vorher wurde die Frage erst NACH dem Bau des + ;; ersten (Winkel-)Segments gestellt (in der Fortsetzungsschleife + ;; von vfl-vf-einheit) - dann war ein evtl. noetiger ES- + ;; Fussabdruck (Motorstation+Separator+ES) beim Winkel-Solve noch + ;; nicht reserviert, der gewaehlte Winkel konnte zu viel Strecke + ;; verbrauchen und der zwingend zweite, separate Endpunkt-Pick + ;; fuer den "Schwanz" schlug dann mangels Reststrecke fehl (Nutzer- + ;; Ruecksprache). ein-dx/ein-dz (ES-Fussabdruck) wirken bereits + ;; automatisch in vfl-vf-hoehe-vorschlaege/vfl-vf-entscheidung + ;; (ueber vfl-winkel-machbar-p) - hier muss nur die Reihenfolge + ;; stimmen. + (setq vorab-antwort (vfl-frage-ist-endpunkt)) + (setq vorab-es-gewuenscht nil vorab-es-seite nil + save-eindx-vorab nil save-eindz-vorab nil) + (setq sep-eindx-add 0.0 sep-eindz-add 0.0) + (if (= vorab-antwort "3") + (progn + (setq erg-es (vfl-es-element-abfragen)) + (setq vorab-es-gewuenscht (nth 0 erg-es) vorab-es-seite (nth 1 erg-es) + save-eindx-vorab (nth 2 erg-es) save-eindz-vorab (nth 3 erg-es)) + ;; vfl-insert-es-element baut IMMER einen eigenen Separator + ;; (300mm, 3 Grad) VOR dem ES-Block - das ist NICHT Teil des + ;; ueber vf-set-es-masse gemessenen ein-dx/ein-dz (die messen + ;; nur den ES-Block selbst, siehe Bibliotheks-Init-Log + ;; "ES_90: dx=436 dz=-65"). Fuer den Solve zusaetzlich + ;; reservieren, sonst fehlt genau dieser Fussabdruck im + ;; Ergebnis - real gemessener Bug (BricsCAD-Test): dX=-299.6mm + ;; dZ=-15.7mm, exakt der Separator-Fussabdruck bei 3 Grad + ;; (300*cos3=299.6, 300*sin3=15.7). Nach dem Solve/Bau wieder + ;; abgezogen (siehe Restaurierung unten), damit ein-dx/ein-dz + ;; danach wieder den reinen ES-Block-Fussabdruck zeigen. + (if vorab-es-gewuenscht + (progn + (setq rad3-sep (* (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) (/ pi 180.0))) + (setq sep-eindx-add (* *vfk-separator-laenge* (cos rad3-sep)) + sep-eindz-add (* *vfk-separator-laenge* (sin rad3-sep))) + (setq ein-dx (+ ein-dx sep-eindx-add) + ein-dz (- ein-dz sep-eindz-add)))))) (setq hoehe-vorschlaege (vfl-vf-hoehe-vorschlaege (caddr p-aktuell) deltaL)) (setq hoehe-ab (nth 0 hoehe-vorschlaege) hoehe-auf (nth 1 hoehe-vorschlaege)) - (setq hoehe-fallback (if hoehe-ab hoehe-ab (caddr p-aktuell))) - (vfl-hoehe-hinweis-falls-noetig hoehe-ab hoehe-auf) - (if (vfl-wizard-aktiv) - (vl-catch-all-apply 'vflw-gruppe-ziel-hoehe-impl - (list (list hoehe-ab hoehe-auf (caddr p-aktuell))))) - (setq hoehe-neu - (vfl-in-real - (if (and hoehe-ab hoehe-auf) - (ssg-textf "vfl-prompt-hoehe-linienendpunkt-2" - (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1))) - (ssg-textf "vfl-prompt-hoehe-linienendpunkt" (list (rtos hoehe-fallback 2 1)))))) - (if (null hoehe-neu) (setq hoehe-neu hoehe-fallback)) + (setq hoehe-neu (vfl-hoehe-endpunkt-abfragen hoehe-ab hoehe-auf (caddr p-aktuell) "linienendpunkt")) (setq deltaH (- hoehe-neu (caddr p-aktuell))) (setq richtung (if (>= deltaH 0.0) "Auf" "Ab")) (setq deltaH (abs deltaH)) (vfl-echo-endpunkt-hoehe p-aktuell deltaL hz-neu hoehe-neu deltaH richtung) + ;; Absoluter Zielpunkt dieses (einzigen) Picks - nur bei "3" + ;; gebraucht: wird bis nach vfl-vf-einheit durchgereicht, damit + ;; vfl-body-abschluss NICHT nochmal denselben Punkt abfragt + ;; (siehe vfl-vf-einheit/vfl-body-abschluss, vorab-ziel-punkt). + (setq vorab-ziel-punkt + (if (= vorab-antwort "3") + (list (+ (car p-aktuell) (* deltaL (cos (* (float hz-neu) (/ pi 180.0))))) + (+ (cadr p-aktuell) (* deltaL (sin (* (float hz-neu) (/ pi 180.0))))) + hoehe-neu) + nil)) + ;; Nutzer-Entscheidung (2026-09-15): der Hauptstrang (GF1/Klettern/ + ;; GF2/Motorstation) behaelt IMMER die urspruenglich gepickte + ;; Fahrtrichtung (hz-neu/deltaL bleiben unveraendert) - KEINE + ;; Vorab-Drehung mehr, um den seitlichen ES-Versatz (ein-dy) + ;; auszugleichen. Ein drehendes ES-Element (30/90 Grad) landet + ;; dadurch mit einem natuerlichen, unvermeidbaren seitlichen + ;; Versatz neben dem urspruenglich gepickten Punkt - das ist + ;; erwartetes Verhalten (Prinzip: Hauptstrang bleibt gerade, ES + ;; faellt "natuerlich" dorthin, wohin sein eigener Fussabdruck + ;; fuehrt), keine zu behebende Abweichung. ein-dx/ein-dz (Laengs/ + ;; Hoehe) werden weiterhin ganz normal ueber vfl-winkel-machbar-p + ;; reserviert (siehe vfl-vf-entscheidung). (setq entscheidung (vfl-vf-entscheidung deltaL deltaH richtung)) (setq typ (nth 0 entscheidung) winkel (nth 1 entscheidung) - L_GF (nth 2 entscheidung) L_VF (nth 3 entscheidung)) + L_GF (nth 2 entscheidung) L_VF (nth 3 entscheidung) + lhor (nth 4 entscheidung) stufe3-position (nth 5 entscheidung)) (if (null typ) (progn (vfl-meldung (ssg-textf "vfl-alert-vf-nicht-baubar" @@ -7022,7 +7343,9 @@ " winkel=" (rtos (float winkel) 2 1) " L_GF=" (rtos L_GF 2 0) " L_VF=" (rtos L_VF 2 0) ")"))) (setq vf-einheit-res - (vfl-vf-einheit-abschluss frame hz-neu richtung winkel L_GF L_VF nil nil nil)) + (vfl-vf-einheit-abschluss frame hz-neu richtung winkel L_GF L_VF lhor stufe3-position nil nil nil + vorab-antwort vorab-es-gewuenscht vorab-es-seite + vorab-ziel-punkt)) (setq frame (nth 0 vf-einheit-res)) (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) (if (nth 2 vf-einheit-res) (setq fertig t)) @@ -7034,6 +7357,18 @@ (dbg-tostring p-aktuell)))) ) ) + ;; ein-dx/ein-dz (ES-Fussabdruck) nach diesem gesamten Aufruf + ;; wiederherstellen - sowohl im Erfolgs- als auch im "nicht + ;; baubar"-Fehlerfall, sonst wuerde eine genullte/aufaddierte + ;; ES-Groesse in eine spaetere, unabhaengige ES-Entscheidung an + ;; anderer Stelle in dieser Kette durchsickern: bei "ES nein" auf + ;; den Zustand VOR der Frage zuruecksetzen, bei "ES ja" die oben + ;; addierte Separator-Reservierung wieder abziehen (ein-dx/ein-dz + ;; zeigen danach wieder den reinen ES-Block-Fussabdruck). + (if (= vorab-antwort "3") + (if vorab-es-gewuenscht + (setq ein-dx (- ein-dx sep-eindx-add) ein-dz (+ ein-dz sep-eindz-add)) + (setq ein-dx save-eindx-vorab ein-dz save-eindz-vorab))) ) ) ) @@ -7099,7 +7434,7 @@ (if es-gewuenscht (setq es-seite (vfl-frage-es-seite t)) (progn (setq save-eindx ein-dx save-eindz ein-dz) (setq ein-dx 0.0 ein-dz 0.0))) - (setq res3 (vfl-body-abschluss frame (car (frame->hz-winkel frame)) (car frame) es-gewuenscht)) + (setq res3 (vfl-body-abschluss frame (car (frame->hz-winkel frame)) (car frame) es-gewuenscht nil)) (if (not es-gewuenscht) (setq ein-dx save-eindx ein-dz save-eindz)) (if (null res3) (if dbg-an @@ -7170,22 +7505,30 @@ (progn (setq hoehe-vorschlaege (vfl-segment-hoehe-vorschlaege (caddr p-aktuell) deltaL)) (setq hoehe-ab (nth 0 hoehe-vorschlaege) hoehe-auf (nth 1 hoehe-vorschlaege)) - (setq hoehe-fallback (if hoehe-ab hoehe-ab (caddr p-aktuell))) - (vfl-hoehe-hinweis-falls-noetig hoehe-ab hoehe-auf) - (if (vfl-wizard-aktiv) - (vl-catch-all-apply 'vflw-gruppe-ziel-hoehe-impl - (list (list hoehe-ab hoehe-auf (caddr p-aktuell))))) - (setq hoehe-neu - (vfl-in-real - (if (and hoehe-ab hoehe-auf) - (ssg-textf "vfl-prompt-hoehe-linienendpunkt-2" - (list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1))) - (ssg-textf "vfl-prompt-hoehe-linienendpunkt" (list (rtos hoehe-fallback 2 1)))))) - (if (null hoehe-neu) (setq hoehe-neu hoehe-fallback)) + (setq hoehe-neu (vfl-hoehe-endpunkt-abfragen hoehe-ab hoehe-auf (caddr p-aktuell) "linienendpunkt")) (setq deltaH (- hoehe-neu (caddr p-aktuell))) (setq richtung (if (>= deltaH 0.0) "Auf" "Ab")) (setq deltaH (abs deltaH)) (vfl-echo-endpunkt-hoehe p-aktuell deltaL hz-neu hoehe-neu deltaH richtung) + ;; Kettenende-Modus (Option 6, "Neue Linie BIS Kettenende"): + ;; ES-Element wird HIER IMMER gebaut (das verspricht schon + ;; der Menuetext) - Winkel/Seite deshalb JETZT erfragen + ;; (vfl-frage-es-seite, setzt ein-dx/ein-dz via + ;; vf-set-es-masse), BEVOR die Fussabdruck-Reservierung + ;; unten laeuft - vorher liefen beide mit veralteten/ + ;; zufaelligen ein-dx/ein-dz (die echte Frage kam bisher + ;; erst spaeter, redundant, INNERHALB von vfl-vf-einheit - + ;; Nutzer-Ruecksprache: derselbe Fehler wie bei "Neue + ;; Linie: Ab/Auf VF", hier nur eine Stufe frueher versteckt). + ;; vorab-ziel-punkt (der bereits gepickte Zielpunkt) wird bis + ;; zu vfl-vf-einheit durchgereicht, damit fuer den "Auf"-Fall + ;; (VF-Einheit noetig) KEIN zweiter Klick mehr noetig ist. + (setq vorab-antwort "3" vorab-es-gewuenscht t) + (setq vorab-es-seite (vfl-frage-es-seite nil)) + (setq vorab-ziel-punkt + (list (+ (car p-aktuell) (* deltaL (cos (* (float hz-neu) (/ pi 180.0))))) + (+ (cadr p-aktuell) (* deltaL (sin (* (float hz-neu) (/ pi 180.0))))) + hoehe-neu)) ;; Kettenende-Modus (Option 4): Der abgefragte Endpunkt (XY+Z) ;; ist das exakte Ende der Kette NACH Separator + ES-Element. ;; Deren Footprint (Separator 300 mm bei ~3 Grad + ES-Element @@ -7204,6 +7547,23 @@ (list (rtos deltaL 2 0) (rtos deltaH 2 0)))) ) ) + ;; ein-dx/ein-dz waehrend der Typ-/Winkel-Entscheidung + ;; (vfl-vf-entscheidung/vfl-segment-entscheidung, nur im + ;; "Auf"-Fall relevant) temporaer nullen: der ES-Fussabdruck + ;; wurde oben BEREITS von deltaL/deltaH abgezogen - eine + ;; zweite Reservierung INNERHALB des Solvers waere doppelt + ;; (analog zu vfl-vf-winkel, das aus-dx/aus-dz aus demselben + ;; Grund temporaer nullt). Nach der Entscheidung sofort + ;; wiederhergestellt - der spaetere Bau (vfl-vf-einheit) + ;; braucht die echten Werte nicht mehr (er nutzt L_GF2-bau + ;; direkt), aber andere Stellen in dieser Kette schon. + (setq save-eindx-vorab ein-dx save-eindz-vorab ein-dz) + (setq ein-dx 0.0 ein-dz 0.0) + ;; Aus einer fruehren Schleifen-Iteration stehengebliebene + ;; Stufe-3-Werte NICHT versehentlich fuer dieses Segment + ;; wiederverwenden - nur die beiden entscheidung-Zweige + ;; unten setzen sie ggf. neu. + (setq lhor nil stufe3-position nil) (if (equal linie-ende-familie "GF") (progn ;; --- Startpunkt als GF-Familie erkannt --- @@ -7221,7 +7581,24 @@ (setq *vfl-stufe2-diagnose-text* nil) (setq *vfl-stufe2-minl-vorschlag* nil *vfl-stufe2-minh-vorschlag* nil) (if (= richtung "Auf") - (setq typ nil) + ;; Steigend: wie im "nicht eindeutig"-Zweig + ;; (vfl-segment-entscheidung) NICHT sofort + ;; aufgeben - eine bestehende GF-Kette darf + ;; durchaus in eine frische VF-Einheit (eigene + ;; Umlenk-/Motorstation) uebergehen. Direkt + ;; vfl-vf-entscheidung aufrufen (identisch zu dem, + ;; was vfl-segment-entscheidung fuer "Auf" ohnehin + ;; getan haette) - der gemeinsame Baubereich unten + ;; (typ="GF" vs. sonst) baut ein typ="VF"-Ergebnis + ;; bereits unveraendert ueber vfl-vf-einheit- + ;; abschluss (mit eigener Umlenkstation), genau wie + ;; bei "Neue Linie: Ab/Auf VF". Realer Bug-Fall: + ;; vorher wurde hier IMMER typ=nil gesetzt, obwohl + ;; ein baubarer VF-Weg existierte (Nutzer-Meldung). + (setq entscheidung (vfl-vf-entscheidung deltaL deltaH richtung) + typ (nth 0 entscheidung) winkel (nth 1 entscheidung) + L_GF (nth 2 entscheidung) L_VF (nth 3 entscheidung) + lhor (nth 4 entscheidung) stufe3-position (nth 5 entscheidung)) (progn (princ (ssg-text "vfl-gf-laenge-modus-header")) (princ (ssg-text "vfl-gf-laenge-modus-opt-endpunkt")) @@ -7254,10 +7631,13 @@ (progn (setq entscheidung (vfl-segment-entscheidung deltaL deltaH richtung)) (setq typ (nth 0 entscheidung) winkel (nth 1 entscheidung) - L_GF (nth 2 entscheidung) L_VF (nth 3 entscheidung))) + L_GF (nth 2 entscheidung) L_VF (nth 3 entscheidung) + lhor (nth 4 entscheidung) stufe3-position (nth 5 entscheidung))) ) ) ) + ;; ein-dx/ein-dz wiederherstellen (siehe Nullung oben). + (setq ein-dx save-eindx-vorab ein-dz save-eindz-vorab) (if (null typ) (progn @@ -7298,24 +7678,23 @@ (if dbg-an (dbgmsg (strcat "GEOMETRIE ERFOLG: GF-Segment gebaut, neue Position=" (dbg-tostring p-aktuell)))) - ;; Kettenende-Modus (Option 4): ohne Frage direkt ES setzen. - ;; Sonst nachfragen, ob die Kette hier endet. - (if linie-ende-modus - (setq antwort "1") - (setq antwort (vfl-frage-ist-kettenende)) - ) - (cond - ((= antwort "1") - (setq es-seite (vfl-frage-es-seite nil)) - (setq frame (vfl-insert-es-element "GF" frame hz-neu winkel - (caddr (car frame)) es-seite)) - (setq fertig t)) - ((= antwort "2") (setq fertig t)) - ) + ;; Kettenende-Modus (Option 6): ES-Element direkt setzen, + ;; Winkel/Seite wurden bereits VOR dem Solve erfragt + ;; (vorab-es-seite) - kein "Ist Kettenende?" noetig, + ;; linie-ende-modus ist in diesem "Linie"-Zweig immer T. + (setq es-seite vorab-es-seite) + (setq frame (vfl-insert-es-element "GF" frame hz-neu winkel + (caddr (car frame)) es-seite)) + (setq fertig t) ) (progn ;; --- VarioFoerderer-Einheit (Umlenk..Motor, mehrsegmentig) --- ;; linie-ende-modus=T -> direkt Kettenende (Separator + ES). + ;; vorab-antwort/-es-gewuenscht/-es-seite/-ziel-punkt (oben + ;; VOR dem Solve erfragt/berechnet) werden durchgereicht, + ;; damit vfl-vf-einheit weder "Ist der Endpunkt?" nochmal + ;; fragt noch einen zweiten Klick braucht (Nutzer-Wunsch, + ;; identischer Fix wie bei "Neue Linie: Ab/Auf VF"). (if dbg-an (dbgmsg (strcat "GEOMETRIE: vfl-vf-einheit-abschluss (deltaL=" (rtos deltaL 2 0) " deltaH=" (rtos deltaH 2 0) " richtung=" richtung @@ -7323,8 +7702,10 @@ " L_GF=" (rtos L_GF 2 0) " L_VF=" (rtos L_VF 2 0) " linie-ende-modus=" (if linie-ende-modus "T" "nil") ")"))) (setq vf-einheit-res - (vfl-vf-einheit-abschluss frame hz-neu richtung winkel L_GF L_VF - linie-ende-modus nil nil)) + (vfl-vf-einheit-abschluss frame hz-neu richtung winkel L_GF L_VF lhor stufe3-position + linie-ende-modus nil nil + vorab-antwort vorab-es-gewuenscht vorab-es-seite + vorab-ziel-punkt)) (setq frame (nth 0 vf-einheit-res)) (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) (if (nth 2 vf-einheit-res) (setq fertig t)) diff --git a/Lisp/vf_standard.lsp b/Lisp/vf_standard.lsp index 80f8aec..484bfdc 100644 --- a/Lisp/vf_standard.lsp +++ b/Lisp/vf_standard.lsp @@ -214,6 +214,44 @@ ;; ============================================================ ;; TEIL 4: WINKELBERECHNUNG ;; ============================================================ +;; Laengen-/Hoehenbilanz (A/B) fuer EINEN Kandidatenwinkel des S-Bogens - +;; die Vektorzerlegung, die vf-winkel-solve danach nach L_GF/L_VF aufloest. +;; Ausgelagert aus berechne-alle-winkel (frueher inline dupliziert), damit +;; vfl-stufe3-winkel-horizontal (vf_linienzug.lsp - fixiert L_GF statt es zu +;; loesen) dieselbe Bilanz ohne Code-Duplikation nutzen kann. +;; mass1/mass2: (get-bogen-mass bogen-auf/-ab winkel), vom Aufrufer schon +;; richtungsabhaengig zugeordnet (Auf: mass1=auf, mass2=ab; Ab: umgekehrt). +;; entry-dx/entry-dz: Fussabdruck eines evtl. vorgeschalteten Uebergangsbogens +;; (schon-flach-Fall), sonst 0.0. Rueckgabe: (list A B), oder nil wenn +;; mass1/mass2 fehlen (Bogen bei diesem Winkel nicht verfuegbar). +(defun vf-winkel-AB (winkel richtung mass1 mass2 deltaL deltaH feste-hz + entry-dx entry-dz abs-dz-AUS abs-dz-EIN sin3 cos3 / + rad5 rad7 bogen-x1 bogen-z1 bogen-x2 bogen-z2 A B) + (if (or (null mass1) (null mass2)) + nil + (progn + (setq rad5 (* *vfk-gefaelle-winkel* (/ pi 180.0))) + (setq rad7 (* (float (if (= richtung "Auf") (- *vfk-gefaelle-winkel* winkel) (+ winkel *vfk-gefaelle-winkel*))) + (/ pi 180.0))) + (setq bogen-x1 (+ (* (car mass1) (cos rad5)) (* (caddr mass1) (sin rad5)))) + (setq bogen-z1 (+ (* (- (car mass1)) (sin rad5)) (* (caddr mass1) (cos rad5)))) + (setq bogen-x2 (+ (* (car mass2) (cos rad7)) (* (caddr mass2) (sin rad7)))) + (setq bogen-z2 (+ (* (- (car mass2)) (sin rad7)) (* (caddr mass2) (cos rad7)))) + (setq A (- deltaL aus-dx ein-dx bogen-x1 bogen-x2 + (* feste-hz cos3) entry-dx)) + (if (= richtung "Auf") + (setq B (+ deltaH abs-dz-AUS abs-dz-EIN (abs entry-dz) + (- bogen-z1) (- bogen-z2) + (* feste-hz sin3))) + (setq B (+ (- deltaH abs-dz-AUS abs-dz-EIN (abs entry-dz) + (* feste-hz sin3)) + bogen-z1 bogen-z2)) + ) + (list A B) + ) + ) +) + ;; feste-hz: horizontales Budget der festen Elemente (Umlenk+Motor+Separatoren). ;; nil => Standardwert FESTE_HORIZONTAL (1600, Standard-VF mit 2 Separatoren). ;; Der Linienzug uebergibt 1300 (nur 1 Separator am Einlauf, siehe vf_linienzug). @@ -229,9 +267,9 @@ ;; Aufrufer, die immer von der 3-Grad-Basis starten). (defun berechne-alle-winkel (deltaL deltaH richtung feste-hz schon-flach / winkel-list ergebnis-liste A B cos3 sin3 - bogen-x1 bogen-z1 bogen-x2 bogen-z2 + ab-erg abs-dz-AUS abs-dz-EIN L_GF L_VF cosα sinα - winkel-eff cosEff sinEff rad5 rad7 + winkel-eff cosEff sinEff winkel mass1 mass2 gueltig entry-mass entry-dx entry-dz best-winkel best-L_GF best-L_VF) @@ -272,31 +310,17 @@ (setq mass2 (get-bogen-mass bogen-auf winkel)) ) ) - (if (or (null mass1) (null mass2)) + (setq ab-erg (vf-winkel-AB winkel richtung mass1 mass2 deltaL deltaH feste-hz + entry-dx entry-dz abs-dz-AUS abs-dz-EIN sin3 cos3)) + (if (null ab-erg) (setq ergebnis-liste (cons (list winkel nil nil nil) ergebnis-liste)) (progn - (setq rad5 (* *vfk-gefaelle-winkel* (/ pi 180.0))) - (setq rad7 (* (float (if (= richtung "Auf") (- *vfk-gefaelle-winkel* winkel) (+ winkel *vfk-gefaelle-winkel*))) - (/ pi 180.0))) - (setq bogen-x1 (+ (* (car mass1) (cos rad5)) (* (caddr mass1) (sin rad5)))) - (setq bogen-z1 (+ (* (- (car mass1)) (sin rad5)) (* (caddr mass1) (cos rad5)))) - (setq bogen-x2 (+ (* (car mass2) (cos rad7)) (* (caddr mass2) (sin rad7)))) - (setq bogen-z2 (+ (* (- (car mass2)) (sin rad7)) (* (caddr mass2) (cos rad7)))) + (setq A (car ab-erg) B (cadr ab-erg)) (setq cosα (cos (* winkel (/ pi 180.0))) sinα (sin (* winkel (/ pi 180.0)))) - (setq A (- deltaL aus-dx ein-dx bogen-x1 bogen-x2 - (* feste-hz cos3) entry-dx)) (setq winkel-eff (if (= richtung "Auf") (- winkel *vfk-gefaelle-winkel*) (+ winkel *vfk-gefaelle-winkel*))) (setq cosEff (cos (* winkel-eff (/ pi 180.0))) sinEff (sin (* winkel-eff (/ pi 180.0)))) - (if (= richtung "Auf") - (setq B (+ deltaH abs-dz-AUS abs-dz-EIN (abs entry-dz) - (- bogen-z1) (- bogen-z2) - (* feste-hz sin3))) - (setq B (+ (- deltaH abs-dz-AUS abs-dz-EIN (abs entry-dz) - (* feste-hz sin3)) - bogen-z1 bogen-z2)) - ) (setq ergebnis-liste (cons (vf-winkel-solve winkel richtung A B sinα cosEff sinEff sin3 cos3) ergebnis-liste)) @@ -398,6 +422,110 @@ ) ) +;; Stufe 3 (Fallback fuer vfl-winkel-stufe-entscheidung, vf_linienzug.lsp): +;; gewinkelter S-Bogen (Tabellenwinkel) UND horizontales Fuellstueck +;; kombiniert, wenn WEDER Stufe 1 (reiner Tabellenwinkel) NOCH Stufe 2 +;; (berechne-horizontale-mitte, reine Horizontale) eine gueltige Loesung +;; liefern. GF wird FIX auf *vfl-gf-min-laenge* gesetzt (Konvention wie die +;; "climber"-Randbedingung in Modus 2/3) statt wie in Stufe 1 simultan +;; geloest - aus der Hoehenbilanz bleibt dann genau L_VF, aus der +;; Laengenbilanz der Rest als L_hor (horizontales Fuellstueck vor ODER nach +;; dem S-Bogen - Position waehlt der Anwender, siehe vfl-winkel-stufe- +;; entscheidung). +;; +;; Herleitung: vf-winkel-solve loest fuer einen Kandidatenwinkel implizit +;; das 2x2-System (durch Ruecksubstitution der dortigen Cramer-Loesung +;; rekonstruiert, winkel-eff=winkel-*vfk-gefaelle-winkel* fuer "Auf"): +;; cos3*L_GF + cosEff*L_VF = A (Laengenbilanz) +;; -sin3*L_GF + sinEff*L_VF = B (Hoehenbilanz) +;; Mit L_GF fix ergibt die Hoehenbilanz L_VF, die Laengenbilanz danach L_hor. +;; +;; Das Fuellstueck braucht sein EIGENES 3-Grad-Uebergangsbogenpaar (auf_3+ +;; ab_3, wie in berechne-horizontale-mitte) - dieser Fussabdruck ist NICHT +;; gratis und wird hier VORAB von A/B abgezogen, sonst wiederholt sich der +;; reale dZ-Fehler aus der Historie (siehe Kommentar bei vfl-winkel-stufe- +;; entscheidung, vf_linienzug.lsp). +;; +;; Absichtlich eng gehalten (kein Blackbox-Rateversuch): +;; - Nur "Auf" implementiert. "Ab" faellt laut bestehendem Kommentar bei +;; vfl-winkel-stufe-entscheidung fast immer schon unter Stufe 2 - ohne +;; einen konkreten Bedarfsfall bleibt der "Ab"-Zweig hier ein +;; dokumentiertes TODO statt einer ungetesteten Vermutung. +;; - schon-flach NICHT unterstuetzt: welcher der beiden Uebergangsboegen +;; (Fuellstueck oder S-Bogen) dabei entfallen wuerde, haengt von der - +;; zum Zeitpunkt der Berechnung noch unbekannten - Position (vor/nach) +;; ab. Stufe 3 liefert in diesem Fall bewusst KEIN Ergebnis statt eine +;; der beiden Moeglichkeiten zu raten. +;; +;; Rueckgabe: (list winkel L_GF L_VF L_hor gueltig) beim ersten (flachsten) +;; Kandidatenwinkel mit L_VF>=0 UND L_hor>=0, sonst (list nil nil nil nil nil). +(defun vfl-stufe3-winkel-horizontal (deltaL deltaH richtung feste-hz schon-flach / + winkel-list mass-f1 mass-f2 sin3 cos3 + bogen-x1f bogen-z1f bogen-x2f bogen-z2f + A-frei B-frei mass1 mass2 ab-erg A B + winkel-eff sinEff cosEff gf-min + L_GF L_VF L_hor winkel treffer) + (if (or (/= richtung "Auf") schon-flach) + (list nil nil nil nil nil) + (progn + (setq gf-min (if (and (boundp '*vfl-gf-min-laenge*) *vfl-gf-min-laenge*) + *vfl-gf-min-laenge* 400.0)) + (setq sin3 (sin (* *vfk-gefaelle-winkel* (/ pi 180.0))) + cos3 (cos (* *vfk-gefaelle-winkel* (/ pi 180.0)))) + (setq mass-f1 (get-bogen-mass bogen-auf (fix *vfk-gefaelle-winkel*))) + (setq mass-f2 (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*))) + (if (or (null mass-f1) (null mass-f2)) + (list nil nil nil nil nil) + (progn + ;; Fuellstueck-Uebergangsboegen (unabhaengig vom Kandidatenwinkel): + ;; Bogen1 (auf_3, Eintritt bei 3 Grad, wie in berechne-horizontale- + ;; mitte), Bogen2 (ab_3, Eintritt bei 0 Grad - keine Rotation noetig). + (setq bogen-x1f (+ (* (car mass-f1) cos3) (* (caddr mass-f1) sin3))) + (setq bogen-z1f (+ (* (- (car mass-f1)) sin3) (* (caddr mass-f1) cos3))) + (setq bogen-x2f (car mass-f2)) + (setq bogen-z2f (caddr mass-f2)) + (setq winkel-list (ssg-cfg-or "vario" "bogen_winkel" + '(3 6 9 12 15 18 21 27 33 39 45 51))) + (setq L_GF gf-min treffer nil) + (foreach winkel winkel-list + (if (not treffer) + (progn + (setq mass1 (get-bogen-mass bogen-auf winkel)) + (setq mass2 (get-bogen-mass bogen-ab winkel)) + (setq ab-erg (vf-winkel-AB winkel richtung mass1 mass2 deltaL deltaH + feste-hz 0.0 0.0 (abs aus-dz) (abs ein-dz) sin3 cos3)) + (if ab-erg + (progn + (setq A (car ab-erg) B (cadr ab-erg)) + ;; Fuellstueck-Fussabdruck vorab abziehen - siehe + ;; Kopfkommentar (NICHT gratis). + (setq A-frei (- A bogen-x1f bogen-x2f)) + (setq B-frei (- B bogen-z1f bogen-z2f)) + (setq winkel-eff (- winkel *vfk-gefaelle-winkel*)) + (setq sinEff (sin (* winkel-eff (/ pi 180.0))) + cosEff (cos (* winkel-eff (/ pi 180.0)))) + (if (> (abs sinEff) 0.0001) + (progn + (setq L_VF (/ (+ B-frei (* sin3 L_GF)) sinEff)) + (setq L_hor (- A-frei (* cos3 L_GF) (* cosEff L_VF))) + (if (and (numberp L_VF) (numberp L_hor) + (>= L_VF 0.0) (>= L_hor 0.0)) + (setq treffer (list winkel L_GF L_VF L_hor T)) + ) + ) + ) + ) + ) + ) + ) + ) + (if treffer treffer (list nil nil nil nil nil)) + ) + ) + ) + ) +) + ;; ============================================================ ;; TEIL 5: BERECHNE-WRAPPER (Schnittstelle fuer Registry) ;; ============================================================ diff --git a/cfg/Merkmalsstamm_in_Sivas.xlsx b/cfg/Merkmalsstamm_in_Sivas.xlsx new file mode 100644 index 0000000..ad4c3ba Binary files /dev/null and b/cfg/Merkmalsstamm_in_Sivas.xlsx differ diff --git a/lang/de_DE.json b/lang/de_DE.json index ee52218..2281bc2 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -46,6 +46,7 @@ "vfl-alert-winkel-ungueltig": "Neigungswinkel ungueltig: %1 Grad. Erlaubt: 0 < Winkel <= %2 Grad.", "vfl-prompt-hoehe-linienendpunkt": "\nHoehe (Z) des Linienendpunkts [%1]: ", "vfl-prompt-hoehe-linienendpunkt-2": "\nHoehe (Z) des Linienendpunkts (Ab: %1 | Auf: %2) [%1]: ", + "vfl-prompt-hoehe-linienendpunkt-3": "\nHoehe (Z) des Linienendpunkts (Ab: %1 | Auf: %2 | gepickt: %3) [%3]: ", "vfl-hinweis-hoehe-vorschlag-fehlt": "\n(Hinweis: In diese Richtung ist bei dieser Segmentlaenge kein baubarer Vorschlag gefunden - bitte Wert manuell pruefen.)", "vfl-alert-gf-kann-nicht-steigen": "Gefaellestrecke (GF) kann nicht steigen - bitte tiefere Zielhoehe waehlen oder Ab/Auf VF benutzen.", "vfl-alert-gefaelle-zu-steil": "Gefaelle zu steil fuer GF: %1 Grad (erlaubt max %2 Grad).\nBitte kleinere Hoehendifferenz waehlen oder Ab/Auf VF benutzen.", @@ -389,6 +390,10 @@ "vfl-stufe2-minh": "\n [Fixwert ohne GF-Bogen] Rest als gerader VF-Anteil (hoehenneutral): diese Aufteilung ergibt IMMER genau %1 mm Hoehenaenderung, unabhaengig von der Laenge - Zielhoehe verlangt aber %2 mm, passt also nur bei exakter Uebereinstimmung", "vfl-stufe2-minh-unmoeglich": "\n [Fixwert ohne GF-Bogen] Nicht per Zielhoehe loesbar: schon ohne geraden VF-Anteil und ohne GF-Bogen betraegt der feste Fussabdruck %1 mm - die Strecke bietet aber nur %2 mm", "vfl-stufe2-ungueltig": "\n Stufe 2 (horizontal+GF) war ungueltig - es wurde stattdessen Stufe 1 (VF-Winkel-Suche) versucht (siehe Tabelle oben).", + "vfl-stufe3-start": "\n\n[Diagnose Stufe 3 - Winkel + horizontales Fuellstueck] Stufe 1 und Stufe 2 gescheitert, versuche Kombination aus steilem S-Bogen und horizontalem Reststueck ...", + "vfl-frage-stufe3-position": "\n\nDie Strecke braucht einen S-Bogen KOMBINIERT mit einem geraden horizontalen Stueck. Wo soll das horizontale Stueck sitzen?", + "vfl-opt-stufe3-vor": "\n 1 - Horizontales Stueck VOR dem S-Bogen", + "vfl-opt-stufe3-nach": "\n 2 - Horizontales Stueck NACH dem S-Bogen", "vfl-frage-gf2-minimum": "\n Berechnetes GF2=%1 mm liegt unter der ueblichen Mindestlaenge (%2 mm).\n Ja = mit den exakt berechneten %1 mm bauen (Zielpunkt wird exakt getroffen).\n Nein = auf %2 mm auffuellen (Zielpunkt verschiebt sich entsprechend weiter).", "vfl-fehler-rest-nicht-baubar": "\n FEHLER: Rest nicht baubar (zu steil/zu kurz fuer Schwanz Motor+GF2+Separator+ES). Bitte weiter zeichnen oder flacher planen.", "vfl-fuege-block-ein": "\n Fuege %1 ein ...", @@ -398,8 +403,10 @@ "vfl-kettenende-modus-ziel": "\n>>> Kettenende-Modus: Ziel dL=%1 mm, dH=%2 mm (%3). GF2 variiert mit dem Winkel.", "vfl-prompt-hoehe-endpunkt": "\nHoehe (Z) des Endpunkts [%1]: ", "vfl-prompt-hoehe-endpunkt-2": "\nHoehe (Z) des Endpunkts (Ab: %1 | Auf: %2) [%1]: ", + "vfl-prompt-hoehe-endpunkt-3": "\nHoehe (Z) des Endpunkts (Ab: %1 | Auf: %2 | gepickt: %3) [%3]: ", "vfl-prompt-hoehe-kettenende": "\nHoehe (Z) des Kettenendes (ES-Element) [%1]: ", "vfl-prompt-hoehe-kettenende-2": "\nHoehe (Z) des Kettenendes (ES-Element) (Ab: %1 | Auf: %2) [%1]: ", + "vfl-prompt-hoehe-kettenende-3": "\nHoehe (Z) des Kettenendes (ES-Element) (Ab: %1 | Auf: %2 | gepickt: %3) [%3]: ", "vfl-prompt-wahl-bis-n": "\nIhre Wahl (1-%1) [1]: ", "vfl-frage-auf-ab-richtung": "\n\nKette kommt flach an - keine Zielhoehe verfuegbar. Richtung waehlen:", "vfl-opt-richtung-auf": "\n 1 - Auf", diff --git a/lang/en_GB.json b/lang/en_GB.json index be66089..a39713f 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -46,6 +46,7 @@ "vfl-alert-winkel-ungueltig": "Incline angle invalid: %1 degrees. Allowed: 0 < angle <= %2 degrees.", "vfl-prompt-hoehe-linienendpunkt": "\nHeight (Z) of the line end point [%1]: ", "vfl-prompt-hoehe-linienendpunkt-2": "\nHeight (Z) of the line end point (down: %1 | up: %2) [%1]: ", + "vfl-prompt-hoehe-linienendpunkt-3": "\nHeight (Z) of the line end point (down: %1 | up: %2 | picked: %3) [%3]: ", "vfl-hinweis-hoehe-vorschlag-fehlt": "\n(Note: no buildable suggestion found for that direction at this segment length - please check the value manually.)", "vfl-alert-gf-kann-nicht-steigen": "Gravity section (GF) cannot rise - please pick a lower target height or use down/up VF.", "vfl-alert-gefaelle-zu-steil": "Slope too steep for GF: %1 degrees (max allowed %2 degrees).\nPlease pick a smaller height difference or use down/up VF.", @@ -390,6 +391,10 @@ "vfl-stufe2-minh": "\n [Fixed value without GF bend] Remainder as a straight VF section (height-neutral): this split ALWAYS results in exactly %1 mm of height change, regardless of length - but the target height calls for %2 mm, so this only fits on an exact match", "vfl-stufe2-minh-unmoeglich": "\n [Fixed value without GF bend] Not solvable via target height: even without a straight VF section and without a GF bend, the fixed footprint is already %1 mm - the distance only offers %2 mm", "vfl-stufe2-ungueltig": "\n Stage 2 (horizontal+GF) was invalid - stage 1 (VF angle search) was tried instead (see table above).", + "vfl-stufe3-start": "\n\n[Diagnosis stage 3 - angle + horizontal filler] Stage 1 and stage 2 failed, trying a combination of a steep S-bend and a horizontal remainder piece ...", + "vfl-frage-stufe3-position": "\n\nThe section needs an S-bend COMBINED with a straight horizontal piece. Where should the horizontal piece go?", + "vfl-opt-stufe3-vor": "\n 1 - Horizontal piece BEFORE the S-bend", + "vfl-opt-stufe3-nach": "\n 2 - Horizontal piece AFTER the S-bend", "vfl-frage-gf2-minimum": "\n Calculated GF2=%1 mm is below the usual minimum length (%2 mm).\n Yes = build with the exact calculated %1 mm (hits the target point exactly).\n No = pad up to %2 mm (target point shifts further accordingly).", "vfl-fuege-block-ein": "\n Inserting %1 ...", "vfl-hinweis-gf2-minimum": "\n NOTE: calculated GF2=%1 mm < minimum %2 mm -> raised to minimum (ES therefore ends a bit further on; see actual-vs-target report).", @@ -398,8 +403,10 @@ "vfl-kettenende-modus-ziel": "\n>>> Chain-end mode: target dL=%1 mm, dH=%2 mm (%3). GF2 varies with the angle.", "vfl-prompt-hoehe-endpunkt": "\nHeight (Z) of the end point [%1]: ", "vfl-prompt-hoehe-endpunkt-2": "\nHeight (Z) of the end point (down: %1 | up: %2) [%1]: ", + "vfl-prompt-hoehe-endpunkt-3": "\nHeight (Z) of the end point (down: %1 | up: %2 | picked: %3) [%3]: ", "vfl-prompt-hoehe-kettenende": "\nHeight (Z) of the chain end (ES element) [%1]: ", "vfl-prompt-hoehe-kettenende-2": "\nHeight (Z) of the chain end (ES element) (down: %1 | up: %2) [%1]: ", + "vfl-prompt-hoehe-kettenende-3": "\nHeight (Z) of the chain end (ES element) (down: %1 | up: %2 | picked: %3) [%3]: ", "vfl-prompt-wahl-bis-n": "\nYour choice (1-%1) [1]: ", "vfl-frage-auf-ab-richtung": "\n\nChain arrives flat - no target height available. Choose direction:", "vfl-opt-richtung-auf": "\n 1 - Up",