fix: Fussabdruecke aller Festbauteile im Segmentbudget (Modus 2)

Leitsatz, aus dem alle Einzelaenderungen folgen (neu als Abschnitt 0 in
doc/VarioFoerderer_Linienzug_Prinzipien.md): die gezeichneten GERADEN
besitzen den Pfad, ein gezeichneter Bogen ist Hilfsgeometrie und nennt nur
Ort und Drehsinn. Jedes fest gebaute Teil zieht seine Laenge von dem
Segment ab, auf dem es steht - und zwar in LAENGEN- UND HOEHENbilanz mit
demselben, GEMESSENEN Wert.

Behoben:
- AS-Element: Fussabdruck wurde bei vf-start = 0 gar nicht abgezogen
  (die Trimmung lief nur im Front-Lauf, der dort leer ist). Jetzt gemessen
  aus dem gebauten KS_AUS - ein aus dem Block-dx gerechneter Wert lag
  184 mm daneben.
- Abschlussbogen + Motorstation: standen auf dem letzten Segment des
  VF-Laufs, fehlten aber im Budget (feste-vf traegt den Motor nur im Zweig
  OHNE flache Zone).
- ES-Element: die letzte Gerade reservierte nur den Laengsanteil. Jetzt die
  Strecke, die auch den Querversatz aufhebt (vfl-es-achs-reserve) - Vorgabe:
  quer zur KS_AUS-Achse MUSS 0 sein, laengs darf abweichen, dZ MUSS 0 sein.
- ES-Abzug griff auch, wenn ein Back-Lauf zwischen VF-Lauf und ES liegt,
  und verkuerzte dann das falsche Segment um die volle ES-Laenge (878 mm).
  Jetzt nur noch bei direktem Anschluss (vf-ende = n-1).
- Eck-Bauteile: Vario-Kurve/Gefaellebogen ersetzen den gezeichneten Bogen
  mit eigenem Fussabdruck. Die Differenz der Tangentenabstaende geht als
  Zuschlag an beide angrenzenden Geraden (vfl-eck-korrekturen/-zerlegen),
  damit die Mittellinie beide beruehrt. Der gezeichnete Radius kuerzt sich
  dabei heraus. GF-Boegen werden geneigt eingebaut - ihr Grundriss-
  Fussabdruck wird mit vfl-projiziere-neigung projiziert.
- Kletterlaenge wurde anteilig mit der Planlaenge im Zaehler und dem
  korrigierten climber-span im Nenner verteilt; die Anteile summierten sich
  dadurch auf weniger als 1.
- Absturz bei fehlendem Uebergangsbogen: get-bogen-mass wurde zweimal fuer
  dieselbe Suche aufgerufen und sein nil ungeprueft an
  vfl-projiziere-neigung gereicht.

Messreihe ueber die Sitzung (gleicher Pfad, Abweichung quer):
695.7 -> 270 -> 139.5 -> 98.5 -> 45.7 -> 2.4 -> 0.2 mm, dZ durchgehend 0.
Beide Pfad-Varianten (Ecke als Vario-Kurve bzw. als GF-Bogen) landen auf
demselben Wert - 0.2 mm ist die Messaufloesung, kein Restfehler.

21 neue file-freie Unit-Tests mit den echten Messwerten; TEST_UNIT 866/866.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 14:29:43 +01:00
parent aabac03d7c
commit ce4ad0f328
5 changed files with 1231 additions and 31 deletions
+341 -30
View File
@@ -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)))