feat: Option 4 - dynamische S-Winkelwahl (3/6/9...Grad) wie Option 5

vfl-gf-auf-ab-solve bekommt einen 3. Parameter s-winkel (S's Neigung -
F_total bleibt immer bei *vfk-gefaelle-winkel*=3 Grad). Neue reine
Massen-Helfer vfl-projiziere-neigung + vfl-gf-bogen-summe (Formel-basiertes
Pendant zu vfl-gf-bogen-erweitern aus Option 5, da Option 4's Solver ohne
Live-Probe rechnet). vfl-qc-linie-mit-gf probiert s-winkel=3,6,9,... in
einer Schleife (max. 10 Versuche), bis F_total/S beide baubar sind - der
flachste passende Winkel wird automatisch gewaehlt, damit auch kuerzere
Mittelteil-Gesamtlaengen baubar sind statt nur bei einem festen Winkel.

Bei s-winkel=3 (extra-grad=0) reduziert sich die neue Formel nachweislich
exakt auf die alte, bereits bestaetigte Formel (Term-fuer-Term verifiziert)
- kein Regressionsrisiko fuer den bisherigen Grundfall. Vom Nutzer in
BricsCAD erfolgreich getestet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Yuelin Wang
2026-09-14 09:07:04 +02:00
co-authored by Claude Sonnet 5
parent ca5c21686f
commit 41419763c0
3 changed files with 125 additions and 33 deletions
+121 -31
View File
@@ -3446,6 +3446,48 @@
frame
)
;; Projiziert eine rohe (dx,dz)-Bogenmasse (bei Rotation 0 gemessen, siehe
;; get-bogen-mass) in die Welt, wenn der Bogen bei einer gegebenen Neigung
;; (Grad, gefaelle-winkel-Konvention: positiv=fallend) eingebaut wird -
;; dieselbe Rz(hz)*Ry(rot)-Projektion wie insert-rotated-block-with-ks, hier
;; OHNE Insert (reine Vorausberechnung fuer vfl-gf-auf-ab-solve). Rueckgabe:
;; (welt-dx welt-dz).
(defun vfl-projiziere-neigung (dx dz rot-grad / rad)
(setq rad (* (float rot-grad) (/ pi 180.0)))
(list (+ (* dx (cos rad)) (* dz (sin rad)))
(+ (- (* dx (sin rad))) (* dz (cos rad))))
)
;; Rein rechnerisches (KEIN Bau) Pendant zu vfl-gf-bogen-erweitern - fuer
;; Option 4's Formel-basierten Solver (vfl-gf-auf-ab-solve), der die
;; Neigungs-Erweiterung VORAB in die Fixterme einrechnen muss, BEVOR
;; ueberhaupt etwas gebaut wird (Nutzer-Vorgabe 2026-09-14: dieselbe
;; dynamische Winkelwahl wie Option 5, jetzt auch fuer Option 4). Summiert
;; die WELT-(dx,dz) einer Kette aus moeglichst wenigen Bausteinen
;; (moeglichst viele 6-Grad-, ein 3-Grad-Rest-Baustein), exakt in derselben
;; Reihenfolge/Neigungs-Abfolge wie vfl-gf-bogen-erweitern es spaeter ECHT
;; baut - extra-grad=0 liefert (0.0 0.0). Rueckgabe: (welt-dx welt-dz).
(defun vfl-gf-bogen-summe (richtung eingehende-neigung extra-grad /
rest neigung-jetzt m proj sum-dx sum-dz)
(setq sum-dx 0.0 sum-dz 0.0 rest extra-grad neigung-jetzt eingehende-neigung)
(while (>= rest 6)
(setq m (get-bogen-mass (if (= richtung "auf") *gf-bogen-auf* *gf-bogen-ab*) 6))
(setq proj (vfl-projiziere-neigung (car m) (caddr m) neigung-jetzt))
(setq sum-dx (+ sum-dx (car proj)) sum-dz (+ sum-dz (cadr proj)))
(setq neigung-jetzt (if (= richtung "auf") (- neigung-jetzt 6) (+ neigung-jetzt 6)))
(setq rest (- rest 6))
)
(if (= rest 3)
(progn
(setq m (get-bogen-mass (if (= richtung "auf") *gf-bogen-auf* *gf-bogen-ab*) 3))
(setq proj (vfl-projiziere-neigung (car m) (caddr m) neigung-jetzt))
(setq sum-dx (+ sum-dx (car proj)))
(setq sum-dz (+ sum-dz (cadr proj)))
)
)
(list sum-dx sum-dz)
)
;; 2D-Rotation eines LOKALEN (dx,dy)-Versatzes (KS_EIN->KS_AUS eines Blocks
;; bei Rotation 0, siehe vf-element-masse) um einen WELT-Richtungswinkel
;; (Grad) - dieselbe Rz(hz)-Rotationsformel wie in insert-block-ks-to-ks/
@@ -3490,14 +3532,20 @@
;; 2026-09-12, Geometrie danach nach BricsCAD-Test komplett ueberarbeitet):
;; automatisch berechnete Auf-Ab-GF-Kette, die auf derselben
;; Hoehe endet, auf der sie beginnt - AS -> fallend-GF -> 30-Grad-Bogen ->
;; [fallend-GF -> 6-Grad-Bogen auf -> steigend-GF -> 6-Grad-Bogen ab ->
;; fallend-GF] -> 30-Grad-Bogen (Gegenseite) -> fallend-GF -> ES. Die beiden
;; fallenden Mittelstuecke (vor/nach dem Steig-Stueck) werden als EIN
;; gemeinsamer Unbekannter F_total behandelt (Nutzer-Vorgabe, aufgeteilt
;; nach einem waehlbaren Anteil) - vfl-gf-auf-ab-solve loest F_total und das
;; steigende Mittelstueck S aus der vorgegebenen Mittelteil-Gesamtlaenge und
;; der geforderten Netto-Hoehenaenderung (hier immer 0, da AS/ES auf
;; derselben Hoehe liegen sollen).
;; [fallend-GF -> Bogen auf (6 Grad, ggf. steiler) -> steigend-GF -> Bogen ab
;; (6 Grad, ggf. steiler) -> fallend-GF] -> 30-Grad-Bogen (Gegenseite) ->
;; fallend-GF -> ES. Die beiden fallenden Mittelstuecke (vor/nach dem
;; Steig-Stueck) werden als EIN gemeinsamer Unbekannter F_total behandelt
;; (Nutzer-Vorgabe, aufgeteilt nach einem waehlbaren Anteil) -
;; vfl-gf-auf-ab-solve loest F_total und das steigende Mittelstueck S aus
;; der vorgegebenen Mittelteil-Gesamtlaenge und der geforderten Netto-
;; Hoehenaenderung (hier immer 0, da AS/ES auf derselben Hoehe liegen
;; sollen). S's Neigung wird dabei DYNAMISCH gewaehlt (Nutzer-Vorgabe
;; 2026-09-14, dieselbe Idee wie Option 5): 3 Grad (symmetrischer
;; Grundfall) zuerst, dann 6/9/12/...Grad, bis F_total UND S beide baubar
;; (>=0) sind - der FLACHSTE passende Winkel gewinnt, damit auch kuerzere
;; Mittelteil-Gesamtlaengen noch baubar sind, statt nur bei einem einzigen
;; fest verdrahteten Winkel zu funktionieren.
;;
;; Weltmass-Formel wie insert-rotated-block-with-ks (ssg_ks_insert.lsp):
;; world_dx = dx*cos(rot) + dz*sin(rot) (horizontal, entlang hz)
@@ -3506,38 +3554,60 @@
;; -gefaelle-winkel fuer den "ab"-Bogen (Einstieg steigend) - siehe
;; vfl-gf-bogen-uebergang, das dieselben Tabellen beim tatsaechlichen Bau
;; verwendet.
;; s-winkel (Nutzer-Vorgabe 2026-09-14: dieselbe dynamische Winkelwahl wie
;; Option 5 - "gegebene Laenge -> passenden Winkel waehlen"): die Neigung
;; des steigenden Mittelstuecks S (Grad, STEIGEND-Betrag - F_total bleibt
;; IMMER bei *vfk-gefaelle-winkel*, nur S wird bei Bedarf steiler). Der
;; Aufrufer probiert typischerweise s-winkel=3 (symmetrischer Grundfall),
;; dann 6, 9, ... bis gueltig=T (siehe vfl-qc-linie-mit-gf).
;; Rueckgabe: (f-total s gueltig ziel-laenge-min) - f-total/s als horizontale
;; Laengen (wie deltaL bei vfl-insert-gf-segment), gueltig=nil bei negativem
;; Ergebnis (Mittelteil-Vorgabe geometrisch nicht baubar). ziel-laenge-min ist
;; in diesem Fall die kleinste Gesamtlaenge, mit der f-total UND s beide >= 0
;; in diesem Fall die kleinste Gesamtlaenge, mit der f-total UND s BEIDE >= 0
;; wuerden (Nutzer-Vorgabe 2026-09-13: als Vorschlag in der Fehlermeldung
;; ausgeben) - hergeleitet aus a=f-total+s, c=s-f-total (a,c wie unten): damit
;; beide >=0 sind, muss a >= |c| gelten, also ziel-laenge-horiz >= fix-dx+|c|.
(defun vfl-gf-auf-ab-solve (ziel-laenge-horiz ziel-hoehe-delta /
rad3 tan3 m-auf m-ab dx-auf dz-auf dx-ab dz-ab
fix-dx fix-dz a b c f-total s gueltig ziel-laenge-min)
;; ausgeben) - aus F_total+S=a, -F_total*tan3+S*tan(s-winkel)=b (a,b wie
;; unten): S>=0 braucht a>=-b/tan3, F_total>=0 braucht a>=b/tan(s-winkel) -
;; das striktere (groessere) der beiden Minima entscheidet.
(defun vfl-gf-auf-ab-solve (ziel-laenge-horiz ziel-hoehe-delta s-winkel /
rad3 tan3 rad-s tan-s m-auf m-ab proj-auf proj-ab
extra-grad extra-auf extra-ab
fix-dx fix-dz a b f-total s gueltig
a-min-s a-min-ftotal a-min ziel-laenge-min)
(vfl-gf-bogen-init)
(setq rad3 (* (float (fix *vfk-gefaelle-winkel*)) (/ pi 180.0)))
(setq tan3 (/ (sin rad3) (cos rad3)))
(setq rad-s (* (float s-winkel) (/ pi 180.0)))
(setq tan-s (/ (sin rad-s) (cos rad-s)))
(setq m-auf (get-bogen-mass *gf-bogen-auf* 6))
(setq m-ab (get-bogen-mass *gf-bogen-ab* 6))
(setq dx-auf (car m-auf) dz-auf (caddr m-auf))
(setq dx-ab (car m-ab) dz-ab (caddr m-ab))
;; Fixterme: zwei Separatoren (300mm, fallend) + die zwei 6-Grad-Boegen,
;; jeweils per obiger Rotationsformel ins hz-Frame transformiert.
;; Basis-Uebergaenge (IMMER vorhanden, wie bisher): auf_6 bei Einstieg
;; fallend(+3), ab_6 bei Einstieg steigend(-3).
(setq proj-auf (vfl-projiziere-neigung (car m-auf) (caddr m-auf) (fix *vfk-gefaelle-winkel*)))
(setq proj-ab (vfl-projiziere-neigung (car m-ab) (caddr m-ab) (- (fix *vfk-gefaelle-winkel*))))
;; Zusaetzliche Erweiterung (nur falls s-winkel > *vfk-gefaelle-winkel*):
;; "auf"-Kette von -3 weiter auf -s-winkel, "ab"-Kette von -s-winkel
;; zurueck auf -3 (rein rechnerisch - vfl-gf-bogen-summe, echter Bau folgt
;; per vfl-gf-bogen-erweitern erst NACH dem Solve).
(setq extra-grad (- s-winkel (fix *vfk-gefaelle-winkel*)))
(setq extra-auf (vfl-gf-bogen-summe "auf" (- (fix *vfk-gefaelle-winkel*)) extra-grad))
(setq extra-ab (vfl-gf-bogen-summe "ab" (- s-winkel) extra-grad))
;; Fixterme: zwei Separatoren (300mm, fallend) + alle Bogen-Beitraege.
(setq fix-dx (+ (* 2.0 *vfk-separator-laenge* (cos rad3))
(* (cos rad3) (+ dx-auf dx-ab))
(* (sin rad3) (- dz-auf dz-ab))))
(car proj-auf) (car proj-ab)
(car extra-auf) (car extra-ab)))
(setq fix-dz (+ (* -2.0 *vfk-separator-laenge* (sin rad3))
(* (sin rad3) (- dx-ab dx-auf))
(* (cos rad3) (+ dz-auf dz-ab))))
(cadr proj-auf) (cadr proj-ab)
(cadr extra-auf) (cadr extra-ab)))
(setq a (- ziel-laenge-horiz fix-dx))
(setq b (- ziel-hoehe-delta fix-dz))
(setq c (/ b tan3))
(setq s (/ (+ a c) 2.0))
(setq f-total (/ (- a c) 2.0))
;; F_total+S=a, -F_total*tan3+S*tan-s=b => S=(b+a*tan3)/(tan3+tan-s)
(setq s (/ (+ b (* a tan3)) (+ tan3 tan-s)))
(setq f-total (- a s))
(setq gueltig (and (>= f-total -0.01) (>= s -0.01)))
(setq ziel-laenge-min (+ fix-dx (abs c)))
(setq a-min-s (- (/ b tan3)))
(setq a-min-ftotal (/ b tan-s))
(setq a-min (if (> a-min-ftotal a-min-s) a-min-ftotal a-min-s))
(setq ziel-laenge-min (+ fix-dx a-min))
(list f-total s gueltig ziel-laenge-min)
)
@@ -3553,6 +3623,7 @@
b-noetig tol hz0 hz1 hz2 e1 e1-brutto e2 anteil ziel-laenge
loesung f-total s gueltig fa fb hz-jetzt
erg-bogen soll-hoehe ist-hoehe antwort
s-winkel extra-grad rad-s versuche
rad3 tan3
p0x p0y px py vx vy esx esy dref-x dref-y
hz1-rad hz2-rad zaehler nenner abweichung
@@ -3779,14 +3850,28 @@
(- (* e2 tan3))
esz))
;; --- Solver: F_total/S aus vorgegebener Mittelteil-Gesamtlaenge und
;; der noch fehlenden Hoehenaenderung (Rest bis Gesamtbilanz=0) ---
(setq loesung (vfl-gf-auf-ab-solve ziel-laenge (- h-aussen-rest)))
(setq f-total (nth 0 loesung) s (nth 1 loesung) gueltig (nth 2 loesung))
;; der noch fehlenden Hoehenaenderung (Rest bis Gesamtbilanz=0) - S
;; beginnt beim symmetrischen Grundfall (3 Grad) und wird in 3-Grad-
;; Schritten steiler (6, 9, 12, ...) probiert, bis F_total/S beide
;; baubar sind (>=0) - der FLACHSTE passende Winkel wird automatisch
;; gewaehlt (Nutzer-Vorgabe 2026-09-14: dieselbe dynamische Winkelwahl
;; wie Option 5, "gegebene Laenge -> passenden Winkel waehlen"). ---
(setq s-winkel (fix *vfk-gefaelle-winkel*))
(setq versuche 0 gueltig nil)
(while (and (not gueltig) (< versuche 10))
(setq loesung (vfl-gf-auf-ab-solve ziel-laenge (- h-aussen-rest) s-winkel))
(setq f-total (nth 0 loesung) s (nth 1 loesung) gueltig (nth 2 loesung))
(if (not gueltig) (setq s-winkel (+ s-winkel (fix *vfk-gefaelle-winkel*))))
(setq versuche (1+ versuche))
)
(princ (ssg-textf "vfl-qc-s-winkel-echo" (list (itoa s-winkel))))
(if (not gueltig)
(progn
(princ (ssg-textf "vfl-qc-nicht-baubar" (list (rtos f-total 2 0) (rtos s 2 0) (rtos (nth 3 loesung) 2 0))))
(vfl-loesche-seit checkpoint))
(progn
(setq extra-grad (- s-winkel (fix *vfk-gefaelle-winkel*)))
(setq rad-s (* (float s-winkel) (/ pi 180.0)))
(setq fa (* f-total anteil))
(setq fb (- f-total fa))
;; --- Rest WIRKLICH bauen (physische Reihenfolge; AS/E1/Sep/1.
@@ -3798,10 +3883,15 @@
(setq frame (vfl-insert-separator frame))
(setq erg-bogen (vfl-gf-bogen-uebergang frame (- (fix *vfk-gefaelle-winkel*))))
(setq frame (car erg-bogen))
;; --- S bei s-winkel Grad (dynamisch gewaehlt): die zusaetzliche
;; Neigung wird mit ECHTEN Bausteinen erreicht (vfl-gf-bogen-
;; erweitern, wie bei Option 5). ---
(setq frame (vfl-gf-bogen-erweitern frame "auf" (- (fix *vfk-gefaelle-winkel*)) extra-grad))
(setq hz-jetzt (car (frame->hz-winkel frame)))
(setq frame (vfl-insert-gf-segment (car frame) hz-jetzt s (- (fix *vfk-gefaelle-winkel*))))
(vfl-acc-gf-seg (/ s (cos rad3)) (- (fix *vfk-gefaelle-winkel*)))
(setq frame (vfl-insert-gf-segment (car frame) hz-jetzt s (- s-winkel)))
(vfl-acc-gf-seg (/ s (cos rad-s)) (- s-winkel))
(setq anzahl-gf (1+ anzahl-gf))
(setq frame (vfl-gf-bogen-erweitern frame "ab" (- s-winkel) extra-grad))
(setq erg-bogen (vfl-gf-bogen-uebergang frame (fix *vfk-gefaelle-winkel*)))
(setq frame (car erg-bogen))
(setq hz-jetzt (car (frame->hz-winkel frame)))