Files
dxfmakros/Lisp/vf_etage.lsp
T
Yuelin WangandClaude Opus 5 3116e639eb fix: Etage-Solver bilanziert Ein-/Auslauf auf der Hauptachse statt im Blocksystem
Die gebaute Etage-Kette endete 987.63 mm zu weit (X) und 3.46 mm zu hoch (Z).
Der Fehlbetrag lag vollstaendig im Ein-/Auslauf: der Solver reservierte dort
1177.87 mm, real verbraucht die Kette 2165.09 mm.

Zwei Ursachen:

(a) vf-element-masse liefert KS_EIN->KS_AUS im BLOCK-Koordinatensystem. AS
    und ES werden aber um (hz - ein-ang) gedreht eingebaut - beim
    AS_Element_30 ist ein-ang = 90 Grad, dadurch landet die Block-Y-
    Komponente auf der Hauptachse. Bilanziert wurde die Block-X-Komponente:
    AS 209.7 statt real 486.10, ES -242.6 statt real 525.29.

(b) Die Auslauf-Staustrecke wurde fix mit 400 mm budgetiert, ist seit dem
    Umbau "Auslauf RUECKWAERTS vom ES gerechnet" aber variabel (im Testfall
    334 mm). Daraus die 3.46 mm Resthoehe.

Fix: neue Funktion etage-rand-budget (seite) rechnet Einlauf (Schritte 1-4)
und Auslauf (12-15) exakt so, wie etage-foerderanlage-einfuegen sie baut,
inklusive analytisch vorab bestimmtem L-exit. Kern sind die reinen,
hz-unabhaengigen Projektionen etage-laengs/etage-quer. Die alte Naeherung
bleibt als Fallback, falls ein Block fehlt.

Damit faellt auch die bisherige Annahme, Laengentreue und seitliche
Fluchtung des ES-KS_AUS seien nicht gleichzeitig loesbar: die Bauschritte
5-11 laufen auf der Hauptachse und tragen nichts zur Quer-Bilanz bei,
L-exit ist deshalb von L_GF/L_VF unabhaengig und vor dem Solve bestimmbar.

Weiter: Konstante *vfk-etage-stau-ein* (400 mm) ersetzt die nackte Zahl an
beiden Stellen; zwei neue lang-Keys (de+en, Paritaet 1095/1095); neuer
Regressionsblock tu-tests-etage in tests/test_unit.lsp mit dem AS/ES-Fall
(Suite 520/520 gruen).

In BricsCAD verifiziert: Soll-Endpunkt (11804.31, 3039.64, 4256.5) exakt
getroffen, Abweichung 0.00 mm in allen drei Achsen.

Offen: bei korrekter Bilanz bleiben im Testfall bei 33 Grad nur 16.13 mm
fuer beide GF-Strecken. Eine Mindest-GF-Laenge als Gueltigkeitskriterium
der Winkelwahl ist noch nicht eingebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-09 18:02:25 +02:00

969 lines
45 KiB
Common Lisp
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;; ============================================================
;; VF_ETAGE - Etage VarioFoerderer (Typ: "etage")
;; Version: 26.0
;; Abhaengigkeit: vf_core.lsp muss zuerst geladen sein
;; Geometrie: AS/ES 30 Grad, direkt uebereinander (60 Grad Hz-Winkel)
;; 15-Schritt-Einfuegung
;; ============================================================
;; Sicherstellen, dass vf_core.lsp geladen ist. Pfad zentral ueber ssg_core
;; (ssg-lisp-verzeichnis), mit Inline-Fallback falls ssg_core hier noch nicht
;; geladen ist (nur relevant beim isolierten Einzel-Laden dieser Datei).
(if (not (car (atoms-family 1 '("VF-TYP-REGISTRIEREN"))))
(progn
(setq *vf-etage-core-pfad*
(if (car (atoms-family 1 '("SSG-LISP-VERZEICHNIS")))
(ssg-lisp-verzeichnis)
(cond
((getenv "DXFM_LISP") (vl-string-translate "\\" "/" (getenv "DXFM_LISP")))
((and (boundp '*ssg-lisp-pfad*) *ssg-lisp-pfad*) *ssg-lisp-pfad*)
(t nil))))
(if *vf-etage-core-pfad*
(load (strcat *vf-etage-core-pfad* "/vf_core.lsp"))
(progn
(princ (ssg-text "vfe-warn-lisp-pfad"))
(exit)
)
)
)
)
;; ============================================================
;; TEIL 1: ETAGE-SPEZIFISCHE GLOBALE VARIABLEN
;; ============================================================
(setq etage-aus-dx nil etage-aus-dz nil)
(setq etage-ein-dx nil etage-ein-dz nil)
(setq etage-gef-dx nil etage-gef-dy nil etage-gef-dz nil)
(setq etage-gef-dx-rechts nil etage-gef-dz-rechts nil)
(setq etage-gef-dx-links nil etage-gef-dz-links nil)
(setq *etage-lib-initialized* nil)
(setq FESTE_ETAGE_MITTE 1000)
(setq ETAGE_SCHRAEG_LAENGE 700)
;; Schwenkwinkel der Ein-/Auslauf-Schraegsektionen: 30 Grad (AS_30/ES_30 +
;; 30-Grad-Gefaellebogen). War frueher 60. Nur noch im Laengen-/Hoehen-Budget
;; berechne-winkel-etage (schraeg-cos) verwendet; die fruehere absolute
;; hz-Platzierung wurde durch Frame-Verkettung (cur-hz) ersetzt.
(setq ETAGE_HZ_WINKEL 30)
;; ============================================================
;; TEIL 2: BIBLIOTHEK INITIALISIEREN (ETAGE-SPEZIFISCH)
;; ============================================================
(defun init-bibliothek-etage ( / temp-obj ks-data ks-ein-pos ks-aus-pos
gefaelle-blockname as30-blk es30-blk m)
(if (and *etage-lib-initialized* (not (tblsearch "BLOCK" (ssg-ils-blockname "AS_Element_30_rechts"))))
(setq *etage-lib-initialized* nil))
(if *etage-lib-initialized*
(progn (princ (ssg-text "vfe-bib-bereits-init")) t)
(progn
(princ (ssg-text "vfe-bib-initialisiere"))
;; Standard-Bogen-Daten laden (bogen-auf/bogen-ab benoetigt)
(if (not *lib-initialized*)
(init-bibliothek)
)
;; AS_30_rechts Masse extrahieren (KS_EIN->KS_AUS (dx dy dz) via vf-element-masse)
(princ (ssg-text "vfe-extrahiere-as30"))
(setq as30-blk (ensure-block-loaded "AS_Element_30_rechts"))
(ensure-block-loaded "AS_Element_30_links")
(setq m (if (tblsearch "BLOCK" as30-blk) (vf-element-masse "AS_Element_30_rechts")))
(if m
(progn
(setq etage-aus-dx (car m) etage-aus-dz (caddr m))
(princ (ssg-textf "vfe-as30-mass" (list (rtos etage-aus-dx 2 1) (rtos etage-aus-dz 2 1))))
)
(progn
(princ (ssg-text (if (tblsearch "BLOCK" as30-blk) "vfe-warn-ks-fehlt-standard" "vfe-warn-block-as30-fehlt")))
(setq etage-aus-dx *vfk-etage-as-fallback-dx* etage-aus-dz *vfk-etage-as-fallback-dz*)
)
)
;; ES_30_rechts Masse extrahieren
(princ (ssg-text "vfe-extrahiere-es30"))
(setq es30-blk (ensure-block-loaded "ES_Element_30_rechts"))
(ensure-block-loaded "ES_Element_30_links")
(setq m (if (tblsearch "BLOCK" es30-blk) (vf-element-masse "ES_Element_30_rechts")))
(if m
(progn
(setq etage-ein-dx (car m) etage-ein-dz (caddr m))
(princ (ssg-textf "vfe-es30-mass" (list (rtos etage-ein-dx 2 1) (rtos etage-ein-dz 2 1))))
)
(progn
(princ (ssg-text (if (tblsearch "BLOCK" es30-blk) "vfe-warn-ks-fehlt-standard" "vfe-warn-block-es30-fehlt")))
(setq etage-ein-dx *vfk-etage-es-fallback-dx* etage-ein-dz *vfk-etage-es-fallback-dz*)
)
)
;; Gefaellebogen_links_30 Masse extrahieren
(setq gefaelle-blockname "Gefaellebogen_links_30_R500")
(princ (ssg-textf "vfe-extrahiere-block-mass" (list gefaelle-blockname)))
(setq gefaelle-blockname (ensure-block-loaded gefaelle-blockname))
(setq m (if (tblsearch "BLOCK" gefaelle-blockname) (vf-element-masse "Gefaellebogen_links_30_R500")))
(if m
(setq etage-gef-dx-links (car m) etage-gef-dz-links (caddr m))
(progn
(princ (ssg-text (if (tblsearch "BLOCK" gefaelle-blockname) "vfe-warn-kein-ks-links" "vfe-warn-block-fehlt-links")))
(setq etage-gef-dx-links 0.0 etage-gef-dz-links *vfk-etage-gefaellebogen-fallback-dz*)
)
)
(princ (ssg-textf "vfe-gefaellebogen-links-mass" (list (rtos etage-gef-dx-links 2 2) (rtos etage-gef-dz-links 2 2))))
;; Gefaellebogen_rechts_30 Masse extrahieren
(setq gefaelle-blockname "Gefaellebogen_rechts_30_R500")
(princ (ssg-textf "vfe-extrahiere-block-mass" (list gefaelle-blockname)))
(setq gefaelle-blockname (ensure-block-loaded gefaelle-blockname))
(setq m (if (tblsearch "BLOCK" gefaelle-blockname) (vf-element-masse "Gefaellebogen_rechts_30_R500")))
(if m
(setq etage-gef-dx-rechts (car m) etage-gef-dz-rechts (caddr m))
(progn
(princ (ssg-text (if (tblsearch "BLOCK" gefaelle-blockname) "vfe-warn-kein-ks-rechts" "vfe-warn-block-fehlt-rechts")))
(setq etage-gef-dx-rechts 0.0 etage-gef-dz-rechts *vfk-etage-gefaellebogen-fallback-dz*)
)
)
(princ (ssg-textf "vfe-gefaellebogen-rechts-mass" (list (rtos etage-gef-dx-rechts 2 2) (rtos etage-gef-dz-rechts 2 2))))
(setq *etage-lib-initialized* t)
(princ (ssg-text "vfe-bib-init-erfolg"))
t
)
)
)
;; ============================================================
;; TEIL 3: EINFUEGEFUNKTIONEN MIT KOMBINIERTER HZ+VERT ROTATION
;; ============================================================
;; Transformationsmatrix: Rz(hz) * Ry(vert)
;; [ cos(hz)*cos(vert) -sin(hz) cos(hz)*sin(vert) ]
;; [ sin(hz)*cos(vert) cos(hz) sin(hz)*sin(vert) ]
;; [ -sin(vert) 0 cos(vert) ]
;; HINWEIS: insert-hz-incl-scaled-block wurde entfernt (2026-09-18) - toter Code,
;; kein Aufrufer im Repo. Die verbleibenden drei insert-hz-*-Funktionen bleiben,
;; weil sie sich in der Platzierungssemantik (KS_EIN vs. Block-Ursprung) bzw. der
;; Skalierung+KS+Vertikale-Kombination von den ssg_ks_insert-Primitiven
;; unterscheiden und ein Ersatz die Geometrie verschoebe (byte-genaue Pruefung).
(defun insert-hz-rotated-block-with-ks (blockname startpunkt hz-winkel vert-winkel block-dx block-dz /
rad-hz rad-vert chz shz cv sv
matrix block-obj endpunkt)
(setq rad-hz (* (float hz-winkel) (/ pi 180.0)))
(setq rad-vert (* (float vert-winkel) (/ pi 180.0)))
(setq chz (cos rad-hz) shz (sin rad-hz))
(setq cv (cos rad-vert) sv (sin rad-vert))
(setq blockname (ensure-block-loaded blockname))
(if (not (tblsearch "BLOCK" blockname))
(progn
(princ (ssg-textf "vfe-block-fehlt-abbruch" (list blockname)))
(exit)
)
)
(setq block-obj (vla-InsertBlock modelspace
(vlax-3D-point '(0 0 0)) blockname 1.0 1.0 1.0 0))
;; Einzelteil auf die eigene Ebene der Blockdefinition legen - wie in den
;; Zwillingen in ssg_ks_insert.lsp; hier fehlte der Aufruf bisher.
(ssg-ils-block-auf-ebene block-obj blockname)
(setq matrix (vf-rot-matrix chz shz cv sv))
(vla-TransformBy block-obj (vlax-tmatrix matrix))
(vla-Move block-obj (vlax-3D-point '(0 0 0)) (vlax-3D-point startpunkt))
(setq endpunkt (list
(+ (car startpunkt) (+ (* block-dx chz cv) (* block-dz chz sv)))
(+ (cadr startpunkt) (+ (* block-dx shz cv) (* block-dz shz sv)))
(+ (caddr startpunkt) (+ (* (- sv) block-dx) (* cv block-dz)))))
(princ (ssg-textf "vfe-hz-rot-status"
(list blockname
(strcat (rtos hz-winkel 2 0) (chr 176))
(strcat (rtos vert-winkel 2 0) (chr 176))
(rtos (caddr endpunkt) 2 1))))
endpunkt
)
(defun insert-hz-rotated-block-by-ks (blockname startpunkt hz-winkel vert-winkel /
rad-hz rad-vert chz shz cv sv
temp-obj block-obj ks-data ks-ein ks-aus
p-ein p-aus p-ein-rot p-aus-rot
offset ausgang)
(setq blockname (ensure-block-loaded blockname))
(if (not (tblsearch "BLOCK" blockname))
(progn
(princ (ssg-textf "vfe-block-fehlt-abbruch" (list blockname)))
(exit)
)
)
(setq rad-hz (* (float hz-winkel) (/ pi 180.0)))
(setq rad-vert (* (float vert-winkel) (/ pi 180.0)))
(setq chz (cos rad-hz) shz (sin rad-hz))
(setq cv (cos rad-vert) sv (sin rad-vert))
;; KS-Positionen aus Cache (Block bei 0 Grad am Ursprung)
(setq temp-obj (vla-InsertBlock modelspace (vlax-3D-point '(0 0 0)) blockname 1.0 1.0 1.0 0))
(setq ks-data (extract-ks-from-block temp-obj))
(if (not (vlax-erased-p temp-obj)) (vla-Delete temp-obj))
(setq ks-ein (cadr (assoc "KS_EIN" ks-data)))
(setq ks-aus (cadr (assoc "KS_AUS" ks-data)))
;; Block einfuegen und rotieren
(setq block-obj (vla-InsertBlock modelspace (vlax-3D-point '(0 0 0)) blockname 1.0 1.0 1.0 0))
;; Einzelteil auf die eigene Ebene der Blockdefinition legen - wie in den
;; Zwillingen in ssg_ks_insert.lsp; hier fehlte der Aufruf bisher.
(ssg-ils-block-auf-ebene block-obj blockname)
(vla-TransformBy block-obj (vlax-tmatrix (vf-rot-matrix chz shz cv sv)))
(if (and ks-ein ks-aus)
(progn
(setq p-ein (car ks-ein))
(setq p-aus (car ks-aus))
;; Rotation auf KS-Punkte anwenden: p' = Rz(hz)*Ry(vert) * p
(setq p-ein-rot
(list (+ (* chz cv (car p-ein)) (- (* shz (cadr p-ein))) (* chz sv (caddr p-ein)))
(+ (* shz cv (car p-ein)) (* chz (cadr p-ein)) (* shz sv (caddr p-ein)))
(+ (* (- sv) (car p-ein)) (* cv (caddr p-ein)))))
(setq p-aus-rot
(list (+ (* chz cv (car p-aus)) (- (* shz (cadr p-aus))) (* chz sv (caddr p-aus)))
(+ (* shz cv (car p-aus)) (* chz (cadr p-aus)) (* shz sv (caddr p-aus)))
(+ (* (- sv) (car p-aus)) (* cv (caddr p-aus)))))
;; Block verschieben: rotiertes KS_EIN auf startpunkt
(vla-Move block-obj
(vlax-3D-point p-ein-rot)
(vlax-3D-point startpunkt))
(setq offset (list (- (car startpunkt) (car p-ein-rot))
(- (cadr startpunkt) (cadr p-ein-rot))
(- (caddr startpunkt) (caddr p-ein-rot))))
(setq ausgang (list (+ (car p-aus-rot) (car offset))
(+ (cadr p-aus-rot) (cadr offset))
(+ (caddr p-aus-rot) (caddr offset))))
(princ (ssg-textf "vfe-hz-rotks-status"
(list blockname
(strcat (rtos hz-winkel 2 0) (chr 176))
(rtos (caddr ausgang) 2 1))))
ausgang
)
(progn
(princ (ssg-textf "vfe-ks-fehlt-startpunkt" (list blockname)))
startpunkt
)
)
)
(defun insert-hz-scaled-block-by-ks (blockname startpunkt laenge hz-winkel vert-winkel /
rad-hz rad-vert chz shz cv sv scale
temp-obj block-obj ks-data ks-ein ks-aus
p-ein p-aus p-ein-s p-aus-s
p-ein-rot p-aus-rot offset ausgang)
(setq blockname (ensure-block-loaded blockname))
(if (not (tblsearch "BLOCK" blockname))
(progn
(princ (ssg-textf "vfe-block-fehlt-abbruch" (list blockname)))
(exit)
)
)
(setq scale (/ (float laenge) 1000.0))
(setq rad-hz (* (float hz-winkel) (/ pi 180.0)))
(setq rad-vert (* (float vert-winkel) (/ pi 180.0)))
(setq chz (cos rad-hz) shz (sin rad-hz))
(setq cv (cos rad-vert) sv (sin rad-vert))
;; KS bei Massstab 1
(setq temp-obj (vla-InsertBlock modelspace (vlax-3D-point '(0 0 0)) blockname 1.0 1.0 1.0 0))
(setq ks-data (extract-ks-from-block temp-obj))
(if (not (vlax-erased-p temp-obj)) (vla-Delete temp-obj))
(setq ks-ein (cadr (assoc "KS_EIN" ks-data)))
(setq ks-aus (cadr (assoc "KS_AUS" ks-data)))
;; Block mit XScale einfuegen und rotieren
(setq block-obj (vla-InsertBlock modelspace (vlax-3D-point '(0 0 0)) blockname scale 1.0 1.0 0))
;; Einzelteil auf die eigene Ebene der Blockdefinition legen - wie in den
;; Zwillingen in ssg_ks_insert.lsp; hier fehlte der Aufruf bisher.
(ssg-ils-block-auf-ebene block-obj blockname)
(vla-TransformBy block-obj (vlax-tmatrix (vf-rot-matrix chz shz cv sv)))
(if (and ks-ein ks-aus)
(progn
(setq p-ein (car ks-ein))
(setq p-aus (car ks-aus))
;; X-Skalierung anwenden
(setq p-ein-s (list (* scale (car p-ein)) (cadr p-ein) (caddr p-ein)))
(setq p-aus-s (list (* scale (car p-aus)) (cadr p-aus) (caddr p-aus)))
;; Rotation: p' = Rz(hz)*Ry(vert) * p
(setq p-ein-rot
(list (+ (* chz cv (car p-ein-s)) (- (* shz (cadr p-ein-s))) (* chz sv (caddr p-ein-s)))
(+ (* shz cv (car p-ein-s)) (* chz (cadr p-ein-s)) (* shz sv (caddr p-ein-s)))
(+ (* (- sv) (car p-ein-s)) (* cv (caddr p-ein-s)))))
(setq p-aus-rot
(list (+ (* chz cv (car p-aus-s)) (- (* shz (cadr p-aus-s))) (* chz sv (caddr p-aus-s)))
(+ (* shz cv (car p-aus-s)) (* chz (cadr p-aus-s)) (* shz sv (caddr p-aus-s)))
(+ (* (- sv) (car p-aus-s)) (* cv (caddr p-aus-s)))))
;; Block verschieben
(vla-Move block-obj
(vlax-3D-point p-ein-rot)
(vlax-3D-point startpunkt))
(setq offset (list (- (car startpunkt) (car p-ein-rot))
(- (cadr startpunkt) (cadr p-ein-rot))
(- (caddr startpunkt) (caddr p-ein-rot))))
(setq ausgang (list (+ (car p-aus-rot) (car offset))
(+ (cadr p-aus-rot) (cadr offset))
(+ (caddr p-aus-rot) (caddr offset))))
(princ (ssg-textf "vfe-hz-scaleks-status"
(list blockname (rtos laenge 2 0)
(strcat (rtos hz-winkel 2 0) (chr 176))
(rtos (caddr ausgang) 2 1))))
ausgang
)
(progn
(princ (ssg-textf "vfe-ks-fehlt-startpunkt" (list blockname)))
startpunkt
)
)
)
;; ============================================================
;; TEIL 3b: RAND-BUDGET (EINLAUF / AUSLAUF)
;; ============================================================
;; Die Etage-Kette wird VON BEIDEN ENDEN ZUR MITTE gerechnet: deltaL/deltaH
;; sind der Abstand AS-KS_EIN -> ES-KS_AUS (Nutzer-Vorgabe 2026-10-09).
;; Einlauf (Schritte 1-4) und Auslauf (Schritte 12-15 in
;; etage-foerderanlage-einfuegen) sind damit FESTE Randstuecke, deren
;; Verbrauch der Solver exakt kennen muss; frei ist nur der Mittelteil
;; (L_GF/L_VF).
;;
;; Warum nicht mehr schlicht (car (vf-element-masse ...)) wie frueher:
;; vf-element-masse liefert KS_EIN->KS_AUS im BLOCK-Koordinatensystem. AS und
;; ES werden aber um (bezugs-hz - ein-ang) gedreht eingebaut - beim
;; AS_Element_30 ist ein-ang 90 Grad, dadurch landet die Block-Y-Komponente
;; (vy) auf der Hauptachse und die Block-X-Komponente (vx) quer dazu. Der
;; alte Code zog vx von deltaL ab und verfehlte die Laenge um rund 990 mm
;; (realer Bug, Nutzer-Befund 2026-10-09: Soll-X 12179.96, gebaut 13167.59;
;; AS real 486.10 statt 209.70, ES real 525.29 statt -242.60).
;;
;; Die Projektion auf die Hauptachse ist hz-UNABHAENGIG: ein um
;; theta = (bezugs-hz - ein-ang) gedreht eingebauter Blockvektor (vx vy)
;; liefert mit d = (theta - hz)
;; laengs = vx*cos(d) - vy*sin(d)
;; quer = vx*sin(d) + vy*cos(d)
;; und d besteht nur aus blockinternen Winkeln. Darum braucht diese Funktion
;; kein hz und gilt fuer jede Hauptrichtung.
;;
;; Rueckgabe-Alist (nil, wenn ein Block fehlt - Aufrufer faellt dann auf die
;; alte Naeherung zurueck):
;; "ein-dx"/"ein-dz" : Laengen-/Hoehenverbrauch Einlauf (dz negativ = fallend)
;; "aus-dx"/"aus-dz" : dito Auslauf
;; "l-exit" : variable Auslauf-Staustrecke (Schritt 13). NICHT fest
;; 400 mm, sondern so gewaehlt, dass ES-KS_AUS
;; querversatzfrei auf der Hauptachse landet - dieselbe
;; Bedingung, die etage-foerderanlage-einfuegen loest
;; (dort aus den realen Punkten gemessen). Wer die
;; Einfuegereihenfolge aendert, muss BEIDE Stellen
;; nachziehen.
;; Laengs-/Queranteil eines um d (rad) gedrehten Blockvektors (vx vy) bezogen
;; auf die Hauptachse. Reine Rechnung, in tests/test_unit.lsp getestet.
(defun etage-laengs (vx vy d) (- (* vx (cos d)) (* vy (sin d))))
(defun etage-quer (vx vy d) (+ (* vx (sin d)) (* vy (cos d))))
(defun etage-rand-budget (seite / as-blk es-blk gef-blk
as-i es-i gef-i as-m es-m gef-m
grad c3 s3 sep stau-ein
a-as d-as a-es a-gef d-gef
vx-as vy-as vx-es vy-es vx-gef vy-gef
d1 d23 d4 d12 d1314 d15
ein-dx ein-dz quer-fix quer-rate l-exit
aus-dx aus-dz)
(if (not *etage-lib-initialized*) (init-bibliothek-etage))
(setq as-blk (strcat "AS_Element_30_" seite)
es-blk (strcat "ES_Element_30_" seite))
;; Gefaellebogen auf der GEGENSEITE des AS-Elements - exakt die Wahl, die
;; etage-foerderanlage-einfuegen trifft (dort gleicher Ausdruck).
(setq gef-blk (if (= seite "rechts")
"Gefaellebogen_links_30_R500"
"Gefaellebogen_rechts_30_R500"))
(setq as-i (vf-element-ks-info as-blk)
es-i (vf-element-ks-info es-blk)
gef-i (vf-element-ks-info gef-blk)
as-m (vf-element-masse as-blk)
es-m (vf-element-masse es-blk)
gef-m (vf-element-masse gef-blk))
(if (or (null as-i) (null es-i) (null gef-i)
(null as-m) (null es-m) (null gef-m))
nil
(progn
(setq grad (/ pi 180.0))
(setq c3 (cos (* *vfk-gefaelle-winkel* grad))
s3 (sin (* *vfk-gefaelle-winkel* grad))
sep *vfk-separator-laenge*
stau-ein *vfk-etage-stau-ein*)
;; ein-ang und Schwenk (aus-ang - ein-ang) je Block
(setq a-as (car as-i) d-as (- (cadr as-i) (car as-i))
a-es (car es-i)
a-gef (car gef-i) d-gef (- (cadr gef-i) (car gef-i)))
(setq vx-as (caddr as-i) vy-as (cadddr as-i)
vx-es (caddr es-i) vy-es (cadddr es-i)
vx-gef (caddr gef-i) vy-gef (cadddr gef-i))
;; Richtungsdifferenzen d = (Einbau-hz - hz) je Bauschritt, in rad.
;; cur-hz laeuft: hz -> (hz + d-as) ab Schritt 2 -> zurueck auf hz ab
;; Schritt 5 (der Einlaufbogen dreht um d-gef = -d-as) -> (hz + d-gef)
;; ab Schritt 13.
(setq d1 (* (- a-as) grad) ; 1 AS-Element
d23 (* d-as grad) ; 2 Staustrecke, 3 Separator
d4 (* (- d-as a-gef) grad) ; 4 Gefaellebogen Anfang
d12 (* (- a-gef) grad) ; 12 Gefaellebogen Ende
d1314 (* d-gef grad) ; 13 Staustrecke, 14 Separator
d15 (* (- d-gef a-es) grad)) ; 15 ES-Element
;; Fuer Strecken der Laenge L bei 3 Grad Neigung gilt
;; laengs = L*c3*cos(d), quer = L*c3*sin(d).
;; --- Einlauf: AS + Staustrecke 400 + Separator + Gefaellebogen
(setq ein-dx (+ (etage-laengs vx-as vy-as d1)
(* stau-ein c3 (cos d23))
(* sep c3 (cos d23))
(etage-laengs vx-gef vy-gef d4)))
(setq ein-dz (+ (caddr as-m)
(- (* stau-ein s3))
(- (* sep s3))
(caddr gef-m)))
;; --- Auslauf: Gefaellebogen + L-exit + Separator + ES
;; L-exit aus der Quer-Bilanz: alle Querbeitraege muessen sich zu 0
;; addieren, damit ES-KS_AUS auf der Hauptachse landet. Die Schritte
;; 5-11 laufen auf der Hauptachse und tragen NICHTS zu quer bei - darum
;; ist L-exit von L_GF/L_VF unabhaengig und hier schon bestimmbar.
(setq quer-fix (+ (etage-quer vx-as vy-as d1)
(* stau-ein c3 (sin d23))
(* sep c3 (sin d23))
(etage-quer vx-gef vy-gef d4)
(etage-quer vx-gef vy-gef d12)
(* sep c3 (sin d1314))
(etage-quer vx-es vy-es d15)))
(setq quer-rate (* c3 (sin d1314)))
(setq l-exit (if (> (abs quer-rate) 1e-6)
(/ (- quer-fix) quer-rate)
*vfk-etage-stau-ein*))
(if (< l-exit *vfk-etage-auslauf-min-clamp*)
(setq l-exit *vfk-etage-auslauf-min-clamp*))
(setq aus-dx (+ (etage-laengs vx-gef vy-gef d12)
(* l-exit c3 (cos d1314))
(* sep c3 (cos d1314))
(etage-laengs vx-es vy-es d15)))
(setq aus-dz (+ (caddr gef-m)
(- (* l-exit s3))
(- (* sep s3))
(caddr es-m)))
(list (cons "ein-dx" ein-dx) (cons "ein-dz" ein-dz)
(cons "aus-dx" aus-dx) (cons "aus-dz" aus-dz)
(cons "l-exit" l-exit))
)
)
)
;; ============================================================
;; TEIL 4: WINKELBERECHNUNG (ETAGE)
;; ============================================================
(defun berechne-winkel-etage (deltaL deltaH richtung seite /
winkel-list ergebnis-liste A B cos3 sin3
bogen-x1 bogen-z1 bogen-x2 bogen-z2
abs-dz-AUS abs-dz-EIN abs-dz-GEF
L_GF L_VF cosα sinα
winkel-eff cosEff sinEff rad5 rad7
winkel mass1 mass2 gueltig
best-winkel best-L_GF best-L_VF
feste-gesamt-cos feste-gesamt-sin
schraeg-hz-rad schraeg-cos schraeg-z
budget rand-dx rand-dz)
(if (not *etage-lib-initialized*)
(progn
(alert (ssg-text "vfe-alert-bib-nicht-init"))
(exit)
)
)
(setq abs-dz-AUS (abs etage-aus-dz)
abs-dz-EIN (abs etage-ein-dz)
abs-dz-GEF (abs etage-gef-dz))
;; Rand-Budget (Einlauf + Auslauf) exakt so, wie die Kette gebaut wird.
;; Faellt NUR zurueck, wenn ein Block fehlt - dann greift die alte
;; Naeherung, damit das Verhalten ohne Bibliothek unveraendert bleibt.
(setq budget (etage-rand-budget seite))
(if budget
(progn
(setq rand-dx (+ (cdr (assoc "ein-dx" budget)) (cdr (assoc "aus-dx" budget))))
;; dz der Randstuecke ist negativ (fallend); das Budget zaehlt Betraege.
(setq rand-dz (- (+ (cdr (assoc "ein-dz" budget)) (cdr (assoc "aus-dz" budget)))))
(princ (ssg-textf "vfe-rand-budget"
(list (rtos (cdr (assoc "ein-dx" budget)) 2 2)
(rtos (cdr (assoc "aus-dx" budget)) 2 2)
(rtos (cdr (assoc "l-exit" budget)) 2 1))))
)
(progn
(princ (ssg-text "vfe-rand-budget-fallback"))
(setq rand-dx nil rand-dz nil)
)
)
(setq winkel-list *vfk-bogen-winkel*)
(setq ergebnis-liste '())
(setq cos3 (cos (* *vfk-gefaelle-winkel* (/ pi 180.0)))
sin3 (sin (* *vfk-gefaelle-winkel* (/ pi 180.0))))
(setq feste-gesamt-cos (* FESTE_ETAGE_MITTE cos3))
(setq feste-gesamt-sin (* FESTE_ETAGE_MITTE sin3))
(setq schraeg-hz-rad (* (float ETAGE_HZ_WINKEL) (/ pi 180.0)))
(setq schraeg-cos (* (cos schraeg-hz-rad) cos3))
(princ "\n\n=========================================")
(princ (ssg-text "vfe-berechnung-header"))
(princ "\n=========================================")
(princ (ssg-textf "vfe-delta-info" (list (rtos deltaL 2 2) (rtos deltaH 2 2) richtung)))
(foreach winkel winkel-list
(if (= richtung "Auf")
(progn
(setq mass1 (get-bogen-mass bogen-auf winkel))
(setq mass2 (get-bogen-mass bogen-ab winkel))
)
(progn
(setq mass1 (get-bogen-mass bogen-ab winkel))
(setq mass2 (get-bogen-mass bogen-auf winkel))
)
)
(if (or (null mass1) (null mass2))
(progn
(princ (ssg-textf "vfe-bogen-nicht-vorhanden" (list (strcat (itoa winkel) (chr 176)))))
(setq ergebnis-liste (cons (list winkel nil nil nil) ergebnis-liste))
)
(progn
(setq rad5 (* *vfk-gefaelle-winkel* (/ pi 180.0)))
(setq rad7 (* (float (if (= richtung "Auf") (- *vfk-gefaelle-winkel* winkel) (+ winkel *vfk-gefaelle-winkel*)))
(/ pi 180.0)))
(setq bogen-x1 (+ (* (car mass1) (cos rad5)) (* (caddr mass1) (sin rad5))))
(setq bogen-z1 (+ (* (- (car mass1)) (sin rad5)) (* (caddr mass1) (cos rad5))))
(setq bogen-x2 (+ (* (car mass2) (cos rad7)) (* (caddr mass2) (sin rad7))))
(setq bogen-z2 (+ (* (- (car mass2)) (sin rad7)) (* (caddr mass2) (cos rad7))))
(setq cosα (cos (* winkel (/ pi 180.0)))
sinα (sin (* winkel (/ pi 180.0))))
(setq winkel-eff (if (= richtung "Auf") (- winkel 3) (+ winkel 3)))
(setq cosEff (cos (* winkel-eff (/ pi 180.0)))
sinEff (sin (* winkel-eff (/ pi 180.0))))
;; A: horizontaler Rest nach Abzug aller festen Teile.
;; feste-gesamt-cos/-sin (FESTE_ETAGE_MITTE=1000 = Umlenkstation+
;; Motorstation KOMBINIERT, vf_konstanten.lsp) tritt genau EINMAL in
;; der Kette auf (die eine mittlere Sektion zwischen den beiden
;; Vertikalboegen, Schritte 6+10 in etage-foerderanlage-einfuegen).
;; Der fruehere Faktor 2.0 reservierte sie doppelt - behoben
;; 2026-10-08 (52.34 mm = FESTE_ETAGE_MITTE*sin(3 Grad)).
;;
;; rand-dx/rand-dz kommen aus etage-rand-budget und ersetzen die
;; frueheren Naeherungen (etage-aus-dx + etage-ein-dx +
;; 2*ETAGE_SCHRAEG_LAENGE*schraeg-cos + 2*etage-gef-dx). Die waren in
;; der Laenge um rund 990 mm daneben, weil sie die BLOCK-X-Komponente
;; von AS/ES verwendeten statt deren Anteil auf der Hauptachse, und
;; die Auslauf-Staustrecke fest mit 400 mm statt variabel ansetzten
;; (siehe Kommentar bei etage-rand-budget).
(setq schraeg-z (* ETAGE_SCHRAEG_LAENGE sin3))
(if rand-dx
(setq A (- deltaL rand-dx bogen-x1 bogen-x2 feste-gesamt-cos))
(setq A (- deltaL
etage-aus-dx
etage-ein-dx
(* 2.0 ETAGE_SCHRAEG_LAENGE schraeg-cos)
(* 2.0 etage-gef-dx)
bogen-x1
bogen-x2
feste-gesamt-cos))
)
;; B: vertikaler Rest (Vorzeichen analog Standard)
(if rand-dz
(if (= richtung "Auf")
(setq B (+ deltaH rand-dz feste-gesamt-sin
(- bogen-z1) (- bogen-z2)))
(setq B (+ (- deltaH rand-dz feste-gesamt-sin)
bogen-z1 bogen-z2))
)
(if (= richtung "Auf")
(setq B (+ deltaH
abs-dz-AUS
abs-dz-EIN
(* 2.0 schraeg-z)
(* 2.0 abs-dz-GEF)
(- bogen-z1)
(- bogen-z2)
feste-gesamt-sin))
(setq B (+ (- deltaH
abs-dz-AUS
abs-dz-EIN
(* 2.0 schraeg-z)
(* 2.0 abs-dz-GEF)
feste-gesamt-sin)
bogen-z1
bogen-z2))
)
)
(setq ergebnis-liste
(cons (vf-winkel-solve winkel richtung A B sinα cosEff sinEff sin3 cos3)
ergebnis-liste))
)
)
)
(setq ergebnis-liste (reverse ergebnis-liste))
(princ "\n\n=================================================================================")
(princ (ssg-text "vfe-ergebnistabelle-header"))
(princ "\n=================================================================================")
(princ (ssg-text "vfe-tabelle-spalten"))
(princ "\n=================================================================================")
(setq best-winkel nil best-L_GF nil best-L_VF nil)
(foreach item ergebnis-liste
(setq winkel (car item)
L_GF (cadr item)
L_VF (caddr item)
gueltig (cadddr item))
(if (and (numberp L_GF) (numberp L_VF))
(progn
(princ (ssg-textf "vfe-tabelle-zeile"
(list (strcat (itoa winkel) (chr 176)) (fmt L_GF) (fmt L_VF))))
(if gueltig
(progn
(princ (ssg-text "vfe-status-gueltig"))
(if (or (null best-winkel) (< winkel best-winkel))
(setq best-winkel winkel best-L_GF L_GF best-L_VF L_VF)
)
)
(princ (ssg-text "vfe-status-ungueltig"))
)
)
(princ (ssg-textf "vfe-tabelle-zeile-fehler" (list (strcat (itoa winkel) (chr 176)))))
)
)
(princ "\n=================================================================================")
(if best-winkel
(princ (ssg-textf "vfe-empfohlener-winkel" (list (strcat (itoa best-winkel) (chr 176)))))
(princ (ssg-text "vfe-kein-winkel-gefunden"))
)
(list best-winkel best-L_GF best-L_VF ergebnis-liste)
)
;; ============================================================
;; TEIL 5: BERECHNE-WRAPPER (Schnittstelle fuer Registry)
;; ============================================================
;; Initialisiert Etage-Bibliothek, aktiviert Gefaellebogen-Masse
;; passend zur gewaehlten Seite (benoetigt fuer Geometrieberechnung).
(defun berechne-etage (deltaL deltaH richtung seite)
(init-bibliothek-etage)
(if (= seite "rechts")
(progn
(setq etage-gef-dx etage-gef-dx-rechts)
(setq etage-gef-dz etage-gef-dz-rechts))
(progn
(setq etage-gef-dx etage-gef-dx-links)
(setq etage-gef-dz etage-gef-dz-links))
)
(princ (ssg-textf "vfe-gefaellebogen-seite-dx" (list seite (rtos etage-gef-dx 2 2))))
(berechne-winkel-etage deltaL deltaH richtung seite)
)
;; ============================================================
;; TEIL 6: EINFUEGEN (15 Schritte, ohne VF_*-Block-Erstellung)
;; ============================================================
;; Gibt endpunkt zurueck.
;; VF_*-Block-Erstellung erfolgt zentral im Dispatcher (vf_core.lsp).
;; hz: Richtung der Hauptlinie (Default 0 = horizontal/X). AS-KS_EIN und
;; ES-KS_AUS liegen koaxial auf dieser Linie; die Kette wird frame-verkettet
;; aufgebaut (cur-hz laeuft durch, Boegen drehen um ihren gemessenen Schwenk).
(defun etage-foerderanlage-einfuegen (deltaL deltaH richtung
best-winkel L_GF1 L_GF2 L_VF
startpunkt seite hz
/ aktueller-punkt bogen-name endpunkt
bogen-mass bogen-dx bogen-dz
as-block es-block gefaelle-blockname
as-hz as-ksinfo
cur-hz bogen-info es-info
perp-sin perp-cos p2b-perp es-place-rad
es-wx es-wy es-perp perp-rate L-exit)
(setq aktueller-punkt startpunkt)
;; hz = Richtung der Hauptlinie (AS-KS_EIN / ES-KS_AUS liegen darauf).
;; Default 0 (horizontal), falls der Aufrufer nichts uebergibt.
(if (null hz) (setq hz 0.0))
(setq hz (float hz))
(setq as-block (strcat "AS_Element_30_" seite))
(setq es-block (strcat "ES_Element_30_" seite))
;; Gefaellebogen auf der GEGENSEITE des AS-Elements (Nutzer-Vorgabe
;; 2026-07-21): AS links -> Boegen rechts, AS rechts -> Boegen links.
;; Beide Etage-Boegen nutzen dieselbe (Gegen-)Seite.
(setq gefaelle-blockname
(if (= seite "rechts")
"Gefaellebogen_links_30_R500"
"Gefaellebogen_rechts_30_R500"
))
;; Sentinel: Bibliothek neu initialisieren wenn Bloecke fehlen
(if (and *etage-lib-initialized* (not (tblsearch "BLOCK" (ssg-ils-blockname "AS_Element_30_rechts"))))
(setq *etage-lib-initialized* nil))
(if (not *etage-lib-initialized*) (init-bibliothek-etage))
;; 1. AS_30 - KS_EIN koaxial zur Hauptlinie (hz) ausrichten.
;; vf-element-ks-info liefert (ein-ang aus-ang vx vy): ein-ang/aus-ang sind die
;; Ebenen-Winkel der KS_EIN- bzw. KS_AUS-Achse IM BLOCK (bei Einbau-Rotation 0).
;; Platzierungs-hz = (hz - ein-ang) dreht die KS_EIN-Achse von ein-ang auf hz
;; -> KS_EIN liegt koaxial auf der Hauptlinie (Abnahmekriterium).
(princ (ssg-textf "vfe-step1-as30" (list as-block)))
(setq as-ksinfo (vf-element-ks-info as-block))
(setq aktueller-punkt
(insert-block-by-ks as-block aktueller-punkt (- hz (car as-ksinfo))))
;; KS_AUS-Richtung (absolut) = hz + Schwenk (aus-ang - ein-ang); die folgende
;; GF laeuft in dieser Richtung weiter (Frame-Verkettung wie im Linienzug).
(setq as-hz (+ hz (- (cadr as-ksinfo) (car as-ksinfo))))
;; 2. Staustrecke (fest, folgt der AS-Richtung as-hz, 3 Grad vert, KS_EIN).
;; Dieselbe Konstante bilanziert etage-rand-budget als Einlauf-Anteil.
(princ (ssg-textf "vfe-step2" (list (rtos as-hz 2 1))))
(setq aktueller-punkt
(insert-hz-scaled-block-by-ks "Staustrecke_SP_1000_mm" aktueller-punkt
*vfk-etage-stau-ein* as-hz *vfk-gefaelle-winkel*))
;; 3. Separator (folgt der AS-Richtung as-hz, 3 Grad vert)
(princ (ssg-textf "vfe-step3" (list (rtos as-hz 2 1))))
(setq aktueller-punkt
(insert-hz-rotated-block-with-ks "Staustrecke_Separator_SP_300_mm" aktueller-punkt
as-hz *vfk-gefaelle-winkel* *vfk-separator-laenge* 0))
;; Ab hier laufende Richtung cur-hz mitfuehren (frame-verkettet). Start =
;; KS_AUS-Richtung des AS (as-hz). Jeder Gefaellebogen dreht cur-hz um seinen
;; gemessenen Schwenk (Vorzeichen/Seite kommen aus dem Block).
(setq cur-hz as-hz)
;; 4. Gefaellebogen Anfang (verkettet: KS_EIN auf cur-hz, dann cur-hz drehen)
(setq bogen-info (vf-element-ks-info gefaelle-blockname))
(princ (ssg-textf "vfe-step4" (list gefaelle-blockname (rtos cur-hz 2 1))))
(setq aktueller-punkt
(insert-hz-rotated-block-by-ks gefaelle-blockname aktueller-punkt
(- cur-hz (car bogen-info)) 0))
(setq cur-hz (+ cur-hz (- (cadr bogen-info) (car bogen-info))))
;; 5. L_GF1 (3 Grad geneigt)
(if (> L_GF1 0.1)
(progn
(princ (ssg-textf "vfe-step5-strecke" (list (rtos L_GF1 2 2))))
(setq aktueller-punkt
(insert-inclined-scaled-block "Staustrecke_SP_1000_mm" aktueller-punkt L_GF1 *vfk-gefaelle-winkel* cur-hz))
)
(princ (ssg-text "vfe-step5-skip"))
)
;; 6. Umlenkstation (3 Grad geneigt)
(princ (ssg-text "vfe-step6"))
(setq aktueller-punkt
(insert-rotated-block-with-ks (strcat "Vario_Umlenkstation_500mm_" (vf-motorseite-aktuell)) aktueller-punkt *vfk-gefaelle-winkel* *vfk-stations-laenge* 0 cur-hz))
;; 7. 1. Vertikalbogen (3 Grad geneigt)
(if (= richtung "Auf")
(progn
(setq bogen-name (strcat "Vario_Bogen_auf_" (itoa best-winkel) "_TEF_rechts"))
(setq bogen-mass (get-bogen-mass bogen-auf best-winkel))
)
(progn
(setq bogen-name (strcat "Vario_Bogen_ab_" (itoa best-winkel) "_TEF_rechts"))
(setq bogen-mass (get-bogen-mass bogen-ab best-winkel))
)
)
(setq bogen-dx (car bogen-mass) bogen-dz (caddr bogen-mass))
(princ (ssg-textf "vfe-step7" (list bogen-name)))
(setq aktueller-punkt
(insert-rotated-block-with-ks bogen-name aktueller-punkt *vfk-gefaelle-winkel* bogen-dx bogen-dz cur-hz))
;; 8. Variable Strecke L_VF
(if (> L_VF 0.1)
(progn
(if (= richtung "Auf")
(progn
(princ (ssg-textf "vfe-step8-auf" (list (itoa (- (- best-winkel 3))) (rtos L_VF 2 2))))
(setq aktueller-punkt
(insert-inclined-scaled-block "Staustrecke_SP_1000_mm" aktueller-punkt
L_VF (- (- best-winkel 3)) cur-hz))
)
(progn
(princ (ssg-textf "vfe-step8-ab" (list (itoa (+ best-winkel 3)) (rtos L_VF 2 2))))
(setq aktueller-punkt
(insert-inclined-scaled-block "Staustrecke_SP_1000_mm" aktueller-punkt
L_VF (+ best-winkel 3) cur-hz))
)
)
)
(princ (ssg-text "vfe-step8-skip"))
)
;; 9. 2. Vertikalbogen
(if (= richtung "Auf")
(progn
(setq bogen-name (strcat "Vario_Bogen_ab_" (itoa best-winkel) "_TEF_rechts"))
(setq bogen-mass (get-bogen-mass bogen-ab best-winkel))
)
(progn
(setq bogen-name (strcat "Vario_Bogen_auf_" (itoa best-winkel) "_TEF_rechts"))
(setq bogen-mass (get-bogen-mass bogen-auf best-winkel))
)
)
(setq bogen-dx (car bogen-mass) bogen-dz (caddr bogen-mass))
(if (= richtung "Auf")
(progn
(princ (ssg-textf "vfe-step9-bogen" (list bogen-name (itoa (- 3 best-winkel)))))
(setq aktueller-punkt
(insert-rotated-block-with-ks bogen-name aktueller-punkt
(- 3 best-winkel) bogen-dx bogen-dz cur-hz))
)
(progn
(princ (ssg-textf "vfe-step9-bogen" (list bogen-name (itoa (+ best-winkel 3)))))
(setq aktueller-punkt
(insert-rotated-block-with-ks bogen-name aktueller-punkt
(+ best-winkel 3) bogen-dx bogen-dz cur-hz))
)
)
;; 10. Motorstation (3 Grad geneigt)
(princ (ssg-text "vfe-step10"))
(setq aktueller-punkt
(insert-rotated-block-with-ks (strcat "Vario_Motorstation_500mm_" (vf-motorseite-aktuell)) aktueller-punkt *vfk-gefaelle-winkel* *vfk-stations-laenge* 0 cur-hz))
;; 11. L_GF2 (3 Grad geneigt)
(if (> L_GF2 0.1)
(progn
(princ (ssg-textf "vfe-step11-gf2" (list (rtos L_GF2 2 2))))
(setq aktueller-punkt
(insert-inclined-scaled-block "Staustrecke_SP_1000_mm" aktueller-punkt L_GF2 *vfk-gefaelle-winkel* cur-hz))
)
(princ (ssg-text "vfe-step11-skip"))
)
;; 12. Gefaellebogen Ende (verkettet: KS_EIN auf cur-hz, dann cur-hz drehen)
(setq bogen-info (vf-element-ks-info gefaelle-blockname))
(princ (ssg-textf "vfe-step12" (list gefaelle-blockname (rtos cur-hz 2 1))))
(setq aktueller-punkt
(insert-block-by-ks gefaelle-blockname aktueller-punkt (- cur-hz (car bogen-info))))
(setq cur-hz (+ cur-hz (- (cadr bogen-info) (car bogen-info))))
;; --- Auslauf RUECKWAERTS vom ES gerechnet (analog Linienzug Modus 2):
;; die Auslauf-Staustrecke wird variabel, sodass das ES-KS_AUS OHNE
;; seitlichen Versatz auf der Hauptachse landet. Reihenfolge (Nutzer-Vorgabe):
;; Staustrecke -> Separator -> ES.
(setq es-info (vf-element-ks-info es-block))
;; perp = Einheitsvektor senkrecht zur Hauptlinie hz: (-sin hz, cos hz)
(setq perp-sin (sin (* hz (/ pi 180.0)))
perp-cos (cos (* hz (/ pi 180.0))))
;; seitliche Lage nach Bogen2 (perp-Abstand des aktuellen Punkts zu startpunkt)
(setq p2b-perp (+ (* (- (car aktueller-punkt) (car startpunkt)) (- perp-sin))
(* (- (cadr aktueller-punkt) (cadr startpunkt)) perp-cos)))
;; ES-Eigenversatz KS_EIN->KS_AUS (Block vx/vy) in Auslaufrichtung gedreht -> perp
(setq es-place-rad (* (- cur-hz (car es-info)) (/ pi 180.0)))
(setq es-wx (- (* (caddr es-info) (cos es-place-rad)) (* (cadddr es-info) (sin es-place-rad))))
(setq es-wy (+ (* (caddr es-info) (sin es-place-rad)) (* (cadddr es-info) (cos es-place-rad))))
(setq es-perp (+ (* es-wx (- perp-sin)) (* es-wy perp-cos)))
;; perp-Auslenkung pro Laengeneinheit bei cur-hz (Neigung 3 Grad)
(setq perp-rate (* (cos (* *vfk-gefaelle-winkel* (/ pi 180.0)))
(sin (* (- cur-hz hz) (/ pi 180.0)))))
;; Loesen: p2b-perp + (L_exit + 300)*perp-rate + es-perp = 0
;; ACHTUNG: etage-rand-budget loest dieselbe Bedingung rein analytisch
;; vorab, damit der Solver den Auslauf korrekt bilanziert. Hier wird
;; p2b-perp aus den REALEN Punkten gemessen (numerisch genauer); beide
;; Wege muessen denselben Wert liefern. Wer die Schrittfolge oder die
;; Quer-Bilanz aendert, zieht BEIDE Stellen nach.
(setq L-exit
(if (> (abs perp-rate) 1e-6)
(- (/ (- (+ p2b-perp es-perp)) perp-rate) *vfk-separator-laenge*)
*vfk-etage-stau-ein*))
(if (< L-exit *vfk-etage-auslauf-min-clamp*) (setq L-exit *vfk-etage-auslauf-min-clamp*)) ; Schutz gegen negative/zu kurze GF
;; 13. Auslauf-Staustrecke (variabel, folgt cur-hz, 3 Grad vert, KS_EIN)
(princ (ssg-textf "vfe-step13" (list (rtos L-exit 2 1) (rtos cur-hz 2 1))))
(setq aktueller-punkt
(insert-hz-scaled-block-by-ks "Staustrecke_SP_1000_mm" aktueller-punkt
L-exit cur-hz *vfk-gefaelle-winkel*))
;; 14. Separator (verkettet, folgt cur-hz, 3 Grad vert)
(princ (ssg-textf "vfe-step14" (list (rtos cur-hz 2 1))))
(setq aktueller-punkt
(insert-hz-rotated-block-with-ks "Staustrecke_Separator_SP_300_mm" aktueller-punkt
cur-hz *vfk-gefaelle-winkel* *vfk-separator-laenge* 0))
;; 15. ES_30 (verkettet: KS_EIN auf cur-hz; KS_AUS auf der Hauptlinie)
(princ (ssg-textf "vfe-step15" (list es-block (rtos cur-hz 2 1))))
(setq endpunkt
(insert-block-by-ks es-block aktueller-punkt (- cur-hz (car es-info))))
(princ "\n\n=========================================")
(princ (ssg-text "vfe-eingefuegt"))
endpunkt
)
;; ============================================================
;; TEIL 7: DCL-DIALOG (Neuanlage + Edit)
;; ============================================================
;; Der Etage-Typ nutzt dieselben Dialoge wie der Standard-Typ
;; (vfs-dialog-eingabe-basis / vfs-dialog-winkel-verteilung in vf_standard.lsp).
;; Unterschiede zum Standard-Ablauf: Berechnung ueber berechne-etage (braucht
;; seite), Einfuegung ueber etage-foerderanlage-einfuegen, KEINE horizontale
;; Zwischenstrecke (Winkel-Sentinel 0). XDATA-Marker "etage".
;; Duenner Wrapper auf den gemeinsamen vf-dialog-berechnen-einfuegen (vf_core).
;; Etage = OHNE horizontale Zwischenstrecke (horizontal-p nil).
(defun vfe-etage-dialog-berechnen-einfuegen (vf-nummer deltaL deltaH richtung startpunkt hz seite
winkel-prefill geruest-einzelmodul geruest-typ)
(vf-dialog-berechnen-einfuegen "etage" vf-nummer deltaL deltaH richtung startpunkt hz seite
winkel-prefill geruest-einzelmodul geruest-typ nil))
;; Neuanlage (Menue/Konsole -> Dialog): Startpunkt (X/Y) picken, Basis-Dialog,
;; dann gemeinsame Berechnung/Winkel-Dialog/Einfuegung.
;; Analog vfs-standard-dialog-ablauf in vf_standard.lsp.
(defun vfe-etage-dialog-ablauf (vf-nummer)
(vf-dialog-ablauf "etage" vf-nummer nil))
;; Edit eines bestehenden Etage-VF-Blocks (VF_n mit SSG_VF_EDIT-XDATA "etage").
;; Wird von c:VARIOFOERDERER_EDIT (vf_standard.lsp) per Typ-Dispatch gerufen und
;; laeuft innerhalb der dort geoeffneten ssg-start-Sitzung. Liest Parameter aus
;; XDATA/Attributen, zeigt sie vorbelegt und baut bei OK am selben Startpunkt neu
;; auf. Analog dem Standard-Edit-Body in c:VARIOFOERDERER_EDIT.
(defun vfe-edit-ent (ent / ed bname startpunkt attribs xd
deltaL deltaH richtung z-start hz seite dim
eingabe vf-nummer best-winkel-attr L_GF_m-teile
prefill-basis prefill-winkel basis-lesung
geruest-einzelmodul geruest-typ motorseite)
(setq ed (entget ent))
(setq bname (cdr (assoc 2 ed)))
(setq xd (vfs-xdata-lesen ent))
;; Bau-Parameter + prefill-basis lesen (gemeinsame Leseroutine in
;; vf_standard.lsp, siehe vfs-edit-basis-lesen). Etage-Defaults: deltaH "0",
;; Seite "links".
(setq basis-lesung (vfs-edit-basis-lesen ent xd "0" "links"))
(setq prefill-basis (cdr (assoc "prefill-basis" basis-lesung)))
(setq attribs (cdr (assoc "attribs" basis-lesung)))
(setq startpunkt (cdr (assoc "startpunkt" basis-lesung)))
(setq hz (cdr (assoc "hz" basis-lesung)))
(setq z-start (cdr (assoc "z-start" basis-lesung)))
(setq eingabe (vfs-dialog-eingabe-basis prefill-basis))
(if (null eingabe)
(princ (ssg-text "vfc-vorgang-abgebrochen"))
(progn
(vf-basis-eingabe-entpacken eingabe)
(setq startpunkt (list (car startpunkt) (cadr startpunkt) z-start))
(setq best-winkel-attr (atoi (cond ((cdr (assoc "VF_WINKEL" attribs))) ("0"))))
(setq L_GF_m-teile (ssg-cfg-split-comma (cond ((cdr (assoc "L_GF_m" attribs))) ("0.0,0.0"))))
(setq prefill-winkel
(list (cons "winkel" best-winkel-attr)
(cons "verteilung-idx" "3")
(cons "lgf1" (* 1000.0 (atof (car L_GF_m-teile))))))
(setq *ssg-ils-dim* dim)
(setq *vf-motorseite* motorseite)
(entdel ent)
(setq vf-nummer (vf-next-number))
(vfe-etage-dialog-berechnen-einfuegen vf-nummer deltaL deltaH richtung
startpunkt hz seite prefill-winkel geruest-einzelmodul geruest-typ)
(setq *ssg-ils-dim* nil)
(setq *vf-motorseite* nil)
(princ (ssg-textf "vfs-edit-neu-aufgebaut" (list bname)))
)
)
)
;; ============================================================
;; REGISTRIERUNG
;; ============================================================
(vf-typ-registrieren
"etage"
'berechne-etage
'etage-foerderanlage-einfuegen
(ssg-text "vfc-typ-etage-beschreibung"))
(princ)