diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp index b03353a..6828ad9 100644 --- a/Lisp/vf_linienzug.lsp +++ b/Lisp/vf_linienzug.lsp @@ -11149,7 +11149,9 @@ (setq seg (nth i plan)) (cond ((= (car seg) "Linie") - (setq w (nth 4 seg) dL (caddr seg)) + ;; Eck-Zuschlag wie im Bau (vfl-eck-extra) - sonst rechnet die Hoehe mit + ;; einer anderen Laenge als gebaut wird (vgl. dZ-Sprung 2026-10-26). + (setq w (nth 4 seg) dL (+ (caddr seg) (vfl-eck-extra i))) (if (= i last-idx) (setq dL (- dL *vfk-separator-laenge* ein-fp))) (setq dL (max *vfk-restlaenge-min-clamp* dL)) (setq rad (* (float w) (/ pi 180.0))) @@ -11267,6 +11269,159 @@ ) ) +;; --- Eck-Korrektur: Eck-Bauteil an die beiden GERADEN anlegen --- +;; Leitsatz (Nutzer-Vorgabe 2026-10-26): der gezeichnete Bogen sagt nur, WO +;; die Ecke liegt und WIE HERUM es geht. Sein Radius und seine Masse sind +;; KEINE Vorgabe - massgeblich sind die beiden Geraden. Die MITTELLINIE des +;; Eck-Bauteils (Vario-Kurve bzw. Gefaellebogen) muss die beiden Geraden +;; beruehren: KS_EIN liegt auf der Geraden davor UND zeigt in deren Richtung, +;; KS_AUS auf der Geraden danach und in deren Richtung. Dann laeuft die Kette +;; knickfrei und ohne Seitenversatz durch die Ecke - egal, welchen Radius +;; jemand gezeichnet hat. +;; Weil zwei nicht-parallele Geradenrichtungen die Ebene aufspannen, ist die +;; Bedingung durch zwei Laengenzuschlaege EXAKT erfuellbar; an einer Ecke +;; bleibt also kein Querrest uebrig. +;; +;; Umgesetzt als Differenz der TANGENTENABSTAENDE zur Ecke. Die Planlaengen +;; der Geraden sind bis zum GEZEICHNETEN Tangentenpunkt gemessen, das Bauteil +;; setzt an seinem EIGENEN an - die Differenz bekommt jede der beiden Geraden +;; als Budget-Zuschlag. Rechnerisch ist das die Zerlegung +;; d = a*u_ein + b*u_aus, d = gezeichnete Sehne - Bauteil-Fussabdruck +;; auf die Richtungen der beiden Geraden (eindeutig, solange die Ecke einen +;; Schwenk hat). Der gezeichnete Radius kuerzt sich dabei vollstaendig heraus: +;; mit R300 statt R500 aendern sich Planlaenge UND Sehne gemeinsam. +;; Ohne diese Korrektur verschiebt die Differenz ALLES Nachfolgende und taucht +;; erst am Kettenende auf (real 2026-10-26: 132.6 mm von 155 mm Gesamtfehler - +;; Pfad R500 = Tangentenabstand 500, Vario-Kurve = 406.2). +;; +;; Grenze: beruehren kann das Bauteil nur beide Geraden, wenn sein Schwenk zum +;; Eckwinkel passt (90-Grad-Kurve an 90-Grad-Ecke). Weichen sie voneinander +;; ab, bleibt ein Rest, den keine Laengenaenderung aufheben kann. +;; +;; GEMESSEN, nicht gerechnet: die Sehne steht als (caddr seg) im Plan (aus der +;; Zeichnung), der Bauteil-Fussabdruck kommt ueber vfl-zsolve-es-vektor aus der +;; realen Blockgeometrie. Darum greift die Korrektur fuer jeden Eckwinkel und +;; jeden gezeichneten Radius, ohne gepflegte Tabelle. +;; Rueckgabe: Alist (segment-index . zuschlag-mm). +(defun vfl-eck-korrekturen (plan n / korr i seg hz w seite blk vek + sgn th s-along s-perp d-along d-perp a b j + jp bm neig) + (setq korr '() i 0) + (while (< i n) + (setq seg (nth i plan)) + (if (and (= (car seg) "Bogen") (nth 3 seg) (nth 4 seg)) + (progn + (setq hz (cadr seg) + w (float (nth 3 seg)) + seite (nth 4 seg) + sgn (if (= seite "links") 1.0 -1.0) + th (* sgn w (/ pi 180.0))) + ;; Sehne des GEZEICHNETEN Bogens im Fahrtrichtungs-System der + ;; einlaufenden Geraden - ihre Richtung ist der halbe Schwenk. + (setq s-along (* (caddr seg) (cos (/ th 2.0))) + s-perp (* (caddr seg) (sin (/ th 2.0)))) + ;; Blocknamen NICHT selbst zusammensetzen - gf-bogen-blockname ist die + ;; eine Stelle, die das Namensschema der Gefaelleboegen kennt (Radius, + ;; Sonderfaelle). Ein handgebauter Name traf den Block nicht, die + ;; Messung kam leer zurueck und die Ecke blieb unkorrigiert - real + ;; 2026-10-26: 2.4 mm Rest, weil der 90-Grad-Gefaellebogen geneigt + ;; eingebaut nur 497.8 statt 500 mm Grundriss-Fussabdruck hat. + (setq blk (if (= (nth 5 seg) "Vario-Kurve") + (strcat "Vario_Kurve_" seite "_" (itoa (fix w)) "_TEF_" + (if (nth 6 seg) (nth 6 seg) "aussen")) + (gf-bogen-blockname (fix w) seite))) + (setq vek (vfl-zsolve-es-vektor blk hz)) + ;; GF-Boegen werden GENEIGT eingebaut (Gefaellewinkel der Strecke) - + ;; ihr GRUNDRISS-Fussabdruck ist dadurch kuerzer als flach gemessen. + ;; Real 2026-10-26: flach 500.0, bei 3 Grad 497.8; der Unterschied + ;; blieb als 2.4 mm am Kettenende stehen. Vario-Kurven werden + ;; waagerecht eingebaut und bleiben unberuehrt (dort war die Kette + ;; bereits auf 0.2 mm genau). Projiziert mit dem vorhandenen + ;; vfl-projiziere-neigung, Neigung = Gefaellewinkel der Geraden davor + ;; (sonst der Standardwert, mit dem auch hinter einer VF-Einheit + ;; gebaut wird). + (if (and vek (/= (nth 5 seg) "Vario-Kurve")) + (progn + (setq jp (1- i)) + (while (and (>= jp 0) (/= (car (nth jp plan)) "Linie")) (setq jp (1- jp))) + (setq neig (if (and (>= jp 0) + (numberp (nth 4 (nth jp plan))) + (> (nth 4 (nth jp plan)) 0)) + (float (nth 4 (nth jp plan))) + (float *vfk-gefaelle-winkel*))) + (setq bm (gf-bogen-masse (fix w) seite)) + (if bm + (setq vek (list (car (vfl-projiziere-neigung (car vek) (cadr bm) neig)) + (cadr vek)))))) + (setq d-along (if vek (vfl-eck-zerlegen s-along s-perp + (car vek) (cadr vek) th) nil)) + (if d-along + (progn + (setq a (car d-along) b (cadr d-along)) + ;; a auf die Gerade DAVOR, b auf die Gerade DANACH + (setq j (1- i)) + (while (and (>= j 0) (/= (car (nth j plan)) "Linie")) (setq j (1- j))) + (if (>= j 0) (setq korr (cons (cons j a) korr))) + (setq j (1+ i)) + (while (and (< j n) (/= (car (nth j plan)) "Linie")) (setq j (1+ j))) + (if (< j n) (setq korr (cons (cons j b) korr))))))) + (setq i (1+ i))) + korr) + +;; Laengs-Reservierung der letzten Geraden, damit der ES-KS_AUS auf der +;; KS_AUS-ACHSE durch den Zielpunkt landet (reiner Kern, getestet). +;; Nutzer-Vorgabe 2026-10-26: QUER zur KS_AUS-Achse MUSS 0 sein, LAENGS darf +;; abweichen (das ist die Ausladung des Elements), dZ MUSS 0 sein. +;; Ein reiner Laengs-Abzug (= along) stellt den Endpunkt zwar in Fahrtrichtung +;; richtig, laesst aber den bauteileigenen Querversatz stehen - real 45.7 mm. +;; Gesucht ist die Strecke entlang der Fahrtrichtung, die ihn AUFHEBT: +;; KS_AUS = P + V_es, Bedingung (KS_AUS - Ziel) . n_a = 0 +;; -> Reserve = (V_es . n_a) / (dp . n_a) +;; dp = Fahrtrichtung der letzten Geraden, n_a = Normale der KS_AUS-Achse +;; (Achsrichtung = hz + es-turn). Die Gerade wird dadurch laenger als die +;; Planlaenge - zulaessig, weil laengs abweichen darf; ihre Mittellinie bleibt +;; dabei auf der gezeichneten Linie. Da derselbe Wert auch in den Rueckanker +;; geht, bleibt dZ 0 (vgl. Leitsatz: eine Groesse, beide Bilanzen). +;; Faellt die Achse mit der Fahrtrichtung zusammen (es-turn = 0), ist der +;; Nenner 0 - dann gibt es keinen Querversatz und along ist richtig. +;; along/perp: ES-Fussabdruck im Fahrtrichtungs-System (vfl-zsolve-es-vektor). +(defun vfl-es-achs-reserve (along perp hz es-turn / rad ua dpx dpy nax nay vx vy nenner) + (setq rad (/ pi 180.0)) + (setq ua (* (+ (float hz) (float es-turn)) rad)) + (setq nax (- (sin ua)) nay (cos ua)) + (setq dpx (cos (* (float hz) rad)) dpy (sin (* (float hz) rad))) + ;; V_es aus (along perp) ins Weltsystem: x^ = dp, y^ = senkrecht dazu + (setq vx (- (* along dpx) (* perp dpy)) + vy (+ (* along dpy) (* perp dpx))) + (setq nenner (+ (* dpx nax) (* dpy nay))) + (if (< (abs nenner) 1e-6) + along + (/ (+ (* vx nax) (* vy nay)) nenner))) + +;; Reiner Kern der Eck-Korrektur (in tests/test_unit.lsp geprueft): +;; Differenz zwischen gezeichneter Sehne (s-*) und Bauteil-Fussabdruck (b-*), +;; beide im Fahrtrichtungs-System der EINLAUFENDEN Geraden, zerlegt auf +;; d = a*u_ein + b*u_aus mit u_ein = (1,0) und u_aus = (cos th, sin th). +;; th = Schwenk der Ecke im Bogenmass (positiv = links). Rueckgabe (a b), +;; nil bei th = 0 (keine Ecke - dann ist die Zerlegung nicht eindeutig). +(defun vfl-eck-zerlegen (s-along s-perp b-along b-perp th / a b) + (if (< (abs (sin th)) 1e-9) + nil + (progn + (setq b (/ (- s-perp b-perp) (sin th))) + (setq a (- (- s-along b-along) (* b (cos th)))) + (list a b)))) + +;; Summe der Eck-Zuschlaege fuer ein Segment (0.0, wenn keiner vorliegt). +;; Globale Tabelle statt zusaetzlichem Parameter, damit die Signatur von +;; vfl-zsolve-rueckanker unveraendert bleibt (AutoLISP verlangt exakte +;; Argumentzahl - eine Erweiterung muesste jeden Aufrufer nachziehen). +(defun vfl-eck-extra (idx / s) + (setq s 0.0) + (if (boundp '*vfl-eck-korr*) + (foreach e *vfl-eck-korr* (if (= (car e) idx) (setq s (+ s (cdr e)))))) + s) + ;; Letzte Fueller-Laenge so, dass der Endpunkt auf der ES-KS_AUS-Achse liegt. ;; pt = Fueller-Start (flach 0 Grad), seg-hz = letzte Richtung, es-block = ES-Block, ;; gf2 = erwartete GF2-Laenge, rad3 = 3 Grad (rad). Modell: der Schwanz (Fueller + @@ -11322,7 +11477,8 @@ hor-koerper-len hor-pairs flat-p flat-zone-erste fill-len wahl3 rad3v feste-vf dH-adj dir-x dir-y fill-D end-hz laengs-wert quer-wert soll-korrigiert - es-vek-fix es-along-fix + es-vek-fix es-along-fix as-along-fix m-ueb + abschluss-fp m-ab3 es-vek-back ein-fp-mess gf-total br-gf-mode br-gf2-exp filler-A-len filler-a-done as-vorhanden es-vorhanden dbg-an dbg-old-error m2-lauf-antwort m2-offenes-ende es-frueh-abgelehnt @@ -11669,11 +11825,24 @@ (if dz (setq z-junction (- z-junction dz))) (setq i (1- i))) + ;; Eck-Zuschlaege EINMAL bestimmen (siehe vfl-eck-korrekturen) - sie gehen + ;; in JEDES Laengenbudget ein: Spannweite, Kletterer, Fueller, Rueckanker. + ;; Nur so koennen Laengen- und Hoehenbilanz nicht auseinanderlaufen. + (setq *vfl-eck-korr* (vfl-eck-korrekturen plan n)) + ;; Auch kleine Zuschlaege melden: bleibt eine Ecke unkorrigiert (Block nicht + ;; messbar), FEHLT die Zeile - und genau das ist die Information, die man + ;; beim Suchen eines Millimeter-Rests braucht. + (foreach e *vfl-eck-korr* + (if (> (abs (cdr e)) 0.05) + (princ (ssg-textf "vfl-m2-eck-korrektur" + (list (rtos (cdr e) 2 1) (itoa (1+ (car e)))))))) + ;; Bruecken-Spannweite = Summe der VF-Geraden-Planlaengen im Lauf. (setq span-bruecke 0.0 i vf-start) (while (<= i vf-ende) (setq seg (nth i plan)) - (if (= (car seg) "Linie") (setq span-bruecke (+ span-bruecke (caddr seg)))) + (if (= (car seg) "Linie") + (setq span-bruecke (+ span-bruecke (caddr seg) (vfl-eck-extra i)))) (setq i (1+ i))) (setq dH-bruecke (- z-front z-junction)) @@ -11828,6 +11997,33 @@ (progn (if (< first-line 0) (setq first-line i)) (setq last-line i))) (setq i (1+ i))) + ;; --- ES-Laengsanteil EINMAL messen, fuer LAENGE *und* HOEHE --- + ;; Der Endpunkt der letzten Geraden ist der KS_AUS des ES-Elements, nicht + ;; dessen Anfang - vom Budget dieses Segments gehen also Separator UND der + ;; ES-Laengsanteil ab. Entscheidend ist, dass BEIDE Bilanzen denselben Wert + ;; benutzen: das Laengenbudget im B4-Bau und das Hoehenbudget in + ;; vfl-zsolve-rueckanker. Liefen sie auseinander, stimmte die Laenge und die + ;; Hoehe kippte um Differenz*tan(Neigung) - real am 2026-10-26 gesehen: + ;; GF 820 mm kuerzer, dZ sprang von 0.1 auf 43 mm (= 820*sin(3 Grad)). + ;; GEMESSEN aus der realen Blockgeometrie statt aus dem Block-dx `ein` - + ;; letzteres kennt den Schwenk des 30-Grad-Elements nicht und lag ~520 mm + ;; daneben. `ein` bleibt nur Rueckfall, wenn der Block nicht lesbar ist. + (setq es-vek-back + (if (and es-vorhanden (>= last-line 0)) + (vfl-zsolve-es-vektor (strcat "ES_Element_" (vfl-es-winkel) "_" es-seite) + (cadr (nth last-line plan))) + nil)) + ;; NICHT der reine Laengsanteil (car), sondern die Reserve, die zusaetzlich + ;; den Querversatz aufhebt - siehe vfl-es-achs-reserve. + (setq ein-fp-mess + (cond (es-vek-back + (vfl-es-achs-reserve (car es-vek-back) (cadr es-vek-back) + (cadr (nth last-line plan)) + (vf-element-plan-turn + (strcat "ES_Element_" (vfl-es-winkel) "_" es-seite)))) + (es-vorhanden ein) + (t 0.0))) + ;; --- B1: FRONT-Lauf bauen (Segmente 0 .. vf-start-1) -> Front-Anker MESSEN --- (princ (ssg-text "vfl-m3-phase-b1")) (setq i 0) @@ -11893,10 +12089,30 @@ (vfl-pfad-as-vf-einfuegen startpunkt hz as-seite) (make-frame-from-dir startpunkt (hz-winkel->xu hz 0.0))))) (setq z-front (caddr (car frame))) + ;; --- AS-Fussabdruck ins LAENGENbudget (GEMESSEN, nicht gerechnet) --- + ;; Das AS-Element steht SELBST auf dem ersten Segment des Laufs; sein + ;; Laengsanteil gehoert nicht mehr der Bruecke. Identische Formel wie die + ;; Trimmung der ersten Geraden im Front-Lauf oben (Projektion von + ;; KS_AUS - startpunkt auf die Fahrtrichtung) - dort laeuft sie nur, wenn + ;; dem VF-Lauf ueberhaupt Segmente vorausgehen; bei vf-start = 0 ist die + ;; Schleife leer und der Abzug fiel bisher komplett aus. Folge: der Solver + ;; verteilte die VOLLE Planlaenge HINTER dem AS, die Kette ueberschoss um + ;; den AS-Fussabdruck (realer Fall 2026-10-26). + ;; GEMESSEN statt aus der Blockgeometrie gerechnet: das Block-Mass stimmt + ;; nach dem Achs-Versatz des AS nicht mehr (ein gerechneter Wert lag + ;; 184 mm daneben und machte das erste Segment sichtbar zu kurz). + ;; Ohne AS-Element faellt der Wert von allein auf 0 - dann ist (car frame) + ;; gleich startpunkt -, eine Abfrage auf as-vorhanden braucht es nicht. + (setq as-along-fix + (+ (* (- (car (car frame)) (car startpunkt)) (cos (* hz (/ pi 180.0)))) + (* (- (cadr (car frame)) (cadr startpunkt)) (sin (* hz (/ pi 180.0)))))) + (if (> (abs as-along-fix) 0.01) + (princ (ssg-textf "vfl-m2-as-fussabdruck" + (list (rtos as-along-fix 2 1) (itoa (1+ vf-start)))))) ;; --- B2: Back-Abstieg -> Junction; Kletterer + Horizontal-Mitte bestimmen --- (setq z-junction (+ (float end-hoehe) - (vfl-zsolve-rueckanker plan (1+ vf-ende) n last-line ein))) + (vfl-zsolve-rueckanker plan (1+ vf-ende) n last-line ein-fp-mess))) ;; Offenes Ende (kein Motor/Separator/ES danach) ODER Motor+GF2 OHNE ES ;; danach (Lauf-Abschluss "Motorstation", es-vorhanden nil - Motor selbst ;; ist bereits ueber dH-adj/*vfk-bruecke-korrektur* separat eingerechnet, @@ -11947,8 +12163,10 @@ ;; NUR die laengste VF-Gerade klettert (siehe Regel oben) - jede ;; weitere wird flacher Fueller. (if (= i longest-idx) - (setq climbers (cons i climbers) climber-span (+ climber-span (caddr seg))) - (setq nonclimber-len (+ nonclimber-len (caddr seg)) nonclimber-cnt (1+ nonclimber-cnt)))) + (setq climbers (cons i climbers) + climber-span (+ climber-span (caddr seg) (vfl-eck-extra i))) + (setq nonclimber-len (+ nonclimber-len (caddr seg) (vfl-eck-extra i)) + nonclimber-cnt (1+ nonclimber-cnt)))) ((= (car seg) "Bogen") (setq nkurve (1+ nkurve) kurve-chords (+ kurve-chords (caddr seg))))) (setq i (1+ i))) @@ -11988,6 +12206,27 @@ (m2-kein-gf1-vorne *vfk-stations-laenge*) (t (+ *vfk-stations-laenge* *vfk-separator-laenge*)))) (setq es-along-fix 0.0) + ;; --- Abschluss-Hardware der VF-Einheit ins LAENGENbudget --- + ;; Spiegelbild zum AS-Fussabdruck am anderen Ende des Laufs: MIT flacher + ;; Zone sitzt die Motorstation nicht im Kletterer, sondern auf dem LETZTEN + ;; Segment des Laufs - zusammen mit dem Abschlussbogen davor. Genau diese + ;; beiden fehlten im Budget: feste-vf traegt die Motorstation nur im Zweig + ;; OHNE flache Zone (siehe unten), und die Fuellerlaenge war schlicht die + ;; volle Planlaenge. Realer Fall 2026-10-26: Segment 3 (Plan 4296 mm) wurde + ;; 4990.55 mm lang = 4296 + 195.73 (Bogen ab_3) + 499.32 (Motorstation). + ;; Der Ueberschuss lief laengs zur ES-Achse und wurde darum von der Zeile + ;; "Soll (ES-korrigiert)" verdeckt - sichtbar war nur die Querabweichung. + ;; Nicht doppelt zu uebergang-fp: das sind die Uebergangsboegen AM + ;; KLETTERER, hier geht es um den Bogen VOR der Motorstation. + (setq abschluss-fp 0.0) + (if (and (> hor-pairs 0) (not m2-offenes-ende)) + (progn + (setq m-ab3 (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*))) + (setq abschluss-fp + (+ (* *vfk-stations-laenge* (cos rad3v)) + (if m-ab3 (car m-ab3) 0.0))) + (princ (ssg-textf "vfl-m2-abschluss-fussabdruck" + (list (rtos abschluss-fp 2 1) (itoa (1+ vf-ende))))))) (if (> hor-pairs 0) (setq feste-vf entry-fixed dH-adj (- dH-bruecke (+ (* *vfk-stations-laenge* (sin rad3v)) *vfk-bruecke-korrektur*))) ; Motor + auf_3/ab_3 @@ -12008,7 +12247,18 @@ ;; Test dieses Fixes (dZ=38.6mm, vorher exakt 0). (setq feste-vf (+ entry-fixed (if m2-offenes-ende 0.0 *vfk-stations-laenge*)) dH-adj dH-bruecke) - (if es-vorhanden + ;; NUR wenn das ES unmittelbar auf den VF-Lauf folgt, also der Lauf das + ;; Pfadende ist. Liegt ein Back-Lauf dazwischen (Segmente nach vf-ende), + ;; traegt dessen letzte Gerade den ES-Fussabdruck bereits selbst + ;; (ein-fp-mess im B4-Zweig) - ihn hier nochmal vom VF-Lauf abzuziehen, + ;; verkuerzt das FALSCHE Segment um die volle ES-Laenge. + ;; Realer Fall 2026-10-26: derselbe Pfad, aber Segment 2 als GF-Bogen + ;; statt Vario-Kurve gewaehlt -> keine flache Zone -> dieser Zweig + ;; griff, und Segment 1 wurde um 875.8 mm zu kurz (Abweichung 878.2 mm + ;; quer). Mit Vario-Kurve lief dieselbe Kette auf 0.2 mm genau. + ;; Der hz von vf-ende waere ausserdem die falsche Fahrtrichtung: das ES + ;; steht dann am Ende des Back-Laufs, nicht am Ende des VF-Laufs. + (if (and es-vorhanden (= vf-ende (1- n))) (progn (setq es-vek-fix (vfl-zsolve-es-vektor (strcat "ES_Element_" (vfl-es-winkel) "_" es-seite) @@ -12033,20 +12283,49 @@ ;; Kletterer, wenn eine flache Zone vorausgeht. ;; Nur die LAENGE wird korrigiert - die Hoehe dieser Boegen steckt bereits ;; in *vfk-bruecke-korrektur* (siehe dH-adj oben). + ;; Die Masse werden EINMAL geholt und gegen nil geprueft. Beides war vorher + ;; nicht so: get-bogen-mass wurde fuer dieselbe Suche zweimal aufgerufen + ;; (car/caddr), meldete einen Fehlschlag also doppelt, und das nil ging + ;; ungeprueft an vfl-projiziere-neigung, wo (cos rad) den ganzen Lauf + ;; abbrach (realer Fall 2026-10-26: "Winkel 3 nicht in Bogen-Tabelle" + ;; zweimal, danach Call-Stack-Abbruch). bogen-auf/bogen-ab entstehen beim + ;; Init nur aus den BLOECKEN, die ladbar UND messbar sind (vf_standard.lsp) + ;; - fehlt eine DWG, fehlt der Eintrag. Das darf den Bau nicht toeten: + ;; ohne Mass bleibt der Uebergangsbogen mit 0 im Budget (die Kette wird um + ;; seine Laenge zu lang, entsteht aber und ist nachbesserbar) und der + ;; Nutzer bekommt gesagt, welcher Block fehlt. (setq uebergang-fp 0.0) (if (> n-climb 0) (progn (if (< (car climbers) vf-ende) ; flache Zone FOLGT -> flach-ein - (setq uebergang-fp - (+ uebergang-fp - (car (vfl-projiziere-neigung (car (get-bogen-mass bogen-auf 3)) - (caddr (get-bogen-mass bogen-auf 3)) - (fix *vfk-gefaelle-winkel*)))))) + (progn + (setq m-ueb (get-bogen-mass bogen-auf (fix *vfk-gefaelle-winkel*))) + (if m-ueb + (setq uebergang-fp + (+ uebergang-fp + (car (vfl-projiziere-neigung (car m-ueb) (caddr m-ueb) + (fix *vfk-gefaelle-winkel*))))) + (vfl-meldung (ssg-textf "vfl-m2-uebergang-bogen-fehlt" + (list (strcat "Vario_Bogen_auf_" + (itoa (fix *vfk-gefaelle-winkel*)) + "_TEF_rechts"))))))) (if (> (car climbers) vf-start) ; flache Zone GEHT VORAUS -> flach-aus - (setq uebergang-fp - (+ uebergang-fp (car (get-bogen-mass bogen-ab 3))))))) + (progn + (setq m-ueb (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*))) + (if m-ueb + (setq uebergang-fp (+ uebergang-fp (car m-ueb))) + (vfl-meldung (ssg-textf "vfl-m2-uebergang-bogen-fehlt" + (list (strcat "Vario_Bogen_ab_" + (itoa (fix *vfk-gefaelle-winkel*)) + "_TEF_rechts"))))))))) + ;; as-along-fix nur abziehen, wenn das AS-Segment auch wirklich ein + ;; Kletterer ist - sitzt es auf einem Fueller-Segment, zieht es dort ab + ;; (siehe fill-len weiter unten). Sonst waere der Abzug doppelt bzw. am + ;; falschen Segment. (setq wahl3 (vfl-zsolve-winkel-waehlen - (- climber-span es-along-fix uebergang-fp) dH-adj feste-vf)) + (- climber-span es-along-fix uebergang-fp + (if (member vf-start climbers) as-along-fix 0.0)) + dH-adj feste-vf)) (if (null wahl3) (progn ;; Stufe 2: KEIN Tabellenwinkel passt (der/die Kletterer sind bei JEDER @@ -12126,7 +12405,15 @@ ;; --- Kletterer (geneigt, 3-Grad-Basis) --- ((and (= (car seg) "Linie") (member i climbers)) (if flat-p (progn (setq pt (vfl-zsolve-flach-aus pt seg-hz)) (setq flat-p nil))) - (setq this-lvf (max *vfk-restlaenge-min-clamp* (* br-lvf (/ (caddr seg) climber-span)))) + ;; Anteil dieses Kletterers an der Gesamt-Kletterlaenge. Zaehler UND + ;; Nenner muessen denselben Laengenbegriff benutzen: climber-span + ;; enthaelt die Eck-Zuschlaege (vfl-eck-extra), die Planlaenge allein + ;; nicht. Ohne den Zuschlag im Zaehler summieren sich die Anteile auf + ;; weniger als 1, der Kletterer wird zu kurz - bei EINEM Kletterer um + ;; genau den Zuschlag (real 2026-10-26: 55 mm zu kurz, davon 5.4 mm + ;; Hoehenfehler ueber sin(Steigwinkel)). + (setq this-lvf (max *vfk-restlaenge-min-clamp* + (* br-lvf (/ (+ (caddr seg) (vfl-eck-extra i)) climber-span)))) (setq pt (vfs-vf-koerper pt br-richtn br-winkel this-lvf seg-hz)) (vfl-acc-vf-seg br-richtn br-winkel this-lvf) (setq anzahl-vf (1+ anzahl-vf) letzt-koerper-hz seg-hz)) @@ -12173,7 +12460,12 @@ (* (+ *vfk-stations-laenge* br-gf2-exp) (cos rad3v))) ) ) - (setq fill-len (caddr seg))) + ;; Gewoehnlicher Fueller: volle Planlaenge des Segments, abzueglich + ;; der fest gebauten Teile, die auf GENAU diesem Segment stehen - + ;; das AS am Laufanfang, Abschlussbogen + Motorstation am Laufende. + (setq fill-len (- (+ (caddr seg) (vfl-eck-extra i)) + (if (= i vf-start) as-along-fix 0.0) + (if (= i vf-ende) abschluss-fp 0.0)))) ;; Kritische Fueller-Position (direkt nach Umlenkstation ODER direkt vor ;; Motorstation) zu kurz fuer den geforderten Wert - statt still zu ;; klemmen, den Nutzer warnen (Zielhoehe/-position wird dann nicht exakt @@ -12245,12 +12537,28 @@ (setq seg (nth i plan) typ (car seg) hz (cadr seg)) (cond ((= typ "Linie") - (setq winkel (nth 4 seg) deltaL (caddr seg) + (setq winkel (nth 4 seg) deltaL (+ (caddr seg) (vfl-eck-extra i)) sep-vor-gf (nth 5 seg) sep-nach-gf (nth 6 seg)) ;; Footprint fuer Separator(300)+ES nur reservieren, wenn ES-Element ;; gewuenscht ist (ein=0 sonst, siehe oben) - ohne ES entfaellt auch ;; der abschliessende Separator. - (if (= i last-line) (setq deltaL (- deltaL (if es-vorhanden *vfk-separator-laenge* 0.0) ein))) + ;; Letzte Gerade vor dem ES: ihr ENDPUNKT ist der KS_AUS des ES-Elements, + ;; nicht dessen Anfang (Nutzer-Vorgabe 2026-10-26). Vom Segmentbudget + ;; gehen darum Separator UND der echte ES-Laengsanteil ab. + ;; Der frueher benutzte Skalar `ein` (ein-dx) ist das Block-dx OHNE den + ;; Schwenk des 30-Grad-Elements und lag real weit daneben: Segment 5 + ;; hatte 1466 mm Plan und wurde 2285 mm lang (GF 1411 statt ~591). + ;; vfl-zsolve-es-vektor misst along/quer aus der REALEN Blockgeometrie + ;; gegen die Fahrtrichtung dieses Segments - dieselbe Funktion, die der + ;; Zweig ohne flache Zone fuer es-along-fix schon benutzt. Nur wenn der + ;; Block nicht lesbar ist, bleibt `ein` als grobe Naeherung. + ;; ein-fp-mess ist derselbe Wert, mit dem vfl-zsolve-rueckanker die + ;; Hoehe gerechnet hat (einmal gemessen, siehe oben) - nur so koennen + ;; Laenge und Hoehe nicht auseinanderlaufen. + (if (= i last-line) + (setq deltaL (- deltaL + (if es-vorhanden *vfk-separator-laenge* 0.0) + ein-fp-mess))) ;; Separator(en) VOR/NACH aus der Klassifizierung (Punkt 1, wie B1). (if sep-vor-gf (setq deltaL (- deltaL *vfk-separator-laenge*))) (if sep-nach-gf (setq deltaL (- deltaL *vfk-separator-laenge*))) @@ -12307,8 +12615,12 @@ (list (rtos (- (car ist-ende) (car soll-ende)) 2 1) (rtos (- (cadr ist-ende) (cadr soll-ende)) 2 1) (rtos (- (caddr ist-ende) (caddr soll-ende)) 2 1)))) - ;; Zerlegung bezogen auf die ES-KS_AUS-ACHSE: Quer (senkrecht zur Achse) soll - ;; ~0 sein (Endpunkt liegt auf der KS_AUS-Achse), Laengs = ES-Ausladung. + ;; Zerlegung bezogen auf die ES-KS_AUS-ACHSE (Nutzer-Vorgabe 2026-10-26): + ;; QUER (senkrecht zur Achse) MUSS 0 sein - der Endpunkt liegt auf der + ;; Achse, in dieser Richtung darf es keine Abweichung geben. + ;; LAENGS (auf der Achse) darf abweichen - das ist die Ausladung des + ;; ES-Elements. + ;; Ebenso MUSS dZ 0 sein. (setq end-hz (car (frame->hz-winkel frame))) ; KS_AUS-Achsrichtung (setq dir-x (cos (* end-hz (/ pi 180.0))) dir-y (sin (* end-hz (/ pi 180.0)))) (setq laengs-wert (+ (* (- (car ist-ende) (car soll-ende)) dir-x) @@ -12317,15 +12629,14 @@ (* (- (cadr ist-ende) (cadr soll-ende)) dir-x))) (princ (ssg-textf "vfl-m3-laengs-quer" (list (rtos laengs-wert 2 1) (rtos quer-wert 2 1)))) (princ (ssg-text "vfl-m3-achse-hinweis")) - ;; Soll (ES-korrigiert): der gepickte Zielpunkt, verschoben um den bereits - ;; oben gemessenen Laengs-Wert entlang der KS_AUS-Achse - zeigt, dass die - ;; Abweichung oben keine Unsicherheit ist, sondern GENAU der ES-eigene - ;; Fussabdruck (90/30-Grad-Schwenk, siehe doc/VarioFoerderer_Linienzug_ - ;; Prinzipien.md Abschnitt 6/14.11: "seitliche ES-Ausladung, kein Fehler"). - ;; Bewusst aus dem bereits gemessenen Laengs-Wert abgeleitet statt ueber - ;; eine zweite, eigenstaendige vf-element-ks-info-Neuberechnung - das waere - ;; ein zweiter Rechenweg fuer dieselbe Information mit eigenem Bug-Risiko, - ;; hier reicht die Umkehrung der bereits validierten Laengs/Quer-Zerlegung. + ;; Der Querversatz ist KEIN Fehler, sondern die Geometrie des ES-Blocks: + ;; das Ausschleuselement wandert beim Schwenken zur Seite. Ihn als + ;; "Abweichung" stehen zu lassen hat real zu einer langen Fehlersuche + ;; gefuehrt - darum wird er benannt. Die frueheren Zeilen "Soll + ;; (ES-korrigiert)" sind damit entfallen: sie haben den gepickten Zielpunkt + ;; um den Laengs-Wert verschoben, damit dY per Konstruktion 0 wurde, und + ;; verdeckten so genau die Fehlerart, die wir hier jagen (ein zu langes + ;; Segment laengs der Achse sah dadurch fehlerfrei aus). (setq soll-korrigiert (list (+ (car soll-ende) (* laengs-wert dir-x)) (+ (cadr soll-ende) (* laengs-wert dir-y)) (caddr soll-ende))) diff --git a/doc/VarioFoerderer_Linienzug_Prinzipien.md b/doc/VarioFoerderer_Linienzug_Prinzipien.md new file mode 100644 index 0000000..5419471 --- /dev/null +++ b/doc/VarioFoerderer_Linienzug_Prinzipien.md @@ -0,0 +1,822 @@ +# VarioFoerderer / Gefaellestrecke - Konstruktionsprinzipien (Linienzug) + +Referenzdokument fuer den Anlagen-Typ "linienzug" (gemischte GF/VF-Kette). +Fasst alle im Gespraech festgelegten Prinzipien und Definitionen zusammen. + +> Hinweis: Dieses Dokument beschreibt das ZIEL-Verhalten. Der aktuelle +> Umsetzungsstand und bekannte Abweichungen sind in Abschnitt 13 gesammelt. + +--- + +## 0. Leitsatz: die Geraden besitzen den Pfad, Boegen sind Verbindungsstuecke + +Festgelegt am 2026-10-26, gilt fuer alle Modi und fuer GF wie VF. + +**Massgeblich sind die gezeichneten GERADEN.** Die Mittellinie jedes geraden +Abschnitts liegt auf seiner Pfadlinie - das ist die Bedingung, an der sich +alles ausrichtet. + +**Ein gezeichneter Bogen ist dagegen nur Hilfsgeometrie.** Er sagt, WO die +Ecke liegt und WIE HERUM es geht. Sein Radius und seine Masse sind KEINE +Vorgabe; wer R300 oder R800 zeichnet, bekommt dieselbe Kette. + +**Das Eck-Bauteil ist ein Verbindungsstueck und hat keinen eigenen +Freiheitsgrad.** Seine Lage folgt aus den beiden Geraden, die es verbindet: +seine Mittellinie beruehrt beide. `KS_EIN` liegt auf der Geraden davor und +zeigt in deren Richtung, `KS_AUS` auf der Geraden danach und in deren +Richtung. Damit laeuft die Kette knickfrei und ohne Seitenversatz durch die +Ecke. + +### Daraus folgt: jedes fest gebaute Teil zieht seine Laenge von dem Segment ab, auf dem es steht + +Ein gerades Segment vergibt Laenge an alles, was darauf gebaut wird. Was +dort ein festes Mass hat, gehoert nicht mehr dem frei rechenbaren Rest: + +| Stelle | Vorgabe aus der Zeichnung | Mass kommt vom Bauteil | +|---|---|---| +| Kettenanfang | Startpunkt | AS-Element (Fussabdruck entlang des Segments) | +| Ende des VF-Laufs | Segmentlaenge | Abschlussbogen + Motorstation | +| letzte Gerade vor dem ES | Endpunkt | Separator + ES-Laengsanteil | +| Ecke | NUR Ort und Drehsinn | Tangentenabstand des Eck-Bauteils | + +**Die Masse werden GEMESSEN, nicht aus Block-Massen oder Radien gerechnet.** +Gerechnete Werte lagen in der Praxis daneben: der AS-Fussabdruck um 184 mm +(das Block-`dx` kennt den Achs-Versatz nicht), der ES-Laengsanteil um ~520 mm +(das Block-`dx` kennt den Schwenk des 30-Grad-Elements nicht). + +**Laengen- und Hoehenbilanz muessen denselben Wert benutzen.** Laufen sie +auseinander, stimmt die Laenge und die Hoehe kippt um +`Differenz * sin(Neigung)` - real aufgetreten: 820 mm Differenz ergaben +43 mm Hoehenfehler. Darum wird jeder Fussabdruck EINMAL gemessen und an +beide Bilanzen gegeben (Bau-Pfad und `vfl-zsolve-rueckanker`). + +### Grenzen + +- Ein Eck-Bauteil kann nur dann beide Geraden beruehren, wenn sein Schwenk + zum Eckwinkel passt (90-Grad-Kurve an 90-Grad-Ecke). Weichen sie ab, + bleibt ein Knick, den keine Laengenaenderung aufhebt. +- Der **seitliche Versatz des ES-Elements** ist GEWOLLT und bleibt bestehen + (Entscheidung aus Modus 1: der Hauptstrang bleibt gerade). Entweder liegt + die letzte Gerade exakt auf der gezeichneten Linie und der ES-`KS_AUS` + landet daneben, oder umgekehrt - beides zugleich geht nicht. + +--- + +## 1. Grundkonzept "Kette" + +- Eine Kette (Linienzug) ist eine durchgehende Foerderstrecke entlang eines + beliebigen 3D-Pfades. +- Eine Kette beginnt **immer** mit genau **einem** `AS_Element` + (Kettenanfang) und endet **immer** mit genau **einem** `ES_Element` + (Kettenende). +- Pro Kette gibt es also **genau ein** AS_Element und **genau ein** ES_Element. +- Die Mittelachse der gesamten Kette liegt exakt auf dem gewaehlten/gezeichneten + Pfad (wie in der Gefaellestrecke, ueber KS_EIN/KS_AUS-Frame-Kettung). + +### Eingabemodi (Konsolen-Nummerierung, intern GLEICHNAMIG - kein Tausch) +1. **Modus 1 - Wert manuell eingeben** (`vf-linienzug-modus`): Startpunkt + waehlen, dann Punkt-fuer-Punkt eine Linie in beliebige Richtung ziehen, + Endhoehe abfragen usw. (interaktiv). Gegenstand der Abschnitte 1-13. +2. **Modus 2 - Pfad + Ziel-Hoehe** (`vf-linienzug-modus2`): Ein bereits + gezeichneter Pfad (LINE/ARC-Kette) wird ausgewaehlt, Start-/Endpunkt + + Zielhoehe abgefragt; die Kette wird als Randwertproblem geloest (Ziel-Hoehe + wird EXAKT getroffen). Siehe Abschnitt 14. +3. **Modus 3 - Vorwaerts-Nachbau** (`vf-linienzug-modus3`): baut eine Kette + Segment fuer Segment vorwaerts anhand ausgewaehlter 3D-Objekte; die + Endhoehe ist nur ein Ergebnis, nicht vorgebbar (Anfangswertproblem). + Legacy, wird schrittweise umgebaut - nicht Gegenstand dieses Dokuments. + +--- + +## 2. Definition: reiner VarioFoerderer (VF) + +- **Prinzipiell ist ein reiner VarioFoerderer die Gesamtheit aller Segmente + zwischen einer Umlenkstation und einer Motorstation.** +- Die **Umlenkstation** steht **einmal** am Anfang einer VF-Einheit, die + **Motorstation** steht **einmal** am Ende einer VF-Einheit. +- Alles dazwischen (geneigte Strecken, horizontale Strecken, Vario-Kurven, + Vertikalboegen) gehoert zum reinen VarioFoerderer und kann eine + Unter-Kette bilden. + +### Warum GF1/GF2 und Separatoren dazugehoeren +Was `variofoerderer-einfuegen` aktuell baut, ist eine Kombination MIT +`AS_Element`, `ES_Element`, `GF1`, `GF2` und Separatoren. Grund: + +- `AS_Element` hat zwischen KS_EIN und KS_AUS eine Neigung von ca. **3 Grad**. + Deshalb ist eine **3-Grad-Gefaellestrecke `GF1`** noetig, um das AS_Element + mit der Umlenkstation zu verbinden. +- Ebenso ist eine **3-Grad-Gefaellestrecke `GF2`** noetig, um die Motorstation + mit dem `ES_Element` zu verbinden. + +### Standard-Aufbau (11 Schritte, Typ "standard") +``` + 1. AS_Element (Kettenanfang, ~3 Grad Neigung) + 2. GF1 Gefaellestrecke (3 Grad) -> verbindet AS mit Umlenkstation + 3. Separator (300 mm) + 4. Umlenkstation (500 mm, 3 Grad) <-- Beginn reiner VF + 5. Vertikalbogen 1 + 6. L_VF variable Strecke + 7. Vertikalbogen 2 + 8. Motorstation (500 mm, 3 Grad) <-- Ende reiner VF + 9. GF2 Gefaellestrecke (3 Grad) -> verbindet Motorstation mit ES +10. Separator (300 mm) +11. ES_Element (Kettenende) +``` +Der "reine VF" sind hier die Schritte 5-7 (zwischen Umlenk- und Motorstation). + +### Aufbau einer VF-Einheit im LINIENZUG (Abweichung vom Standard) +Im Linienzug sitzt die Staustrecke bevorzugt am **Einlauf** und der Separator +wird erst spaeter gesetzt: +``` +[AS bzw. vorheriges Element] +GF1 Gefaellestrecke (3 Grad) <- ganze oder halbe Staustrecke (Wahl) +Separator (300 mm) +Umlenkstation (500 mm, 3 Grad) <-- Beginn reiner VF +[1..n Koerper-Sub-Segmente + Vario-Kurven] <-- reiner VF (mehrsegmentig) +Motorstation (500 mm, 3 Grad) <-- Ende reiner VF +[GF2 Gefaellestrecke (3 Grad)] <- nur falls "halbe am Ausgang" + -> KEIN Separator direkt hinter der Motorstation/GF2 +``` +Der abschliessende Separator + das ES-Element kommen erst am **Kettenende** +(bzw. optional ein Separator zwischen zwei Foerderern). Details: Abschnitt 5, 8, +9 (GF-Verteilung) und 12 (Block-Attribute). + +--- + +## 3. Segment-Entscheidung: GF oder VF + +Fuer jedes gerade Linien-Segment wird anhand von Start-/Endhoehe entschieden. +**Wichtig: Eine reine Gefaellestrecke ist NIE steiler als 3 Grad.** Deshalb ist +die reine GF auf ~3 Grad beschraenkt, alles Steilere ist ein VarioFoerderer: + +| Bedingung | Ergebnis | +|---|---| +| Endpunkt **hoeher** als Startpunkt | **immer VF** (eine Gefaellestrecke kann als reine Schwerkraftstrecke nicht steigen) - **Ausnahme:** "Neue Linie: GF" bietet dafuer einen eigenen, bewusst getrennten Sonderfall "Steigende GF" (siehe unten) | +| Endpunkt **tiefer**, Neigung **~ 3 Grad** (Toleranzband) | **reine Gefaellestrecke (GF)** bei fest 3 Grad | +| Endpunkt **tiefer**, Neigung **> 3 Grad** | **VF-ab** (angetrieben, erreicht die Steilheit ueber Vertikalboegen 3-51 Grad) | +| Endpunkt **tiefer**, Neigung **< 3 Grad** | **VF** (zu flach fuer reine GF: Horizontale Mitte oder diskreter Winkel) | + +Das Toleranzband um 3 Grad ist ueber `*vfl-gf-winkel-toleranz*` (Vorgabe 0.5 Grad) +einstellbar. + +### Sonderfall: Steigende GF (bis 3 Grad) + +"Neue Linie: GF" hat eine dritte, eigenstaendige Laengen-Modus-Option +("Steigende GF (Sonderfall, bis 3 Grad)", `gf-laenge-modus="3"`) NUR fuer +diesen einen Ausnahmefall: eine reine Gefaellestrecke, die bis zu 3 Grad +STEIGT statt faellt (Vorzeichenkonvention wie `frame->hz-winkel`: negativer +Winkel = steigend). Bewusst getrennt von den normalen Zweigen A (Endpunkt +waehlen) und B (Zielhoehe zuerst) - kein Separator wird dafuer gefragt +(Separatoren gelten nur fuer die normale, fallende GF). + +- Die Bibliothek kennt nur EINEN 3-Grad-Bogen gegenueber FLACH (0 Grad, + `Vario_Bogen_auf_3`/`Vario_Bogen_ab_3`) - ein Wechsel zwischen der + fallenden (+3) und der steigenden (-3) Basis braucht daher IMMER 1-2 + Boegen ueber 0 Grad als Zwischenhalt (`vfl-bogen-uebergang`, generischer + Uebergangs-Helfer): 3 Grad Differenz = ein Bogen, 6 Grad Differenz (voll + fallend -> voll steigend oder umgekehrt) = zwei Boegen. +- `vfl-bogen-uebergang` wird sowohl VOR jeder normalen (fallenden) GF + eingehaengt (falls der Vorgaenger noch steigend ist - No-Op, wenn schon + passend) als auch VOR jeder Separator+ES-Element-Anhaengung ans + Kettenende: **ES-Element-Hardware gibt es nur fuer die Anfahrt auf der + Standard-Fallend-Basis** - kommt die Kette steigend an, wird zuerst + automatisch zurueckgewandelt (siehe Abschnitt 6). + +- **3 Grad** ist die kleinste im gesamten Projekt verfuegbare Neigung + (`Vario_Bogen_auf_3` / `Vario_Bogen_ab_3`). Eine flachere GF gibt es nicht. + +### Kurzsegment-Regel (deltaL < 1000 mm) +- Ein VarioFoerderer braucht mindestens **1000 mm** deltaL, weil Umlenkstation + (500 mm) + Motorstation (500 mm) diese Strecke bereits belegen. Unterhalb + 1000 mm ist kein VF baubar. +- Ist deltaL < 1000 mm, wird das Segment **automatisch eine 3-Grad-geneigte + Gefaellestrecke** (die kleinstmoegliche GF-Neigung). +- Fuer solche Kurzsegmente wird die **Endpunkt-Hoehe NICHT abgefragt** - die + Hoehe ergibt sich fest aus `deltaH = deltaL * tan(3 Grad)`. +- Ein steigendes Kurzsegment ist nicht baubar (GF kann nicht steigen, VF kein + Platz). Da fuer Kurzsegmente keine Hoehe abgefragt wird, entsteht dieser Fall + im interaktiven Ablauf ohnehin nicht (immer fallend mit 3 Grad). +- Beim VF-Fall (< 3 Grad Gefaelle) wird die Machbarkeit in dieser Reihenfolge + geprueft: + 1. Horizontaler VarioFoerderer (`berechne-horizontale-mitte`), + 2. sonst diskreter Winkel 3-51 Grad (`berechne-alle-winkel`). +- **Ein VarioFoerderer ist im Kern eine Kombination aus Foerderer und + Gefaellestrecke** - deshalb greifen fuer die geneigten Anteile eines VF + dieselben 3-Grad-Prinzipien. + +--- + +## 4. Verfeinerter interaktiver Ablauf eines VF-Segments + +Wird ein Linien-Segment als VF eingestuft, folgt diese Logik: + +### 4.1 Frage: Ist der Endpunkt dieser Linie das Ende des Foerderers? + +**JA (Foerderer-Ende):** +- Es wird eine **Motorstation** gebaut (unabhaengig von Auf/Ab). +- Der gewaehlte **Vertikalbogen** muss **denselben Winkel** wie der + **vorherige Vertikalbogen** haben. Dadurch neigt sich der KS_AUS des + Vertikalbogens um **3 Grad nach unten** - passend zur 3-Grad-Neigung der + Motorstation. + +**NEIN (kein Foerderer-Ende):** +- Es wird **keine Motorstation** gebaut. +- Stattdessen Frage: **Horizontalen Foerderer fortsetzen?** + - Falls ja: eine weitere Linie **ohne Hoehendifferenz** zeichnen und einen + **horizontalen VF-Abschnitt** bauen. + - Nach dem horizontalen Foerderer folgt erneut eine 3-fache Auswahl + (siehe 4.2). + +### 4.2 Auswahl nach einem horizontalen Foerderer-Abschnitt + +1. **Foerderer-Ende bauen**: einen **3-Grad-Bogen (auf oder ab)** einfuegen, + sodass KS_AUS sich 3 Grad nach unten neigt, danach die **Motorstation** + (die selbst 3 Grad geneigt ist). +2. **Vario-Kurve einfuegen?** Falls ja: Winkel abfragen. Reihenfolge/Vorgabe: + **90 / 60 / 30 Grad** (90 Grad auf Position 1 und als Standard, da am + haeufigsten gebraucht). +3. **Auf/Ab-Foerderer fortsetzen**: naechste Linie zeichnen, Endhoehe abfragen + und pruefen, ob das das Foerderer-Ende ist (entscheidet, ob eine + Motorstation gebaut wird - zurueck zu 4.1). + +### 4.3 Rekursion +Erreicht der Foerderer noch nicht das Ende, wird immer wieder gefragt, ob der +horizontale Foerderer fortgesetzt werden soll (Schleife ueber 4.1 -> 4.2), bis +schliesslich ein Foerderer-Ende (mit Motorstation) gesetzt wird. + +### 4.4 Prompt "Ist der Endpunkt der Foerderer?" - drei Optionen + +Der Prompt in der VF-Koerper-Schleife hat drei Optionen: +1. **Ja (nur Motorstation setzen)** - VF-Einheit schliessen, Kette laeuft weiter + (weiterer Foerderer/Motor moeglich, siehe Abschnitt 8). +2. **Nein (weiterbauen)** - naechstes Koerper-Sub-Segment (zurueck zu 4.2). +3. **Ja (Motorstation setzen, Kettenende definieren)** - siehe 4.5. + +### 4.5 Option 3: Kettenende exakt am Zielpunkt definieren (ein Motor) + +Deckt den Fall ab, dass **Start- und Endpunkt der Kette feststehen** und die +Strecke - unter Nutzung von Horizontal-Stuecken/Vario-Kurven, aber mit **nur +einem Motor** - genau am Ziel schliessen soll. + +Ablauf (`vfl-body-abschluss` / `vfl-body-zerlegung`): +1. Zielpunkt (XY, auf die Fahrtrichtung projiziert) + Zielhoehe (Z) abfragen. +2. Der verbleibende **gerade** Lauf wird mit der bewaehrten Standard-Logik + zerlegt (`berechne-alle-winkel` bzw. `berechne-horizontale-mitte`), aber mit + **reduziertem Mid-Body-Budget**: der Einlauf (AS/GF1/Separator/Umlenk) ist + schon gebaut, deshalb `aus-dx = aus-dz = 0` und festes Budget nur + **Motor 500 + Auslauf-Separator 300 = 800 mm** (statt 1300). +3. **GF2 variiert mit dem gewaehlten Winkel** (Winkeltabelle wie aussen, jede + Zeile hat ihr eigenes L_GF) und sitzt **hinter dem Motor**. Die + GF-Verteilungs-Frage (Abschnitt 9) wird fuer diesen GF2-Anteil ueberschrieben; + Mindestwert `*vfl-gf-min-laenge*` (400 mm). +4. **Hoehe** kommt aus gewinkeltem VF + GF2, die ueberschuessige **Laenge** aus + einem horizontalen VF (flacher/langer Lauf). +5. Abschluss automatisch: Motorstation -> GF2 -> Separator -> ES-Element. + +Horizontale **Vario-Kurven** (XY-Richtungswechsel) bleiben **manuell** und werden +von Option 3 nicht platziert - Option 3 baut nur den geraden Schluss. + +**Ist-Ziel-Report:** Nach dem ES wird Soll (Zielpunkt) vs. Ist (ES-KS_AUS) +ausgegeben (dX/dY/dZ). Ein `ES_Element_90` macht einen 90-Grad-Schwenk; sein +KS_AUS liegt daher ~637 mm **seitlich** (dY) neben der Kettenachse. dX/dZ treffen +das Ziel exakt - das dY ist der Fussabdruck des ES-Elements, **kein Fehler** (die +Soll-Berechnung modelliert nur den geraden Lauf ohne den seitlichen ES-Versatz). + +### 4.6 "Neue Linie BIS Kettenende" (Option 4/6): Startpunkt-Typ-Erkennung + +Das Hauptmenue "Naechstes Element waehlen" bietet daneben eine EIGENE Option +("Neue Linie BIS Kettenende (danach Separator + ES-Element)"), die IMMER mit +Separator+ES-Element abschliesst, aber - anders als Option 3 oben - auch am +echten Kettenanfang (allererster Klick der Session) aufgerufen werden kann. +Damit sich die Bau-Logik dabei nicht blind auf die generische GF/VF-Automatik +(Abschnitt 3) verlaesst, wird zuerst erkannt, ob der gewaehlte Startpunkt zu +einer bestehenden VF- oder GF-Kette gehoert: + +- **Laufende Fortsetzung** (ein Frame existiert schon in dieser Session): + der Vorgaengertyp ist bereits bekannt (`letzter-typ`) - `"Vario-Kurve"` + zaehlt zur VF-Familie, `"GF"`/`"Separator"` zur GF-Familie. `"VF"` (eine + gerade FERTIG gebaute VF-Einheit) zaehlt bewusst NICHT zur VF-Familie fuer + diesen Zweck - eine neue VF-Einheit direkt danach braucht trotzdem eine + frische Umlenkstation (siehe unten). +- **Echter Kettenanfang ohne AS-Element**: KS_AUS-Scan am gepickten Punkt + (`vfl-ks-aus-hz-an-punkt`, dieselbe Erkennung wie in + `vfl-eingang-modus-bestimmen`) - trifft der Punkt exakt auf das KS_AUS + eines bestehenden Bausteins, wird dessen Typ uebernommen. + +**VF-Familie erkannt** (kein eigener Umlenkstation-Bedarf, z.B. Fortsetzung +einer Vario-Kurve): baut ueber `vfl-body-abschluss` + `vfl-vf-ausgang-bauen` +- dieselben Bausteine wie Option 3 oben (800 mm Budget statt 1300, volle +Stufe-1/2-Diagnose inkl. exakter Zielhoehe bleibt erhalten, da kein +GF1-Stumpf noetig ist, dessen Wegfall die Loesung stoeren wuerde). + +**GF-Familie erkannt** (Fortsetzung einer Gefaellestrecke): dieselbe +Endpunkt-/Zielhoehe-Wahl wie "Neue Linie: GF" Zweig A/B (Abschnitt 3) statt +der generischen Automatik - eine reine Gefaellestrecke kann nicht steigen. + +**Nicht eindeutig zuordenbar** (z.B. frisch gesetztes AS-Element ohne +erkennbaren Vorgaenger): unveraendert die generische Machbarkeits-Automatik +(`vfl-segment-entscheidung`, Abschnitt 3). + +--- + +## 5. Separator-Regeln + +Ein Separator (300 mm) wird eingefuegt: +- am **Ende jeder Gefaellestrecke (GF)**, und +- **vor jedem Gefaellebogen**. + +### Basis-Regel (keine Verdopplung) +Da nach einer GF nur ein GF-Bogen oder eine neue Linie folgen darf, fallen die +beiden obigen Regeln an einer direkten **GF -> Gefaellebogen**-Verbindung auf +**dieselbe Nahtstelle**. Dort wird nur **EIN** Separator eingefuegt (nicht zwei +- kein Separator "am GF-Ende" zusaetzlich zu einem Separator "vor dem Bogen"). +Ein Separator sitzt also genau einmal an der Grenze zwischen GF und +Gefaellebogen. + +### Separator im LINIENZUG (Platzierung) +Damit zwischen einem AS und ES **mehrere Foerderer (Motoren)** liegen koennen, +wurde der Separator vom VF-Ausgang geloest: +- **Direkt hinter Motorstation/GF2: KEIN Separator** (`vfs-vf-exit` wird mit + `mit-separator=nil` aufgerufen). +- **Am Kettenende (Ja):** Separator (300 mm) + ES-Element werden zusammen + gesetzt (`vfl-insert-es-element`). +- **Bei "kein Kettenende" (Nein):** es wird gefragt, ob an dieser Stelle ein + **zusaetzlicher Separator** eingefuegt werden soll (`vfl-insert-separator`); + danach geht es mit dem naechsten Element weiter (neuer Foerderer moeglich). +- Der Standard-/Etage-VF (nicht Linienzug) behaelt den Separator direkt hinter + GF2 (`mit-separator=t`). + +### Separator am horizontalen Stueck (0 Grad) +Wird VOR/NACH einem horizontalen Stueck ein Separator gewuenscht, liegt er in der +**horizontalen Ebene (0 Grad)** - also ZWISCHEN den beiden Vertikalboegen +(`auf_3` ... `ab_3`), nicht auf der 3-Grad-Basis davor/danach. Umgesetzt in +`vfl-sep-hz` (Einfuegung bei 0 Grad). Der optionale **Zwischen-Separator** +zwischen zwei Foerderern sitzt dagegen auf der 3-Grad-Basis (`vfl-insert-separator`). + +--- + +## 6. Vertikalbogen-Regeln + +- Der Vertikalbogen am **Ende** eines VF (vor der Motorstation) hat **denselben + Winkel** wie der Vertikalbogen am **Anfang** (nach der Umlenkstation). +- Der abschliessende Vertikalbogen bringt die Strecke auf **3 Grad Neigung nach + unten**, damit der Anschluss an die (3 Grad geneigte) Motorstation passt. + +### Bogen-Blockname vs. Konsolen-Rotationsanzeige der Geraden (KEIN Fehler) +Beim Bau (`vfs-vf-koerper`) zeigt die Konsole fuer einen S-Bogen mit +Tabellenwinkel z.B. 18 Grad folgendes Muster: +``` +5/11: Vario_Bogen_auf_18_TEF_rechts (3 Grad geneigt) +6/11: Steigungsstrecke (-15 Grad, L=... mm) +7/11: Vario_Bogen_ab_18_TEF_rechts (-15 Grad geneigt) +``` +Die **Bogen-Bloecke** (`auf_18`/`ab_18`) tragen den ECHTEN, gewaehlten +Tabellenwinkel im Namen - das ist der massgebliche Wert (auch fuer +`Vario_Kette_Merge`s `VF_WINKEL`-Attribut, das ihn direkt aus dem Blocknamen +liest). Die **Gerade dazwischen** wird dagegen mit `winkel-eff = best-winkel - +3` inseriert (hier 18-3=15, negativ fuer "Auf") - NICHT mit dem vollen +Tabellenwinkel. Grund: der Eintrittsbogen hat den Frame bereits von der +3-Grad-Basis auf den vollen Winkel (18 Grad) gedreht; die Gerade wird relativ +zu diesem BEREITS gedrehten Frame eingefuegt und muss deshalb nur noch die +DIFFERENZ zur Basis nachdrehen. In Weltkoordinaten liegt die Gerade trotzdem +exakt bei 18 Grad - die "-15" ist eine reine Bau-interne Rotationsangabe, +kein zweiter, abweichender Winkel. Bestaetigt durch den Ist-Ziel-Report +(dX/dZ exakt) - waeren beide Werte real unterschiedliche Winkel, koennte das +Ergebnis nicht exakt stimmen. + +### Flache Zone (horizontale Stuecke bleiben flach - Uebergang spaeter) +Ein horizontales Stueck endet **NICHT** mehr automatisch mit `ab_3` auf der +3-Grad-Basis, sondern bleibt **flach (0 Grad)**: +- Der `auf_3`-Eintritt wird nur gesetzt, wenn der Frame noch **nicht** flach ist + (sonst bleibt die laufende flache Zone erhalten). +- Der `ab_3`-Uebergang zurueck auf 3 Grad kommt **erst dann**, wenn ein echtes + 3-Grad-Element folgt: Motorstation, Auf/Ab-Koerper oder Option-3-Abschluss + (`vfl-nach-3grad`). +- Die aktuelle Neigung wird direkt aus dem Frame gelesen (`vfl-frame-flach-p`, + Schwelle 1,5 Grad). + +Vorteil: aufeinanderfolgende horizontale Elemente (Strecken, Separatoren, +Vario-Kurven) bleiben **durchgehend flach** - kein unnoetiges 3->0->3-Gezappel. +Die VF-Einheit als Ganzes endet weiterhin auf 3-Grad-Basis (der Guard vor der +Motorstation stellt das sicher), sodass die aeussere Kette unveraendert +3-Grad-basiert bleibt. + +### ES-Element/Umlenkstation brauchen die Standard-Fallend-Basis +Separator+ES-Element (Kettenende) UND eine Umlenkstation (VF-Einheit-Start) +gibt es hardwareseitig nur fuer die Anfahrt auf der Standard-Fallend-Basis +(+3 Grad) - eine Anfahrt auf der steigenden Basis (-3 Grad, siehe Abschnitt 3 +"Steigende GF") ist physisch nicht vorgesehen. Vor beiden Anhaengepunkten wird +deshalb `vfl-bogen-uebergang` (Ziel = die Standard-Fallend-Basis) eingehaengt +- bei einer bereits fallenden Ankunft ist das ein No-Op, bei einer steigenden +Ankunft werden automatisch die noetigen 1-2 Ausgleichsboegen eingefuegt. + +### Motorstation-Seite +- Jede Motorstation hat eine **Seite** (links/rechts). Aktuell existiert nur die + **rechts**-DWG (`Vario_Motorstation_500mm`), daher wird derzeit immer "rechts" + verwendet und im Attribut `MOTORSEITE` (kommagetrennt je Motorstation) + festgehalten. Die "links"-Einzel-DWG wird spaeter ergaenzt. + +--- + +## 7. 20-Meter-Regel (Motorstation) + +- Ueberschreitet die **Gesamtlaenge der VF-Segmente nach einer Umlenkstation + 20 m**, erscheint der Hinweis: + **"Foerderer laenger als 20 m - Motorstation einfuegen?"** + +--- + +## 8. Uebergangsregeln zwischen Elementen + +- Vario-Kurven werden **innerhalb** einer VF-Einheit behandelt (im + Foerderer-Ende-Ablauf, Abschnitt 4), nicht auf Ketten-Ebene. +- Nach einem **GF-Segment ODER einer abgeschlossenen VF-Einheit** (beide enden + auf 3-Grad-Neigung) darf als naechstes ein **GF-Bogen** oder eine **neue + Linie** folgen. Nur am Kettenanfang folgt direkt eine neue Linie. +- Bei Boegen/Kurven ist **keine Hoehenabfrage** noetig (die Hoehe ergibt sich + aus der Bogen-/Kurvengeometrie). Bei Linien wird die Endhoehe abgefragt (Aus- + nahme: Kurzsegment < 1000 mm, siehe Abschnitt 3). + +### Mehrere Foerderer (Motoren) je Kette +Zwischen **einem** AS und **einem** ES koennen **mehrere VF-Einheiten** liegen +(jede mit eigener Umlenk- und Motorstation). Nachdem eine VF-Einheit mit der +Motorstation geschlossen ist und "kein Kettenende" gewaehlt wurde, kann die +Kette (optional mit Zwischen-Separator) mit einem GF-Bogen oder einer neuen +Linie weitergebaut werden - ist die neue Linie wieder ein VF, entsteht eine +weitere VF-Einheit (weiterer Motor). Die Motorenzahl/-seiten stehen im Attribut +`MOTORSEITE`. + +### Fahrtrichtung folgt dem Vorgaengerelement +Eine gerade Foerderstrecke kann die Fahrtrichtung nicht aendern - das geht nur +ueber einen GF-Bogen bzw. eine Vario-Kurve. Beim Zeichnen einer neuen Linie +wird der gewaehlte Punkt daher auf die aktuelle Fahrtrichtung **projiziert** +(`vfl-neue-linie-messen`); nur das allererste Segment definiert die Richtung +frei. Auch die Start-Z-Hoehe der Kette wird abgefragt. + +--- + +## 9. GF-Verteilung (Staustrecke: Einlauf / Ausgang) + +Pro VF-Einheit wird gefragt, wie die Staustrecke (L_GF) verteilt wird: +- **1** - halbe Staustrecke als **GF2 am Ausgang**, halbe als **GF1 am Einlauf**. +- **2** - **gesamte** Staustrecke am **Einlauf** (GF1), kein GF2. + +- Mindest-Gesamt-Staustrecke (GF1+GF2) = `*vfl-gf-min-laenge*` (Vorgabe **400 mm**); + faellt das berechnete L_GF darunter, wird angehoben. +- Hinter GF2 folgt **nie** direkt ein Separator (siehe Abschnitt 5). + +--- + +## 10. Laengenbudget & FESTE_HORIZONTAL + +`berechne-alle-winkel` zieht das horizontale Budget der festen Elemente von +deltaL ab (`FESTE_HORIZONTAL`): +- **Standard-/Etage-VF:** `FESTE_HORIZONTAL = 1600` mm + (Umlenk 500 + Motor 500 + **2** Separatoren 300). +- **Linienzug:** `*vfl-feste-horizontal* = 1300` mm + (Umlenk 500 + Motor 500 + **1** Separator am Einlauf 300; der Ausgangs- + Separator entfaellt). Wird als optionaler 4. Parameter an + `berechne-alle-winkel` uebergeben. + +> Offen: Bei mehrsegmentigen VF-Einheiten rechnet `berechne-alle-winkel` je +> Sub-Segment mit eigener Entry/Exit-Annahme - der reale Fussabdruck kann +> abweichen (empirisch pruefen). + +--- + +## 11. Bedienung + +- Alle Ja/Nein- und Auswahl-Abfragen sind **zahlenbasiert** (1/2 bzw. 1/2/3), + keine Buchstaben. +- **Winkelauswahl:** Gibt es fuer einen VF mehrere gueltige Winkel, listet das + System sie (mit L_GF/L_VF) und der Nutzer waehlt (`vfl-waehle-winkel`). +- Beim Start wird die **Z-Hoehe des Kettenstartpunkts** abgefragt. +- **Lueckenlose Nummerierung** im Prompt "Naechstes Element waehlen": der + GF-Bogen ist nur mit vorhandenem Frame Option 1; ohne Frame ruecken die + uebrigen Optionen auf 1/2/3 auf (keine Luecke wie frueher 2/3/4). +- **Vario-Kurve-Winkel:** Reihenfolge 90 / 60 / 30 Grad, 90 Grad ist Standard. +- **GF-Verteilungstext:** "1 - 1/2GF am Ausgang (GF2), 1/2GF am Einlauf (GF1)". + +--- + +## 12. Block-Attribute (TYP = "Streckengruppe") + +Der Linienzug erzeugt EINEN Block `VF_` mit der vollen Attributstruktur +(analog Standard/Etage-VF), aber mit **segmentweisen, kommagetrennten** Werten. +Zwei parallele Segment-Achsen: +- **VF-Sub-Segmente:** `L_VF_m` <-> `WINKEL` <-> `ANTRIEBFAHRTRICHTUNG` +- **GF-Segmente:** `L_GF_m` <-> `GF_WINKEL` + +| Attribut | Inhalt | +|---|---| +| `NUMMER` | fortlaufende VF-Nummer | +| `ID` | aufsteigende, eindeutige ID (`ssg-id-generate`, wie Kreisel) | +| `TYP` | `"Streckengruppe"` | +| `MONTAGEHOEHE_m` | mittlere Hoehe (m) | +| `HOEHE_VON` / `HOEHE_BIS` / `DELTA_H` | Start-/End-/Differenzhoehe (mm) | +| `DELTA_L` | planare Gesamtdistanz Start->Ende (mm) | +| `L_VF_m` | Laenge je VF-Sub-Segment (m), kommagetrennt | +| `WINKEL` | Vertikalbogen-Winkel je VF-Sub-Segment (0 = horizontal), kommagetrennt | +| `ANTRIEBFAHRTRICHTUNG` | `Auf`/`Ab`/`horizontal` je VF-Sub-Segment, kommagetrennt | +| `L_GF_m` | Laenge je GF-Segment **inkl. GF1/GF2** (m), kommagetrennt | +| `GF_WINKEL` | Neigung je GF-Segment (Grad), parallel zu `L_GF_m`, kommagetrennt | +| `MOTORSEITE` | Seite je Motorstation (`rechts`/`links`), kommagetrennt | +| `ANZAHL_SEGMENTE` / `ANZAHL_GF` / `ANZAHL_VF` | Segmentzaehler | +| `SEITE` | AS-Seite (links/rechts) | +| `GF_Bogen_L_90/60/30`, `GF_Bogen_R_90/60/30` | Anzahl GF-Boegen je Seite + Winkel (je eigenes Attribut) | +| `VF_Bogen_A_90/60/30`, `VF_Bogen_I_90/60/30` | Anzahl Vario-Kurven je Variante (A=aussen/I=innen) + Winkel (je eigenes Attribut) | + +Gefuellt werden diese Werte ueber Akkumulatoren (`vfl-acc-*`), die waehrend des +Baus in Reihenfolge angehaengt bzw. hochgezaehlt werden. + +--- + +## 13. Umsetzungsstand & bekannte Abweichungen + +Stand `vf_linienzug.lsp` (Modus 1). + +### Umgesetzt +- **Kettenstruktur** (Abschnitt 1): AS am Anfang, ES am Ende, EIN Block je Kette. +- **GF/VF-Entscheidung** (Abschnitt 3): `vfl-segment-entscheidung` - reine GF + nur ~3 Grad, steiler VF-ab, flacher VF; Kurzsegment < 1000 mm -> 3-Grad-GF + ohne Hoehenabfrage. +- **VF-Einheit, mehrsegmentig** (Abschnitt 2/4): `vfl-vf-einheit` baut Entry + (Umlenkstation) einmal, dann beliebig viele Koerper-Sub-Segmente + + Vario-Kurven, dann Exit (Motorstation) einmal. Bausteine in `vf_standard.lsp`: + `vfs-vf-entry` / `vfs-vf-koerper` / `vfs-vf-exit`. +- **Mehrere Foerderer (Motoren) je Kette** (Abschnitt 8): nach einer VF-Einheit + kann (optional mit Zwischen-Separator) weitergebaut werden. +- **Foerderer-Ende-Abfrage + horizontale Fortsetzung + Vario-Kurve** (Abschn. 4). +- **Vertikalbogen-Winkelgleichheit** (Abschnitt 6): jeder Koerper faengt/endet + auf 3-Grad-Basis. +- **20-m-Regel** (Abschnitt 7): `vfl-20m-check` gibt den Hinweis aus. +- **GF-Verteilung waehlbar** (Abschnitt 9): ½ am Ausgang (GF2) oder alles am + Einlauf; Mindest-GF `*vfl-gf-min-laenge*` = 400 mm. +- **Separator-Platzierung** (Abschnitt 5): kein Separator hinter Motorstation/ + GF2; Separator + ES am Kettenende bzw. optionaler Zwischen-Separator. + `vfs-vf-exit` mit `mit-separator=nil` (Linienzug) bzw. `t` (Standard-VF). +- **FESTE_HORIZONTAL = 1300** im Linienzug (Abschnitt 10). +- **Fahrtrichtung folgt dem Vorgaenger** (Projektion), Start-Z-Abfrage (Abschn. 8/11). +- **Winkelauswahl** bei mehreren gueltigen Winkeln (Abschnitt 11). +- **Zahlenbasierte Menues** (Abschnitt 11). +- **ID-Vergabe** (`ssg-id-generate`) fuer Linienzug-, Standard-/Etage-VF- und + Gefaellestrecke-Bloecke. +- **Volle Block-Attribute** (Abschnitt 12): TYP="Streckengruppe", segmentweise + L_VF_m/L_GF_m/GF_WINKEL/ANTRIEBFAHRTRICHTUNG/WINKEL, MOTORSEITE, GF_Bogen_*/ + VF_Bogen_*-Zaehler. +- **Option 3 "Kettenende definieren"** (Abschnitt 4.5): Mid-Body-Zerlegung mit + reduziertem Budget (800), GF2 variiert mit Winkel (min 400), gewinkeltes + + horizontales VF, Auto-Abschluss (Motor->GF2->Separator->ES), Ist-Ziel-Report. +- **Flache Zone** (Abschnitt 6): horizontales Stueck endet flach, `ab_3` erst vor + einem 3-Grad-Element (`vfl-nach-3grad` / `vfl-frame-flach-p`). +- **Horizontale Separatoren** (Abschnitt 5): Separator am horizontalen Stueck in + der 0-Grad-Ebene (`vfl-sep-hz`). +- **Menue-Nummerierung lueckenlos** + **Vario-Kurve 90/60/30** (Abschnitt 11). +- **Regressionstest `TEST_LINIENZUG`** (in `tests/test_linienzug.lsp`, Skript aus + `tests/testdata/linienzug_tests.json`): spielt eine reale Session per + Eingabe-Mock ab (getpoint/getstring/getint/getreal), laesst den echten + `vf-linienzug-modus` durchlaufen. Punkt-Abfragen laufen ueber den + verhaltensneutralen Wrapper **`vfl-getpoint`** (feste 2-Arity), damit der Mock + greift. Zusaetzlich `TEST_LINIENZUG_STUFE2` (`linienzug_tests_stufe2.json`, + siehe `doc/vf_linienzug_modus1_anleitung.md`) fuer den Stufe-2-Fallback am + Kettenende. +- **Steigende GF (Sonderfall, bis 3 Grad)** (Abschnitt 3): eigener Zweig C in + "Neue Linie: GF" (`gf-laenge-modus="3"`), `vfl-bogen-uebergang` als + genereller Uebergangs-Helfer zwischen Basis-Neigungen (auch in Zweig A/B + eingehaengt, falls der Vorgaenger steigend war). +- **"Neue Linie BIS Kettenende" erkennt VF-/GF-Startpunkt** (Abschnitt 4.6): + `vfl-ks-aus-hz-an-punkt`/`letzter-typ`-basierte Familien-Erkennung, + VF-Familie baut ueber `vfl-body-abschluss` + `vfl-vf-ausgang-bauen` (wie + Option 3), GF-Familie ueber dieselbe Endpunkt-/Zielhoehe-Wahl wie "Neue + Linie: GF". +- **Abbruch-Trigger vollstaendig** (`vfl-in-value`/`-value-p`): ein per + Wizard-Dialog gesetztes Abbruch-Flag wird jetzt auch von den Funktionen mit + eigener Replay-/Pending-Logik erkannt (`vfl-in-abstand`, `vfl-in-selection`) + - vorher konnte ein Dialog-Abbruch dort auf einen rohen `getpoint` statt auf + Terminierung zurueckfallen. + +### Noch offen / zu pruefen (bitte in BricsCAD verifizieren) +1. **Laengen-Budget** bei mehrsegmentigen VF-Einheiten (Abschnitt 10) - + empirisch nachmessen. +2. **20-m-Regel** nur Hinweis, noch keine automatische Zwischen-Motorstation. +3. **Knick-Risiko** am Uebergang GF-Segment -> VF-Einheit (Entry startet immer + bei 3 Grad). +4. **AS-/ES-Fusspunkte** (`aus-dx` / `ein-dx`) werden nicht von der Segmentlaenge + abgezogen (anders als in `gf-linienzug-modus`). +5. **"Neue Linie BIS Kettenende" bei erkannter VF-Familie MIT Umlenkstation** + (letzter-typ="VF", eine gerade fertig gebaute VF-Einheit, oder ein per + KS_AUS erkannter reiner Umlenk-/Motor-/Bogen-Typ am Kettenanfang): faellt + bewusst auf die generische Automatik zurueck (Abschnitt 4.6) - noch NICHT + live in BricsCAD getestet. +6. **Regressionstests noch nicht nachgezogen**: keiner der Tests + (`test_linienzug.lsp`, `test_vf_spec.lsp`/`.py`) deckt bisher die neuen + Punkte oben ab (Steigende GF, Kettenende-Familien-Erkennung, + Abbruch-Trigger-Fix) - nur manuell in BricsCAD verifiziert. +7. **Modus 3** (`vf-linienzug-modus3`, Vorwaerts-Nachbau, siehe Eingabemodi in + Abschnitt 1): Grundgeruest gebaut (GF-Kette + VF-Laeufe per + Run-State-Machine, AS-Querschub auf Pfad). Offen: Laengen-Reservierung am + Kettenende (Exit-Fussabdruck ueberschiesst). Kann eine vorgegebene + Endpunkt-Hoehe NICHT treffen - dafuer ist Modus 2 vorhanden (Abschnitt 14). +8. **Option-3 Ist-Ziel-Report**: die Soll-Berechnung modelliert nur den geraden + Lauf und beruecksichtigt den **seitlichen Versatz des ES_Element_90** nicht + (dY ~ 637 mm ist erwartet, kein Fehler). dX/dZ treffen exakt. Zwei bekannte + Naeherungen koennten dennoch kleine Restabweichungen erzeugen: der + Eintrittsbogen wird mit 3-Grad-Basis gerechnet (Sonderfall: Start nach einer + horizontalen Vario-Kurve), und GF2 wird bei < 400 mm auf das Minimum + angehoben. Bei Bedarf empirisch nachjustieren. + +### Geloest +- **Vario-Kurve-Bloecke** (`data/ils/3D/Vario_Kurve_*`): fuehren ihre KS als + `KSYS_EIN`/`KSYS_AUS` (statt `KS_EIN`/`KS_AUS`); `extract-ks-from-block-raw` + normalisiert beide ueber `ks-normalize-name` - die Kurven docken korrekt an. +- **Versatz Separator/ES** (Bild-Befund): Ursache war das erzwungene Z-Ziel von + `insert-block-mixed-to-ks` (setzt ES-KS_AUS auf eine feste Hoehe - sinnvoll im + Standalone-Gefaelle mit vorgegebener Endhoehe, aber falsch im frei auslaufenden + Linienzug). Der Linienzug-ES wird jetzt rein per **KS-zu-KS** + (`insert-block-ks-to-ks`) an den Separator angekettet - ES-KS_EIN folgt exakt + dem Separator-Ausgang, kein Z-Ziel -> Versatz behoben (in BricsCAD gegenpruefen). +- **Vario-Kurve war 3 Grad geneigt**: der uebergebene Frame trug die + 3-Grad-Basisneigung. Die Kurve wird jetzt auf einem abgeflachten Frame (0 Grad) + eingefuegt (horizontaler Richtungswechsel). + +--- + +## 14. Modus 2: Pfad + Ziel-Hoehe (Randwert-Solver) + +> **Hinweis zur Nummerierung:** Dieser Solver ist im finalen Menue **Modus 2** +> (Befehl waehlt intern `vf-linienzug-modus2`); Modus 3 ist der aeltere +> Vorwaerts-Nachbau (`vf-linienzug-modus3`, siehe Eingabemodi in Abschnitt 1 +> und Abschnitt 13 Punkt 7). +> +> Die internen Helfer trugen bis 2026-10-22 die Praefixe `vfl3-`/`vfl2-` aus +> einer frueheren Planungsphase - und zwar GENAU VERTAUSCHT zur endgueltigen +> Menue-Zuordnung. Sie heissen jetzt nach ihrer ROLLE statt nach einer +> Modus-Nummer: +> +> | Praefix | Rolle | genutzt von | +> |---|---|---| +> | `vfl-zsolve-*` (frueher `vfl3-*`) | rechnet die Hoehen (Z) durch | nur Modus 2 | +> | `vfl-pfad-*` (frueher `vfl2-*`) | arbeitet auf dem gezeichneten Pfad (Eckwinkel, AS einfuegen, VF oeffnen/schliessen) | Modus 2 **und** Modus 3 | +> +> Eine Modus-Nummer war fuer die zweite Gruppe ohnehin nicht richtig +> vergebbar, weil drei dieser Funktionen von beiden Modi benutzt werden. + +Modus 3 (Vorwaerts-Nachbau) kann eine vorgegebene Endpunkt-Hoehe nicht treffen, +weil er die Kette Segment fuer Segment vorwaerts baut - die Endhoehe ist nur ein +Ergebnis (Anfangswertproblem / initial value problem). Modus 2 loest stattdessen +ein Randwertproblem (boundary value problem): Start UND Ziel (je X,Y,Z) sind +bekannt, die Zwischenhoehen werden gerechnet. + +### 14.0 Grundprinzip (zwei getrennte Probleme) +- XY = gezeichneter Pfad, reine KS-Kettung (wie Modus 3, mm-Drift akzeptiert). +- Z = wird gerechnet, sodass das Kettenende die Ziel-Hoehe EXAKT trifft. +Ablauf in zwei Phasen: erst planen (alle Hoehen durchrechnen), dann bauen. + +### 14.1 Entkopplung Neigung <-> XY +Die Neigung eines Teils aendert seine Hoehe (Z), aber nicht seinen +XY-Fussabdruck: ein Staustrecken-Teil mit Planlaenge L belegt in der Draufsicht +immer L (die 3D-Laenge waechst mit L/cos(w), die Projektion bleibt L). Deshalb +lassen sich Z-Ziel und Pfadtreue unabhaengig loesen. + +### 14.2 GF-Rollen +- GF1/GF2 INNERHALB einer VF-Einheit: fest 3 Grad, Laenge variabel (Ausgleich). +- Eigenstaendige GF-Laeufe: Neigung per Eingabe (wie Modus 3, Vorgabe 3 Grad). + +### 14.3 VF-Bruecke = Standard-VF-Einheit +Jede VF-Strecke im Pfad ist eine Standard-VF-Einheit ohne eigene AS/ES: +GF1 + Separator + Umlenkstation + Vario-Koerper + Motorstation + GF2. Alle Teile +liegen kolinear auf derselben Pfad-Geraden; variieren ihre Laengen, verschieben +sich nur die inneren Grenzen, die Bruecken-Enden bleiben fix. Pro Bruecke ein +2-Gleichungs-System: +- Summe der Planlaengen = gezeichnete Spannweite (aus dem Pfad) +- Summe der Hoehenaenderung = delta-H zwischen den Ankern +Freie Variablen: Vario-Winkel (diskret) + GF1-Laenge + GF2-Laenge. Das loest der +vorhandene Modus-1-Solver (berechne-alle-winkel / berechne-horizontale-mitte). + +### 14.4 Anker rechnen (Zwei-Anker / shooting) +- Front-Anker: Z von AS vorwaerts durch alle fuehrenden deterministischen Teile + (GF-Geraden mit Eingabe-Winkel + GF-Boegen) bis zur ersten VF-Bruecke. +- Rueck-Anker (Junction): Z_Ende RUECKWAERTS durch alle nachlaufenden + deterministischen Teile - reine Rechnung, kein Rueckwaerts-Bauen. +- delta-H_Bruecke = Z_Front - Z_Junction; Spannweite = Pfad-Planlaenge der + VF-Strecke (minus Fussabdruck etwaiger Vario-Kurven darin). + +### 14.5 Ecken in der Hoehen-Bilanz +- GF-Bogen: Z-Drop aus der KS_EIN->KS_AUS-Z-Differenz des Blocks + (Temp-Objekt + extract-ks-from-block, wie insert-block-mixed-to-ks). +- Vario-Kurve: auf 0 Grad geflacht -> delta-Z ~ 0 (verifizieren). + +### 14.6 Phasen +Phase A (planen): Pfad waehlen; Start (X,Y,Z)+AS-Seite; Ende (X,Y,Z)+ES-Seite; +sortieren/analysieren; Eck-Vorpruefung; Segmente klassifizieren (Ergebnis nur +speichern); deterministische delta-Z; Anker rechnen; Bruecke loesen. Nicht +loesbar -> Meldung + Abbruch. +Phase B (bauen): vorwaerts, KS-Kettung; alle Bau-Primitive existieren bereits +(vfl-pfad-as-gf-einfuegen, vfl-insert-gf-segment, vfl-insert-gf-bogen-block, +vfl-pfad-vf-oeffnen / vfs-vf-koerper / vfl-pfad-vf-schliessen mit GELOESTEN Werten, +vfl-insert-vario-kurve-block, vfl-insert-es-element, vfl-block-erstellen). + +### 14.7 Machbarkeit (warum eine Bruecke "infeasibel" sein kann) +Hoehen-Erhaltung (conservation of height): delta-H_gesamt = Z_Start - Z_Ende ist +fest und wird aufgeteilt in Summe(deterministische GF+Boegen) + delta-H_Bruecke. +Die deterministischen Anteile bestimmen die Benutzer-Winkel; der Rest muss die +Bruecke ueber ihre feste Spannweite liefern. + +WICHTIG - Vorzeichen ist nur Richtung, nicht Machbarkeit: Ein VF ist +ANGETRIEBEN und kann STEIGEN und FALLEN (VF-Auf / VF-Ab). Eine reine GF kann nur +fallen; steigen erzwingt daher immer VF (siehe Abschnitt 3). +- delta-H_Bruecke > 0 -> Bruecke faellt -> VF-Ab. +- delta-H_Bruecke < 0 -> Bruecke steigt -> VF-Auf (voellig gueltig, KEIN Fehler). + +Infeasibel wird es nur ueber die MAGNITUDE: die noetige mittlere Neigung +uebersteigt den Vario-Bereich (rund 3..51 Grad), oder die Spannweite fasst den +Pflicht-Fussabdruck nicht (Separator 300 + Umlenk 500 + Motor 500 + min. GF1/GF2 ++ mind. 1 Vario-Koerper). Dann: saubere Meldung statt Fehlbau. Flachere GF-Winkel +verschieben den Rest-delta-H der Bruecke. + +### 14.8 Staffelung +- M2a - EINE VF-Bruecke (GF ... VF ... GF): delta-H_Bruecke voll determiniert -> + eindeutig loesbar. Erster Wurf (Fokus). +- M2b - MEHRERE VF-Bruecken: die delta-H-Verteilung auf N Bruecken ist + unterbestimmt; Regel: die fehlende Hoehe je Bruecke wird abgefragt. Spaeter. + +### 14.9 Grenzen (bewusst) +Z trifft die Ziel-Hoehe EXAKT (Solver). XY landet auf dem Pfad mit mm-Drift +(reine KS-Kettung); der Endpunkt wird in XY nahe, nicht mathematisch exakt +getroffen. Exakter XY-Endschluss = eigenes Inverse-Kinematik-Problem, das mit +reiner KS-Kettung kollidiert - vorerst nicht Ziel. + +### 14.10 Wiederverwendung +Vorhanden: Pfad-Analyse (gf-sortiere-objekte / gf-analysiere-kette), Eck-Pruefung +(vfl-pfad-eckwinkel-pruefen, geteilt mit Modus 3), Klassifizierung (aus Modus 3), +alle Bau-Primitive, Bruecken-Solver (Modus 1). Neu (~mittel): delta-Z-Helfer +(vfl-zsolve-gf-dz / vfl-zsolve-bogen-dz / vfl-zsolve-kurve-dz), Vorwaerts/Rueckwaerts-Z- +Propagation, Endpunkt/ES-Setup, Zwei-Phasen-Orchestrierung +(`vf-linienzug-modus2`). + +### 14.11 Umsetzungsstand M2a (umgesetzt, in BricsCAD getestet) + +**Ablauf (`vf-linienzug-modus2`):** Pfad waehlen; Start (X,Y,Z) + AS-Winkel(30/90) ++ AS-Seite; Ende (X,Y,Z) + ES-Winkel + ES-Seite; sortieren/analysieren/Eck-Pruefung; +Segmente klassifizieren; VF-Lauf finden; Phase B (bauen). + +**Hoehe exakt (messen statt schaetzen):** +- **Front-Anker** wird GEMESSEN: AS + Front-GF werden zuerst gebaut, dann die reale + KS_AUS-Hoehe abgelesen (AS-Eigen-dz + Trimmung automatisch enthalten). +- **Junction (Rueck-Anker)** exakt rueckwaerts vom Ziel gerechnet (`vfl-zsolve-rueckanker`: + GF-Bogen bei Neigung, Separator, ES-dz). +- **GF2** = kleine gemessene Feinkorrektur bis zur Junction -> **dZ = 0**. + +**VF-Bruecke = Standard-Vario:** `berechne-alle-winkel` auf dem Kletter-Segment, +GF-Verteilung feste-hz = 800/1300 + Hoehen-Anpassung fuer Motor/Uebergaenge. Bei +mehreren gueltigen Winkeln waehlt der Nutzer (`vfl-waehle-winkel`). + +**Vario-Kurve = mehrsegmentiger VF-Lauf (eine VF-Einheit):** +- VF-Geraden + Vario-Kurven bilden EINEN Lauf (die Kurve unterbricht ihn nicht). +- **Kletterer = GENAU EINE VF-Gerade: die laengste des Laufs.** Alle anderen + werden **horizontal** (nur Anschluss). Verbindet also eine Vario-Kurve zwei + VF-Geraden, wird die kuerzere flach gebaut und die laengere traegt die ganze + Hoehe (Regel 2026-10-22). + Grund: der Solver budgetiert EIN Kletterstueck mit EINEM Paar Vertikalboegen, + der Bau setzt dieses Paar aber je Kletterer. Mit zwei Kletterern fehlten ein + zweites Bogenpaar UND die Uebergaenge der zusaetzlichen flachen Zone im + Laengenbudget - gemessen: erstes Segment 1355.9 mm zu lang, Kette insgesamt + 3185 mm zu lang, bei korrekter Hoehe (dZ -10.2 mm). + Die frueher benutzte Schwelle `*vfl-min-climber-laenge*` (Vorgabe 3000) spielt + fuer die Auswahl damit keine Rolle mehr. +- **Flache Zone:** genau EIN `auf_3` rein, EIN `ab_3` raus; Kurve + horizontale + Fueller sind bei 0 Grad DIREKT verbunden (keine Zwischen-Boegen). +- **Horizontaler Fueller** gleicht die Restlaenge aus (dein „Laenge folgt"). + +**GF-Verteilung waehlbar:** 1 = alles am Einlauf (GF1); 2 = 1/2 GF1 + 1/2 GF2. Bei +1/2/1/2 wird der im Kletter-Segment durch das halbe GF1 frei werdende Platz mit +einem horizontalen Fueller-A in VF1 gefuellt; GF2 sitzt hinter dem Motor. + +**AS/ES 30 oder 90 Grad waehlbar** (auch in Gefaellestrecke): +- Abfrage `vf-frage-element-winkel` vor der Seite; Blockname + `AS_Element_<30|90>_` / `ES_...`; Masse pro Wahl neu gesetzt + (`vf-set-as-masse` / `vf-set-es-masse`) -> berechne/GF rechnen variantengerecht. +- **AS-Schwenk GEMESSEN** (`vf-element-plan-turn`, KS_EIN.xu -> KS_AUS.xu), damit + KS_AUS entlang hz zeigt - unabhaengig von der Bauform (30/90/gerade). + +**AS/ES-Achs-Verankerung (Start-/Endpunkt liegen auf der KS-Achse):** +- **AS:** Versatz ENTLANG der KS_EIN-Achse (statt senkrecht zum Pfad) -> KS_AUS + auf dem Pfad UND Startpunkt auf der KS_EIN-Achse. +- **erste GF-Trimmung:** echter AS-Fussabdruck entlang des Pfads aus dem realen + KS_AUS (nicht Block-dx) -> GF-Bogen sitzt exakt auf der Ecke. +- **ES:** letzte Fueller-Laenge so geloest (`vfl-zsolve-es-fueller` + `vf-element-ks-info`), + dass der Endpunkt auf der ES-KS_AUS-Achse liegt (Spiegel der AS-Regel). + +**Ergebnis (Test 30 und 90 Grad):** dZ = 0; Startpunkt auf AS-KS_EIN-Achse; +Endpunkt auf ES-KS_AUS-Achse (Quer zur Achse ~0). Der Rest-Laengs-Wert ist die +seitliche ES-Ausladung (Bauform), kein Fehler. + +**Bekannte Rest-Abweichung (bewusst akzeptiert):** nach einem GF-Bogen driftet die +Mittellinie ~einige mm vom gezeichneten Pfad (reine KS-Kettung: Folge-Teile haengen +am Bogen-KS_AUS, nicht am gezeichneten Eckpunkt). Kein Re-Anchoring (Wunsch: Teile +schliessen exakt aneinander). + +### 14.12 Noch offen +- **25-m-Regel** im Pfad-Modus (VF-Kette zu lang -> Motorstation als Einheitsende). +- **M2b:** mehrere getrennte VF-Bruecken (delta-H-Verteilung abfragen). +- Standard-VarioFoerderer (`vf_standard.lsp`) bleibt bei 90 Grad (bewusst). diff --git a/lang/de_DE.json b/lang/de_DE.json index 6258539..ea88085 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -261,6 +261,10 @@ "vfl-m3-lauf-anfang-opt-offen": "\n 3 - Ohne Umlenkstation (offener Anfang, spaeter fortsetzen/verschmelzen)", "vfl-m3-lauf-anfang-opt-umlenk": "\n 2 - Umlenkstation direkt (kein GF1/Separator)", "vfl-m3-lauf-anfang-opt-umlenk-gf1": "\n 1 - Umlenkstation mit GF1 + Separator", + "vfl-m2-uebergang-bogen-fehlt": "Uebergangsbogen %1 fehlt in der Bogen-Tabelle - der Block ist nicht ladbar oder hat kein messbares KS. Der Lauf wird ohne seinen Laengenanteil gebaut und faellt dadurch um dessen Laenge zu lang aus.", + "vfl-m2-eck-korrektur": "\n Eck-Korrektur %1 mm auf Segment %2 (gezeichneter Bogen gegen Fussabdruck des Eck-Bauteils).", + "vfl-m2-abschluss-fussabdruck": "\n Abschluss-Fussabdruck %1 mm von Segment %2 abgezogen (Abschlussbogen + Motorstation stehen darauf).", + "vfl-m2-as-fussabdruck": "\n AS-Fussabdruck %1 mm von Segment %2 abgezogen (das AS-Element steht selbst darauf).", "vfl-m2-anfang-wert-unbekannt": "Lauf-Anfang nicht lesbar: Kette %1 enthaelt den unbekannten Wert %2. Aufbau abgebrochen - eine geratene Anfangsvariante wuerde die Kette falsch neu bauen.", "vfl-m3-lauf-anfang-offen-hinweis": "\n Anfang bleibt bewusst offen - keine Umlenkstation/GF1/Separator gebaut, spaeter fortsetzen oder per Vario_Kette_Merge verbinden.", "vfl-m2-anfang-offen-gesperrt-zeile": "\n (nicht waehlbar - mit AS-Element braucht der Lauf eine Umlenkstation)", diff --git a/lang/en_GB.json b/lang/en_GB.json index 5a764d0..eb05ad9 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -261,6 +261,10 @@ "vfl-m3-lauf-anfang-opt-offen": "\n 3 - No reversing station (open start, continue/merge later)", "vfl-m3-lauf-anfang-opt-umlenk": "\n 2 - Reversing station directly (no GF1/separator)", "vfl-m3-lauf-anfang-opt-umlenk-gf1": "\n 1 - Reversing station with GF1 + separator", + "vfl-m2-uebergang-bogen-fehlt": "Transition bend %1 is missing from the bend table - the block cannot be loaded or has no measurable KS. The run is built without its length share and therefore comes out too long by that amount.", + "vfl-m2-eck-korrektur": "\n Corner correction %1 mm on segment %2 (drawn arc versus footprint of the corner component).", + "vfl-m2-abschluss-fussabdruck": "\n Closing footprint %1 mm deducted from segment %2 (closing bend + motor station sit on it).", + "vfl-m2-as-fussabdruck": "\n AS footprint %1 mm deducted from segment %2 (the AS element sits on it).", "vfl-m2-anfang-wert-unbekannt": "Cannot read run start: chain %1 contains the unknown value %2. Build aborted - guessing a start variant would rebuild the chain incorrectly.", "vfl-m3-lauf-anfang-offen-hinweis": "\n Start deliberately left open - no reversing station/GF1/separator built; continue later or join with Vario_Kette_Merge.", "vfl-m2-anfang-offen-gesperrt-zeile": "\n (not selectable - with an AS element the run needs a reversing station)", diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index d9581e7..ad8330e 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -409,7 +409,7 @@ ;; vfl-par-gf-laenge - wie lang wird das GF-Stueck dazwischen? ;; Die Plan-Turn-Werte unten sind die Vorzeichen-Konvention aus ;; vf-element-plan-turn (KS_EIN -> KS_AUS in der XY-Ebene, links positiv). -(defun tu-tests-vfl-parallel ( / _b30 _b60 _b90 _kand _l _plan _plan3 _g _p _w) +(defun tu-tests-vfl-parallel ( / _b30 _b60 _b90 _kand _l _plan _plan3 _g _p _w _ez) (princ "\n[vf_linienzug Parallel zum Kreisel]") ;; --- Bogenwahl: der Bogen muss den AS-Schwenk GENAU aufheben --- @@ -516,6 +516,65 @@ (tu-nil "vfl-m2-anfang/kein-string" (vfl-m2-anfang-norm 3)) (tu-nil "vfl-m2-anfang/nil" (vfl-m2-anfang-norm nil)) + ;; --- Eck-Korrektur: gezeichneter Bogen gegen Eck-Bauteil --- + ;; Die Ecke des Pfads wird durch ein reales Bauteil ersetzt, dessen + ;; Fussabdruck abweichen darf. Die Differenz wird auf die Richtungen der + ;; beiden angrenzenden Geraden zerlegt. Werte aus dem echten Lauf vom + ;; 2026-10-26: 90-Grad-Ecke, Pfad R500 (Sehne 707.1 -> 500/-500), gebaute + ;; Vario-Kurve wirkt wie R406.2 -> je 93.78 mm auf beide Nachbargeraden. + (setq _ez (vfl-eck-zerlegen 500.0 -500.0 406.22 -406.22 (- (/ pi 2.0)))) + (tu-eqf "vfl-eck-zerlegen/90-grad-davor" 93.78 (car _ez)) + (tu-eqf "vfl-eck-zerlegen/90-grad-danach" 93.78 (cadr _ez)) + ;; 30-Grad-Ecke links, Gefaellebogen R500 trifft den Pfad fast exakt: + ;; nur 0.85 mm, und zwar ausschliesslich auf der Geraden DAVOR. + (setq _ez (vfl-eck-zerlegen 250.0 66.99 249.15 66.99 (/ pi 6.0))) + (tu-eqf "vfl-eck-zerlegen/30-grad-davor" 0.85 (car _ez)) + (tu-eqf "vfl-eck-zerlegen/30-grad-danach" 0.0 (cadr _ez)) + ;; Deckungsgleiches Bauteil -> kein Zuschlag auf keiner Seite. + (setq _ez (vfl-eck-zerlegen 500.0 -500.0 500.0 -500.0 (- (/ pi 2.0)))) + (tu-eqf "vfl-eck-zerlegen/gleich-davor" 0.0 (car _ez)) + (tu-eqf "vfl-eck-zerlegen/gleich-danach" 0.0 (cadr _ez)) + ;; Ohne Schwenk ist die Zerlegung nicht eindeutig - nil statt Division durch 0. + (tu-nil "vfl-eck-zerlegen/ohne-schwenk" + (vfl-eck-zerlegen 100.0 0.0 90.0 0.0 0.0)) + ;; GF-Boegen werden GENEIGT eingebaut: ihr Grundriss-Fussabdruck ist kuerzer + ;; als flach gemessen, und genau diese Differenz muss in die Eck-Korrektur. + ;; Werte aus dem echten Lauf 2026-10-26: Gefaellebogen_rechts_90_R500 flach + ;; dx=500 dz=-28.6, bei 3 Grad eingebaut 497.8 - die 2.2 mm blieben vorher + ;; als Rest am Kettenende stehen. Negatives dz muss dabei VERKUERZEN. + (tu-eqf "vfl-projiziere-neigung/gf-bogen-3-grad" + (- (* 500.0 (cos (* 3.0 (/ pi 180.0)))) + (* 28.6 (sin (* 3.0 (/ pi 180.0))))) + (car (vfl-projiziere-neigung 500.0 -28.6 3.0))) + (tu-true "vfl-projiziere-neigung/gf-bogen-kuerzer-als-flach" + (< (car (vfl-projiziere-neigung 500.0 -28.6 3.0)) 500.0)) + ;; Waagerecht eingebaut (Vario-Kurve) bleibt der Fussabdruck unveraendert. + (tu-eqf "vfl-projiziere-neigung/waagerecht" 500.0 + (car (vfl-projiziere-neigung 500.0 -28.6 0.0))) + + ;; --- ES-Reservierung auf die KS_AUS-Achse --- + ;; Vorgabe: quer zur KS_AUS-Achse MUSS 0 sein, laengs darf abweichen. + ;; Der reine Laengsanteil reicht dafuer NICHT - er laesst den bauteileigenen + ;; Querversatz stehen. Werte aus dem echten Lauf 2026-10-26: + ;; ES_Element_30_rechts an einer Geraden mit hz=-60, Schwenk -30 Grad, + ;; Fussabdruck (laengs 575.84 | quer -52.54). Reserve 484.8 statt 575.8 - + ;; die Gerade wird also rund 91 mm laenger und hebt den Querversatz auf. + (tu-eqf "vfl-es-achs-reserve/echt-30-grad" 484.838050570331 + (vfl-es-achs-reserve 575.84 -52.54 -60.0 -30.0)) + ;; Ohne Querversatz bleibt es beim reinen Laengsanteil. + (tu-eqf "vfl-es-achs-reserve/ohne-quer" 500.0 + (vfl-es-achs-reserve 500.0 0.0 -60.0 -30.0)) + ;; Ohne Schwenk faellt die Achse mit der Fahrtrichtung zusammen - dann gibt + ;; es keinen aufhebbaren Querversatz und der Laengsanteil ist richtig + ;; (Nenner 0, Rueckfall statt Division durch null). + (tu-eqf "vfl-es-achs-reserve/ohne-schwenk" 575.84 + (vfl-es-achs-reserve 575.84 -52.54 -60.0 0.0)) + ;; Richtungsunabhaengig: dieselbe Geometrie um 90 Grad gedreht gibt + ;; denselben Wert - die Reserve haengt an den Winkeln zueinander, nicht + ;; an der Lage im Welt-KS. + (tu-eqf "vfl-es-achs-reserve/gedreht" 484.838050570331 + (vfl-es-achs-reserve 575.84 -52.54 30.0 -30.0)) + ;; --- Rueckwaerts bauen: Punkt entgegen der Fahrtrichtung zuruecksetzen --- ;; vfl-rueck-punkt ist der Rechenkern des Rueckwaerts-Baus: zu einem ;; BEKANNTEN Endpunkt den Startpunkt eines Bausteins finden. Vorwaerts gilt