diff --git a/Lisp/Gefaellestrecke.lsp b/Lisp/Gefaellestrecke.lsp index eb65025..b43f739 100644 --- a/Lisp/Gefaellestrecke.lsp +++ b/Lisp/Gefaellestrecke.lsp @@ -146,12 +146,19 @@ (setq ks-results '()) (setq sub-entities (vlax-invoke block-obj 'Explode)) (foreach sub-obj sub-entities - (if (and (not (vlax-erased-p sub-obj)) - (= (vla-get-ObjectName sub-obj) "AcDbBlockReference") - (or (= (vla-get-Name sub-obj) "KS_EIN") - (= (vla-get-Name sub-obj) "KS_AUS"))) + ;; KS-Blocknamen normalisieren: aeltere Bloecke nutzen KS_EIN/KS_AUS, + ;; Vario_Kurve-Bloecke KSYS_EIN/KSYS_AUS (inline, damit diese Fallback- + ;; Kopie unabhaengig von vf_core.lsp funktioniert). + (setq ks-ref + (if (and (not (vlax-erased-p sub-obj)) + (= (vla-get-ObjectName sub-obj) "AcDbBlockReference")) + (cond + ((or (= (vla-get-Name sub-obj) "KS_EIN") (= (vla-get-Name sub-obj) "KSYS_EIN")) "KS_EIN") + ((or (= (vla-get-Name sub-obj) "KS_AUS") (= (vla-get-Name sub-obj) "KSYS_AUS")) "KS_AUS") + (t nil)) + nil)) + (if ks-ref (progn - (setq ks-ref (vla-get-Name sub-obj)) (setq inner-entities (vlax-invoke sub-obj 'Explode)) (setq origin nil x-end nil y-end nil z-end nil) (foreach inner-obj inner-entities @@ -1334,7 +1341,8 @@ bL90 bL60 bL30 bR90 bR60 bR30 startpunkt last-ent nach-aus-pt entry-hz - (caddr (nth first-idx segmente))) + (caddr (nth first-idx segmente)) + es-seite) (princ "\n\n=========================================") (princ "\n>>> Fertig! <<<") @@ -1348,49 +1356,25 @@ ;; TEIL 4.5: GF-BLOCK-SYSTEM (Nummerierung, Label, Attribute) ;; ============================================================ -;; Attribut-Definitionen fuer GF_N-Bloecke -(setq *gf-attrib-defs* - '(("NUMMER" "0") - ("TYP" "standard") - ("SEITE" "rechts") - ("MONTAGEHOEHE_m" "0.000") - ("HOEHE_VON" "0") - ("HOEHE_BIS" "0") - ("DELTA_H" "0") - ("DELTA_L" "0") - ("L_GF_m" "0.000") - ("WINKEL" "0.000") - ("ANZAHL_SEPARATOR" "0") - ("ANZAHL_SCANNER" "0") - ("GF_Bogen_L_90" "0") - ("GF_Bogen_L_60" "0") - ("GF_Bogen_L_30" "0") - ("GF_Bogen_R_90" "0") - ("GF_Bogen_R_60" "0") - ("GF_Bogen_R_30" "0") - ("ID" "") - ) -) +;; Attribut-Definitionen fuer GF_N-Bloecke kommen aus dem gemeinsamen +;; Strecken-Schema in ssg_core.lsp (ssg-strecke-attrib-defs). TYP wird zur +;; Laufzeit bestimmt: ohne Bogen -> "Gefaellestrecke" (reduziert), mit Bogen +;; -> "Streckengruppe" (voll). ;; Naechste freie GF-Nummer ;; Stufe 1: NUMMER-Attribut aller GF_*-Inserts lesen ;; Stufe 2: Block-Tabelle pruefen – GF_N darf noch nicht als Definition existieren -(defun gf-next-number ( / ss i nr maxnr attribs) +(defun gf-next-number ( / ss i nr maxnr bname) (setq maxnr 0) (setq ss (ssget "X" '((0 . "INSERT") (2 . "GF_*")))) (if ss (progn (setq i 0) (while (< i (sslength ss)) - (setq attribs (ssg-attrib-read (ssname ss i))) - (foreach pair attribs - (if (= (car pair) "NUMMER") - (progn - (setq nr (atoi (cdr pair))) - (if (> nr maxnr) (setq maxnr nr)) - ) - ) - ) + ;; Nummer aus dem Blocknamen GF_ ableiten ("GF_" = 3 Zeichen) + (setq bname (cdr (assoc 2 (entget (ssname ss i))))) + (setq nr (atoi (substr bname 4))) + (if (> nr maxnr) (setq maxnr nr)) (setq i (1+ i)) ) ) @@ -1456,13 +1440,31 @@ L-gf-str winkel n-separator n-scanner bogen-L90 bogen-L60 bogen-L30 bogen-R90 bogen-R60 bogen-R30 - startpunkt last-ent nach-aus-pt entry-hz L-first-seg / - gf-bname gf-ss ent gf-insert) + startpunkt last-ent nach-aus-pt entry-hz L-first-seg + es-seite / + gf-bname gf-ss ent gf-insert typ-str anzahl-gf + gf-winkel-str i) ;; Beschriftung: Mitte der ersten Geraden, 400mm senkrecht versetzt (gf-make-label gf-nummer hoehe-von hoehe-bis deltaH deltaL L-gf-str winkel n-separator nach-aus-pt entry-hz L-first-seg) - ;; ATTDEFs (unsichtbar, am Startpunkt) - (foreach def *gf-attrib-defs* + ;; TYP: mit Bogen -> "Streckengruppe"; ohne Bogen (einsegmentig) -> + ;; "Gefaellestrecke" (reduzierter Attributsatz, siehe ssg-strecke-attrib-defs). + (setq typ-str + (if (> (+ bogen-L90 bogen-L60 bogen-L30 bogen-R90 bogen-R60 bogen-R30) 0) + "Streckengruppe" "Gefaellestrecke")) + (setq gf-bname (strcat "GF_" (itoa gf-nummer))) + ;; Anzahl GF-Segmente = Anzahl kommagetrennter Werte in L-gf-str; + ;; GF_WINKEL parallel dazu (alle Segmente teilen die Gefaelle-Neigung). + (setq anzahl-gf 1 i 1) + (while (<= i (strlen L-gf-str)) + (if (= (substr L-gf-str i 1) ",") (setq anzahl-gf (1+ anzahl-gf))) + (setq i (1+ i))) + (setq gf-winkel-str (rtos (float winkel) 2 1) i 1) + (while (< i anzahl-gf) + (setq gf-winkel-str (strcat gf-winkel-str "," (rtos (float winkel) 2 1))) + (setq i (1+ i))) + ;; ATTDEFs (unsichtbar, am Startpunkt) nach gemeinsamem Strecken-Schema + (foreach def (ssg-strecke-attrib-defs typ-str) (entmake (list '(0 . "ATTDEF") (cons 10 startpunkt) @@ -1482,42 +1484,43 @@ (ssadd ent gf-ss) (setq ent (entnext ent)) ) - ;; Block definieren und einfuegen - (setq gf-bname (strcat "GF_" (itoa gf-nummer))) - ;; ATTREQ/ATTDIA=0: verhindert interaktive Attribut-Abfrage bei INSERT - ;; (sonst bleiben die per ssg-attrib-set-on gesetzten Werte wirkungslos, - ;; da INSERT auf der Kommandozeile auf Attribut-Eingaben wartet), siehe - ;; identisches Muster in vf-block-erstellen (vf_core.lsp). + ;; Block definieren und einfuegen (ATTREQ/ATTDIA=0: keine interaktive Abfrage) (setvar "ATTREQ" 0) (setvar "ATTDIA" 0) (command "_.-BLOCK" gf-bname startpunkt gf-ss "") (command "_.INSERT" gf-bname startpunkt 1.0 1.0 0.0) (setvar "ATTREQ" 1) - ;; Attribute auf das eingefuegte INSERT-Objekt setzen + ;; Attribute auf das eingefuegte INSERT-Objekt setzen (volle Werteliste; + ;; nicht vorhandene Tags ignoriert ssg-attrib-set-on -> reduzierter Satz ok). (setq gf-insert (entlast)) (ssg-attrib-set-on gf-insert (list - (cons "NUMMER" (itoa gf-nummer)) - (cons "TYP" typ) - (cons "SEITE" seite) + (cons "Bezeichnung" gf-bname) (cons "MONTAGEHOEHE_m" "0.000") (cons "HOEHE_VON" (itoa (fix hoehe-von))) (cons "HOEHE_BIS" (itoa (fix hoehe-bis))) (cons "DELTA_H" (itoa (fix (abs deltaH)))) (cons "DELTA_L" (itoa (fix deltaL))) + (cons "TYP" typ-str) + (cons "SEITE_AS" seite) + (cons "SEITE_ES" es-seite) + (cons "ANZAHL_GF" (itoa anzahl-gf)) (cons "L_GF_m" L-gf-str) - (cons "WINKEL" (rtos (float winkel) 2 3)) - (cons "ANZAHL_SEPARATOR" (itoa n-separator)) - (cons "ANZAHL_SCANNER" (itoa n-scanner)) + (cons "GF_WINKEL" gf-winkel-str) (cons "GF_Bogen_L_90" (itoa bogen-L90)) (cons "GF_Bogen_L_60" (itoa bogen-L60)) (cons "GF_Bogen_L_30" (itoa bogen-L30)) (cons "GF_Bogen_R_90" (itoa bogen-R90)) (cons "GF_Bogen_R_60" (itoa bogen-R60)) (cons "GF_Bogen_R_30" (itoa bogen-R30)) + (cons "ANZAHL_SEPARATOR" (itoa n-separator)) + (cons "ANZAHL_SCANNER" (itoa n-scanner)) ) ) - (princ (strcat "\n>>> GF-Block erstellt: " gf-bname)) + ;; Aufsteigende, eindeutige ID vergeben (wie beim Kreisel), falls verfuegbar. + (if (car (atoms-family 1 '("SSG-ID-GENERATE"))) + (ssg-id-generate gf-insert)) + (princ (strcat "\n>>> GF-Block erstellt: " gf-bname " (" typ-str ")")) gf-insert ) @@ -1612,7 +1615,8 @@ winkel 1 0 0 0 0 0 0 0 - startpunkt last-ent nach-aus-pt (float hz) (float L_stau))) + startpunkt last-ent nach-aus-pt (float hz) (float L_stau) + es-seite)) (princ "\n\n=========================================") (princ "\n>>> Gefaellestrecke erfolgreich eingefuegt! <<<") diff --git a/Lisp/ssg_core.lsp b/Lisp/ssg_core.lsp index 85072ca..0191762 100644 --- a/Lisp/ssg_core.lsp +++ b/Lisp/ssg_core.lsp @@ -384,6 +384,64 @@ ) ) +;; ============================================================ +;; GEMEINSAMES ATTRIBUT-SCHEMA FUER STRECKEN-BLOECKE +;; (Gefaellestrecke + VarioFoerderer, feste Reihenfolge) +;; ============================================================ +;; Vorderteil (immer vorhanden). Bezeichnung = Blockname (GF_n/VF_n), +;; Artinr. fest 6220 (alle GF/VF). TYP-Wert wird zur Laufzeit gesetzt. +(setq *strecke-attr-front* + '(("Bezeichnung" "") + ("ID" "") + ("Artinr." "6220") + ("MONTAGEHOEHE_m" "0.000") + ("HOEHE_VON" "0") + ("HOEHE_BIS" "0") + ("DELTA_H" "0") + ("DELTA_L" "0") + ("TYP" "Streckengruppe") + ("SEITE_AS" "rechts") + ("SEITE_ES" "rechts") + ("ANZAHL_GF" "0") + ("L_GF_m" "0.000") + ("GF_WINKEL" "0.0"))) + +;; Mittelteil - nur fuer TYP "Streckengruppe" (mehrsegmentig/mit Bogen bzw. VF). +;; Eine einsegmentige Gefaellestrecke ohne Bogen ("Gefaellestrecke") bekommt +;; diese Attribute NICHT. +(setq *strecke-attr-gruppe* + '(("GF_Bogen_L_90" "0") + ("GF_Bogen_L_60" "0") + ("GF_Bogen_L_30" "0") + ("GF_Bogen_R_90" "0") + ("GF_Bogen_R_60" "0") + ("GF_Bogen_R_30" "0") + ("ANZAHL_VF" "0") + ("MOTORSEITE" "") + ("L_VF_m" "0.000") + ("ANTRIEBFAHRTRICHTUNG" "Auf") + ("VF_WINKEL" "0") + ("VF_Bogen_A_90" "0") + ("VF_Bogen_A_60" "0") + ("VF_Bogen_A_30" "0") + ("VF_Bogen_I_90" "0") + ("VF_Bogen_I_60" "0") + ("VF_Bogen_I_30" "0"))) + +;; Hinterteil (immer vorhanden). ANZAHL_SEPARATOR-Wert wird zur Laufzeit gesetzt +;; (einsegmentige Gefaellestrecke: 1; Streckengruppe: gezaehlt). +(setq *strecke-attr-hinten* + '(("ANZAHL_SEPARATOR" "1") + ("ANZAHL_SCANNER" "0"))) + +;; Geordnete ATTDEF-Definitionsliste fuer einen Strecken-Block-Typ. +;; typ "Gefaellestrecke" -> Vorderteil + Hinterteil (reduziert) +;; sonst ("Streckengruppe") -> Vorderteil + Mittelteil + Hinterteil (voll) +(defun ssg-strecke-attrib-defs (typ) + (if (= typ "Gefaellestrecke") + (append *strecke-attr-front* *strecke-attr-hinten*) + (append *strecke-attr-front* *strecke-attr-gruppe* *strecke-attr-hinten*))) + ;; Assoziationsliste in ATTDEF-Definitionsliste konvertieren. ;; alist = (("TAG" . "Wert") ...) -> ((TAG Wert) ...) ;; Passend fuer ssg-attrib-make-defs. diff --git a/Lisp/vf_core.lsp b/Lisp/vf_core.lsp index a7390fa..0e84d36 100644 --- a/Lisp/vf_core.lsp +++ b/Lisp/vf_core.lsp @@ -2,7 +2,7 @@ ;; VF_CORE - VarioFoerderer Kern-Modul ;; Version: 26.0 ;; Architektur: Plugin/Dispatcher -;; Laedt automatisch: vf_standard.lsp, vf_etage.lsp +;; Laedt automatisch: vf_standard.lsp, vf_etage.lsp, vf_linienzug.lsp ;; Befehle: VarioFoerderer (Alias: FOERDERANLAGE, ETAGEVARIOFOERDERER) ;; ============================================================ @@ -87,35 +87,9 @@ (setq *ks-cache* nil) (setq grad-zeichen (chr 176)) -;; 26 Attribut-Definitionen fuer VF_*-Block (alle Typen) -(setq *vf-attrib-defs* - '(("NUMMER" "0") - ("MONTAGEHOEHE_m" "0.000") - ("HOEHE_VON" "0") - ("HOEHE_BIS" "0") - ("DELTA_H" "0") - ("DELTA_L" "0") - ("L_VF_m" "0.000") - ("L_GF_m" "0.000") - ("ANTRIEBFAHRTRICHTUNG" "Auf") - ("WINKEL" "0") - ("ANZAHL_SEPARATOR" "2") - ("ANZAHL_SCANNER" "0") - ("TYP" "standard") - ("SEITE" "rechts") - ("GF_Bogen_L_90" "0") - ("GF_Bogen_L_60" "0") - ("GF_Bogen_L_30" "0") - ("GF_Bogen_R_90" "0") - ("GF_Bogen_R_60" "0") - ("GF_Bogen_R_30" "0") - ("VF_Bogen_A_90" "0") - ("VF_Bogen_A_60" "0") - ("VF_Bogen_A_30" "0") - ("VF_Bogen_I_90" "0") - ("VF_Bogen_I_60" "0") - ("VF_Bogen_I_30" "0") - ("ID" ""))) +;; Attribut-Definitionen fuer VF_*-Bloecke kommen aus dem gemeinsamen +;; Strecken-Schema in ssg_core.lsp (ssg-strecke-attrib-defs). VarioFoerderer +;; ist immer TYP "Streckengruppe" -> voller Attributsatz. ;; ============================================================ ;; TEIL 4: HILFSFUNKTIONEN @@ -131,6 +105,15 @@ ((and (> len 2.5) (< len 3.5)) "Z") (t nil))) +;; Normalisiert die verschiedenen KS-Blocknamen auf "KS_EIN"/"KS_AUS". +;; Die aelteren Bloecke (AS/ES/Gefaellebogen) verwenden KS_EIN/KS_AUS, die +;; Vario_Kurve-Bloecke KSYS_EIN/KSYS_AUS. Rueckgabe nil, wenn kein KS-Block. +(defun ks-normalize-name (nm) + (cond + ((or (= nm "KS_EIN") (= nm "KSYS_EIN")) "KS_EIN") + ((or (= nm "KS_AUS") (= nm "KSYS_AUS")) "KS_AUS") + (t nil))) + (defun fmt (x) (if (and (numberp x) (not (equal x nil))) (rtos x 2 2) "---")) @@ -232,6 +215,35 @@ (setq zu (vec3-cross xu-unit yu)) (list P xu-unit yu zu)) +;; --- Bruecke Frame <-> (hz, winkel) --- +;; Fuer vf-linienzug (gemischte GF/VF-Ketten, vf_linienzug.lsp): Gefaellestrecke +;; traegt den Kettenzustand als Frame (Position + Richtungsvektor xu) weiter, +;; waehrend die aelteren VF-Einfuegefunktionen (insert-block-by-ks, +;; insert-rotated-block-with-ks, insert-inclined-scaled-block) Position+hz+winkel +;; getrennt erwarten. Beide nutzen dieselbe Rz(hz)*Ry(winkel)-Rotation +;; (winkel positiv = abwaerts, xu = (cos(hz)cos(v), sin(hz)cos(v), -sin(v))), +;; daher sind Frame und (hz winkel) verlustfrei ineinander umrechenbar. +(defun frame->hz-winkel (frame / xu horiz-len) + (setq xu (cadr frame)) + (setq horiz-len (sqrt (+ (* (car xu)(car xu)) (* (cadr xu)(cadr xu))))) + (list + (* (atan (cadr xu) (car xu)) (/ 180.0 pi)) + (* (atan (- (caddr xu)) horiz-len) (/ 180.0 pi)) + ) +) + +(defun hz-winkel->xu (hz winkel / rad-h rad-v) + (setq rad-h (* (float hz) (/ pi 180.0))) + (setq rad-v (* (float winkel) (/ pi 180.0))) + (list (* (cos rad-h)(cos rad-v)) + (* (sin rad-h)(cos rad-v)) + (- (sin rad-v))) +) + +(defun punkt-hz-winkel->frame (punkt hz winkel) + (make-frame-from-dir punkt (hz-winkel->xu hz winkel)) +) + ;; ============================================================ ;; TEIL 5: KS_EIN/KS_AUS EXTRAKTION ;; ============================================================ @@ -260,12 +272,13 @@ (setq ks-results '()) (setq sub-entities (vlax-invoke block-obj 'Explode)) (foreach sub-obj sub-entities - (if (and (not (vlax-erased-p sub-obj)) - (= (vla-get-ObjectName sub-obj) "AcDbBlockReference") - (or (= (vla-get-Name sub-obj) "KS_EIN") - (= (vla-get-Name sub-obj) "KS_AUS"))) + (setq ks-ref + (if (and (not (vlax-erased-p sub-obj)) + (= (vla-get-ObjectName sub-obj) "AcDbBlockReference")) + (ks-normalize-name (vla-get-Name sub-obj)) + nil)) + (if ks-ref (progn - (setq ks-ref (vla-get-Name sub-obj)) (setq inner-entities (vlax-invoke sub-obj 'Explode)) (setq origin nil x-end nil y-end nil z-end nil) (foreach inner-obj inner-entities @@ -400,22 +413,18 @@ ;; ============================================================ ;; TEIL 7: VF-NUMMERIERUNG ;; ============================================================ -(defun vf-next-number ( / ss i nr maxnr attribs) +;; Naechste freie VF-Nummer aus den Blocknamen VF_ ableiten (der Blockname +;; ist die Bezeichnung/Zaehlung; ein separates NUMMER-Attribut gibt es nicht mehr). +(defun vf-next-number ( / ss i nr maxnr bname) (setq maxnr 0) (setq ss (ssget "X" '((0 . "INSERT") (2 . "VF_*")))) (if ss (progn (setq i 0) (while (< i (sslength ss)) - (setq attribs (ssg-attrib-read (ssname ss i))) - (foreach pair attribs - (if (= (car pair) "NUMMER") - (progn - (setq nr (atoi (cdr pair))) - (if (> nr maxnr) (setq maxnr nr)) - ) - ) - ) + (setq bname (cdr (assoc 2 (entget (ssname ss i))))) + (setq nr (atoi (substr bname 4))) ; "VF_" = 3 Zeichen -> ab Position 4 + (if (> nr maxnr) (setq maxnr nr)) (setq i (1+ i)) ) ) @@ -993,9 +1002,10 @@ (vf-make-label vf-nummer hoehe-von hoehe-bis deltaH deltaL L_VF L_GF1 L_GF2 richtung best-winkel 2 n-scanner startpunkt hz) - ;; Unsichtbare ATTDEFs am Startpunkt erzeugen + ;; Unsichtbare ATTDEFs am Startpunkt erzeugen (gemeinsames Strecken-Schema; + ;; VarioFoerderer ist immer TYP "Streckengruppe" -> voller Attributsatz). (setq attdef-ypos (cadr startpunkt)) - (foreach def *vf-attrib-defs* + (foreach def (ssg-strecke-attrib-defs "Streckengruppe") (entmake (list '(0 . "ATTDEF") '(8 . "0") @@ -1037,20 +1047,28 @@ (setq L_GF_m-str (strcat (rtos (/ L_GF1 1000.0) 2 3) "," (rtos (/ L_GF2 1000.0) 2 3))) (ssg-attrib-set-on vf-insert (list - (cons "NUMMER" (itoa vf-nummer)) + (cons "Bezeichnung" vf-bname) + ;; Artinr. bleibt auf Schema-Vorgabe "6220" (cons "MONTAGEHOEHE_m" (rtos montagehoehe-m 2 3)) (cons "HOEHE_VON" (itoa (fix hoehe-von))) (cons "HOEHE_BIS" (itoa (fix hoehe-bis))) (cons "DELTA_H" (itoa (fix deltaH))) (cons "DELTA_L" (itoa (fix deltaL))) - (cons "L_VF_m" (rtos (/ L_VF 1000.0) 2 3)) + (cons "TYP" "Streckengruppe") + (cons "SEITE_AS" seite) + (cons "SEITE_ES" seite) + (cons "ANZAHL_GF" "2") ; GF1 + GF2 (cons "L_GF_m" L_GF_m-str) + ;; GF_WINKEL: Neigung je GF-Segment (GF1,GF2 - beide feste Grundneigung) + (cons "GF_WINKEL" (strcat (rtos (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) 2 1) + "," (rtos (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) 2 1))) + (cons "ANZAHL_VF" "1") ; ein VF-Koerper + (cons "MOTORSEITE" "rechts") + (cons "L_VF_m" (rtos (/ L_VF 1000.0) 2 3)) (cons "ANTRIEBFAHRTRICHTUNG" richtung) - (cons "WINKEL" (itoa best-winkel)) + (cons "VF_WINKEL" (itoa best-winkel)) (cons "ANZAHL_SEPARATOR" "2") (cons "ANZAHL_SCANNER" (itoa n-scanner)) - (cons "TYP" anlage-typ) - (cons "SEITE" seite) ) ) ;; Typ-spezifische Bogen-Zaehler (Etage: 2x Gefaellebogen je Seite) @@ -1060,6 +1078,9 @@ (ssg-attrib-set-on vf-insert (list (cons "GF_Bogen_L_60" "2"))) ) ) + ;; Aufsteigende, eindeutige ID vergeben (wie beim Kreisel), falls verfuegbar. + (if (car (atoms-family 1 '("SSG-ID-GENERATE"))) + (ssg-id-generate vf-insert)) (princ (strcat "\n>>> Block '" vf-bname "' erstellt und eingefuegt.")) ) (princ "\n>>> FEHLER: Keine Entities fuer Block gefunden!") @@ -1087,6 +1108,7 @@ (progn (load (strcat *vf-lisp-pfad* "/vf_standard.lsp")) (load (strcat *vf-lisp-pfad* "/vf_etage.lsp")) + (load (strcat *vf-lisp-pfad* "/vf_linienzug.lsp")) (setq *ssg-vf-submodule-loaded* t) ) (princ "\n[vf_core] WARNUNG: DXFM_LISP nicht gesetzt - Untermodule konnten nicht geladen werden!") @@ -1144,6 +1166,17 @@ (setq einfuege-fn (nth 2 typ-eintr)) (princ (strcat "\n>>> Typ: " anlage-typ)) + ;; Sonderfall "linienzug": eigener Befehlsablauf (interaktive Mehrsegment- + ;; Kette mit gemischten GF/VF-Segmenten), passt nicht in das berechne-fn/ + ;; einfuege-fn-Schema fuer ein einzelnes durchgehendes Segment - siehe + ;; vf_linienzug.lsp. Dispatcht hier direkt und beendet den Standard-Ablauf. + (if (= anlage-typ "linienzug") + (progn + (vf-linienzug-modus) + (exit) + ) + ) + ;; 2. VF-Nummer automatisch ermitteln (setq vf-nummer (vf-next-number)) (princ (strcat "\n>>> Naechste VF-Nummer: VF" (itoa vf-nummer))) @@ -1288,9 +1321,8 @@ (princ (strcat "\n>>> L_GF1=" (rtos L_GF1 2 2) " mm L_GF2=" (rtos L_GF2 2 2) " mm")) - ;; 10. Anzahl Scanner - (setq n-scanner (getint "\nAnzahl Scanner [0]: ")) - (if (null n-scanner) (setq n-scanner 0)) + ;; Scanner-Anzahl wird nicht mehr abgefragt (Attribut ANZAHL_SCANNER bleibt 0). + (setq n-scanner 0) ;; 11. Bestaetigung (princ "\n\nMoechten Sie die Foerderanlage einfuegen?") diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp new file mode 100644 index 0000000..d94da62 --- /dev/null +++ b/Lisp/vf_linienzug.lsp @@ -0,0 +1,902 @@ +;; ============================================================ +;; VF_LINIENZUG - Gemischte Gefaellestrecke/VarioFoerderer-Kette +;; Typ "linienzug" fuer VarioFoerderer (3. Typ neben "standard"/"etage") +;; ============================================================ +;; Kette: immer genau EIN AS_Element am Anfang, genau EIN ES_Element am Ende, +;; dazwischen beliebig viele Segmente. Jedes gerade Segment wird automatisch +;; klassifiziert: +;; - Endpunkt hoeher als Startpunkt -> immer VF (Gefaelle kann nicht steigen, +;; reine Schwerkraftstrecke) +;; - Endpunkt tiefer, Neigung >= 3 Grad -> reine Gefaellestrecke (GF), da 3 +;; Grad im ganzen Projekt die kleinste +;; Neigung ist (Vario_Bogen_auf/ab_3) +;; - Endpunkt tiefer, Neigung < 3 Grad -> VF (zu flach fuer reine GF): +;; erst Horizontale Mitte pruefen +;; (berechne-horizontale-mitte), sonst +;; diskreten Winkel 3-51 Grad suchen +;; (berechne-alle-winkel) +;; Nach einem GF-Segment: naechstes Element nur GF-Bogen oder neue Linie. +;; Nach einem VF-Segment: naechstes Element nur Vario-Kurve oder neue Linie. +;; +;; Architektur-Entscheidung (mit Nutzer abgestimmt): eigener Befehlsablauf, +;; NICHT ueber die berechne-fn/einfuege-fn-Registry (die ist fuer ein einzelnes +;; durchgehendes Segment gedacht, nicht fuer eine interaktive Mehrsegment-Kette). +;; c:VarioFoerderer erkennt den Typ "linienzug" und dispatcht direkt hierher. +;; +;; BEKANNTE EINSCHRAENKUNGEN dieser ersten Version (bitte in BricsCAD pruefen): +;; - Vario_Kurve_*-Bloecke (data/ils/3D/) wurden bislang nirgends im Projekt +;; verwendet. Ob sie KS_EIN/KS_AUS enthalten (Voraussetzung fuer +;; insert-block-ks-to-ks) ist ungeklaert und muss beim ersten Testlauf +;; verifiziert werden. +;; - Am Uebergang GF-Segment -> VF-Segment (ueber "neue Linie", die +;; automatisch als VF eingestuft wird) kann ein sichtbarer Knick entstehen: +;; vfs-mitte-teil beginnt sein erstes Element (GF1) immer fest bei 3 Grad, +;; unabhaengig vom Neigungswinkel des vorangehenden GF-Segments. +;; - Die Fusspunkte von AS-/ES-Element (aus-dx/ein-dx) werden NICHT von der +;; angegebenen Segmentlaenge abgezogen (anders als in gf-linienzug-modus). +;; Das erste bzw. letzte Segment kann dadurch um bis zu aus-dx/ein-dx mm +;; (typischerweise wenige hundert mm) laenger werden als eingegeben. +;; ============================================================ + +;; Attribut-Definitionen fuer den Linienzug-Block kommen aus dem gemeinsamen +;; Strecken-Schema in ssg_core.lsp (ssg-strecke-attrib-defs). Der TYP wird zur +;; Laufzeit bestimmt: einsegmentige GF ohne Bogen -> "Gefaellestrecke" +;; (reduziert), sonst -> "Streckengruppe" (voll, segmentweise Werte). + +;; Toleranzband (Grad) um die feste 3-Grad-Neigung: liegt der natuerliche +;; Winkel eines fallenden Segments innerhalb 3+/-Toleranz, wird es als reine +;; 3-Grad-Gefaellestrecke gebaut. Steiler -> VF-ab, flacher -> VF (flach). +;; Grund: eine reine Gefaellestrecke ist nie steiler als 3 Grad. +;; Bei Bedarf empirisch anpassen. +(if (null *vfl-gf-winkel-toleranz*) (setq *vfl-gf-winkel-toleranz* 0.5)) + +;; Mindestlaenge (mm) fuer die 3-Grad-Gefaellestrecke GF1 am Einlauf (zwischen +;; AS und Umlenkstation). Im Linienzug sitzt die gesamte Staustrecke am Einlauf +;; (GF1 = komplettes L_GF), GF2 am Ausgang entfaellt. Faellt das berechnete +;; L_GF darunter, wird GF1 auf diesen Wert angehoben - damit der 3-Grad- +;; Anschluss immer physisch vorhanden ist. +(if (null *vfl-gf-min-laenge*) (setq *vfl-gf-min-laenge* 400.0)) + +;; Horizontales Budget der festen Elemente im Linienzug-VF: +;; Umlenkstation (500) + Motorstation (500) + EIN Separator am Einlauf (300) +;; = 1300 mm. Der Ausgangs-Separator entfaellt (anders als Standard-VF=1600). +(if (null *vfl-feste-horizontal*) (setq *vfl-feste-horizontal* 1300.0)) + +;; ============================================================ +;; TEIL 0: ATTRIBUT-AKKUMULATOREN (werden waehrend des Baus gefuellt) +;; ============================================================ +;; Segment-Listen werden in Bau-Reihenfolge angehaengt und spaeter +;; kommagetrennt in die Attribute geschrieben. Bogen-/Kurven-Zaehler als Alist. +(defun vfl-acc-reset () + (setq *vfl-acc-lvf* '() ; L_VF je VF-Sub-Segment (m, String) + *vfl-acc-lgf* '() ; L_GF je GF-Segment (m, String) - inkl. GF1/GF2 + *vfl-acc-gfwinkel* '() ; Neigungswinkel je GF-Segment (String, parallel zu lgf) + *vfl-acc-richtung* '() ; "Auf"/"Ab"/"horizontal" je VF-Sub-Segment + *vfl-acc-winkel* '() ; Winkel je VF-Sub-Segment (String, 0=horizontal) + *vfl-acc-motorseite* '() ; Seite ("rechts"/"links") je Motorstation + *vfl-acc-gfbogen* '() ; Alist ("L_90".n ...) GF-Boegen + *vfl-acc-variokurve* '() ; Alist ("A_90".n ...) Vario-Kurven + *vfl-acc-separator* 0)) ; Anzahl eingefuegter Separatoren (300 mm) + +;; Alist-Zaehler erhoehen / lesen +(defun vfl-inc-count (al key / e) + (setq e (assoc key al)) + (if e (subst (cons key (1+ (cdr e))) e al) (cons (cons key 1) al))) +(defun vfl-get-count (al key / e) + (if (setq e (assoc key al)) (cdr e) 0)) + +;; Ein VF-Sub-Segment (Koerper) erfassen. Winkel 0 => horizontal. +(defun vfl-acc-vf-seg (richtung winkel L_VF) + (setq *vfl-acc-lvf* (append *vfl-acc-lvf* (list (rtos (/ L_VF 1000.0) 2 3)))) + (setq *vfl-acc-winkel* (append *vfl-acc-winkel* (list (itoa (fix winkel))))) + (setq *vfl-acc-richtung* (append *vfl-acc-richtung* + (list (if (= (fix winkel) 0) "horizontal" richtung))))) + +;; Ein GF-Segment erfassen (Laenge = Schraeglaenge in m, winkel = Neigung). +;; Gilt fuer reine GF-Chain-Segmente UND die VF-internen GF1/GF2-Anschluesse. +(defun vfl-acc-gf-seg (L_GF winkel) + (setq *vfl-acc-lgf* (append *vfl-acc-lgf* (list (rtos (/ L_GF 1000.0) 2 3)))) + (setq *vfl-acc-gfwinkel* (append *vfl-acc-gfwinkel* (list (rtos (float winkel) 2 1))))) + +;; Liste kommagetrennt verketten ("" bei leer). +(defun vfl-join-komma (lst / s first) + (setq s "" first t) + (foreach x lst + (if first (progn (setq s x) (setq first nil)) (setq s (strcat s "," x)))) + s) + +;; ============================================================ +;; TEIL 1: SEGMENT-ENTSCHEIDUNG (GF oder VF) +;; ============================================================ + +;; Aus der Ergebnisliste von berechne-alle-winkel die GUELTIGEN Winkel filtern +;; und - bei mehreren - den Nutzer waehlen lassen. Rueckgabe: (winkel L_GF L_VF) +;; oder nil, wenn kein gueltiger Winkel existiert. +(defun vfl-waehle-winkel (ergebnis-liste / gueltige idx antwort e) + (setq gueltige '()) + (foreach e ergebnis-liste + (if (and (cadddr e) (numberp (cadr e)) (numberp (caddr e)) + (> (cadr e) 0) (> (caddr e) 0)) + (setq gueltige (append gueltige (list e))))) + (cond + ((null gueltige) nil) + ((= (length gueltige) 1) + (setq e (car gueltige)) (list (nth 0 e) (nth 1 e) (nth 2 e))) + (t + (princ "\n\nMehrere gueltige Winkel - bitte gewuenschten Winkel waehlen:") + (setq idx 1) + (foreach e gueltige + (princ (strcat "\n " (itoa idx) " - " (itoa (car e)) " Grad" + " (L_GF=" (rtos (cadr e) 2 1) " mm L_VF=" (rtos (caddr e) 2 1) " mm)")) + (setq idx (1+ idx))) + (setq antwort (getint (strcat "\nIhre Wahl (1-" (itoa (length gueltige)) ") [1]: "))) + (if (or (null antwort) (< antwort 1) (> antwort (length gueltige))) (setq antwort 1)) + (setq e (nth (1- antwort) gueltige)) + (list (nth 0 e) (nth 1 e) (nth 2 e))))) + +;; berechne-alle-winkel ausfuehren (mit Linienzug-FESTE_HORIZONTAL = 1300) und +;; den Winkel waehlen lassen. Rueckgabe: (winkel L_GF L_VF) oder nil. +(defun vfl-vf-winkel (deltaL deltaH richtung) + (vfl-waehle-winkel + (nth 3 (berechne-alle-winkel deltaL deltaH richtung *vfl-feste-horizontal*)))) + +;; Rueckgabe: (typ winkel L_GF L_VF) +;; typ="GF": winkel = kontinuierlicher Neigungswinkel, L_GF/L_VF ungenutzt (nil) +;; typ="VF": winkel = best-winkel (0 = Horizontale Mitte), L_GF/L_VF wie +;; berechne-alle-winkel bzw. berechne-horizontale-mitte +;; typ=nil : weder GF noch VF fuer dieses deltaL/deltaH geometrisch moeglich +(defun vfl-segment-entscheidung (deltaL deltaH richtung / + winkel-natuerlich wahl horizontal-info) + (if (not *lib-initialized*) (init-bibliothek)) + (cond + ;; Zu kurz fuer eine VF-Einheit: Umlenkstation (500 mm) + Motorstation + ;; (500 mm) belegen zusammen 1000 mm deltaL - darunter ist kein VF baubar. + ;; Also nur GF moeglich, und GF ist mindestens 3 Grad geneigt -> fest 3 Grad. + ;; Steigend geht mit einer Gefaellestrecke nicht -> nicht baubar. + ((< deltaL 1000.0) + (if (= richtung "Auf") + (list nil nil nil nil) + (list "GF" 3.0 nil nil))) + + ;; Segment ohne messbare Hoehenaenderung: weder Gefaelle noch sinnvolles VF + ((< deltaH 1.0) (list nil nil nil nil)) + + ;; Steigend: Gefaelle kann nicht steigen -> nur VF moeglich. + ;; Bei mehreren gueltigen Winkeln waehlt der Nutzer (vfl-vf-winkel). + ((= richtung "Auf") + (setq wahl (vfl-vf-winkel deltaL deltaH "Auf")) + (if wahl + (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) + (list nil nil nil nil))) + + ;; Fallend: natuerlichen Neigungswinkel bestimmen (atan der Schraege). + ;; Eine reine Gefaellestrecke ist nie steiler als 3 Grad, daher: + ;; ~3 Grad (Toleranzband) -> reine GF (fest 3 Grad) + ;; steiler als 3 Grad -> absteigender VarioFoerderer (VF-ab) + ;; flacher als 3 Grad -> VF (Horizontale Mitte oder diskreter Winkel) + (t + (setq winkel-natuerlich (* (atan (/ deltaH deltaL)) (/ 180.0 pi))) + (cond + ;; ~3 Grad -> reine 3-Grad-Gefaellestrecke + ((<= (abs (- winkel-natuerlich 3.0)) *vfl-gf-winkel-toleranz*) + (list "GF" 3.0 nil nil)) + + ;; steiler als 3 Grad -> VF-ab (Nutzer waehlt aus gueltigen Winkeln) + ((> winkel-natuerlich 3.0) + (setq wahl (vfl-vf-winkel deltaL deltaH "Ab")) + (if wahl + (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) + (list nil nil nil nil))) + + ;; flacher als 3 Grad -> VF: erst Horizontale Mitte, dann diskreter Winkel + (t + (setq horizontal-info (berechne-horizontale-mitte deltaL deltaH "Ab")) + (if (and horizontal-info (caddr horizontal-info)) + (list "VF" 0 (car horizontal-info) (cadr horizontal-info)) + (progn + (setq wahl (vfl-vf-winkel deltaL deltaH "Ab")) + (if wahl + (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) + (list nil nil nil nil))))) + ) + ) + ) +) + +;; ============================================================ +;; TEIL 2: SEGMENT-EINFUEGUNG (ohne AS/ES - reine Kettenmitte) +;; ============================================================ + +;; Reine, kontinuierlich skalierte Gefaelleschraege (kein AS/ES, kein Bogen). +;; punkt: Startpunkt (3D), hz: Horizontalrichtung, deltaL: horizontaler +;; Fussabdruck, winkel: Neigungswinkel (aus vfl-segment-entscheidung). +;; Die Schraeglaenge wird aus deltaL/cos(winkel) abgeleitet, damit der +;; horizontale Fussabdruck exakt deltaL entspricht (deltaH = deltaL*tan(winkel) +;; ergibt sich damit konsistent). Rueckgabe: neuer Frame am Segmentende. +(defun vfl-insert-gf-segment (punkt hz deltaL winkel / l-schraeg endpunkt) + (setq l-schraeg (/ deltaL (cos (* (float winkel) (/ pi 180.0))))) + (setq endpunkt + (gf-insert-hz-incl-scaled "Staustrecke_SP_1000_mm" punkt l-schraeg hz winkel)) + (make-frame-from-dir endpunkt (hz-winkel->xu hz winkel)) +) + +;; --- Hilfsfunktionen fuer die VF-Einheit --- + +;; Planare (XY-)Distanz und Richtung zwischen zwei Punkten. +(defun vfl-planar-dist (p1 p2) + (sqrt (+ (expt (- (car p2) (car p1)) 2) (expt (- (cadr p2) (cadr p1)) 2)))) +(defun vfl-planar-hz (p1 p2) + (* (atan (- (cadr p2) (cadr p1)) (- (car p2) (car p1))) (/ 180.0 pi))) + +;; Neue Linie ausmessen: Laenge (deltaL) und Fahrtrichtung (hz) bestimmen. +;; Ist hz-vorgabe gesetzt (Fahrtrichtung durch das vorherige Element - z.B. +;; einen GF-Bogen oder eine Vario-Kurve - bereits festgelegt), wird der +;; gewaehlte Punkt auf diese Richtung PROJIZIERT: die Linie folgt exakt der +;; Fahrtrichtung, der Nutzer gibt praktisch nur die Laenge vor (eine gerade +;; Foerderstrecke kann die Richtung nicht aendern). Nur beim allerersten +;; Segment (hz-vorgabe=nil) definiert der gewaehlte Punkt die Richtung frei. +;; Rueckgabe: (deltaL hz) oder nil bei Abbruch. +(defun vfl-neue-linie-messen (p-akt hz-vorgabe / p2 rad ux uy) + (setq p2 (getpoint p-akt + (if hz-vorgabe + "\n\nEndpunkt entlang Fahrtrichtung waehlen (bestimmt die Laenge): " + "\n\nEndpunkt der Linie (XY, beliebige Richtung): "))) + (if (null p2) + nil + (if hz-vorgabe + (progn + (setq rad (* (float hz-vorgabe) (/ pi 180.0)) ux (cos rad) uy (sin rad)) + ;; Skalarprojektion des gewaehlten Vektors auf die Fahrtrichtung + (list (+ (* (- (car p2) (car p-akt)) ux) (* (- (cadr p2) (cadr p-akt)) uy)) + hz-vorgabe) + ) + (list (vfl-planar-dist p-akt p2) (vfl-planar-hz p-akt p2)) + ) + ) +) + +;; Rahmen am Ende eines vfs-*-Bausteins: die Bausteine (Entry/Koerper/Exit) +;; enden IMMER auf der 3-Grad-Basisneigung (siehe Prinzipien-Dok Abschnitt 6). +(defun vfl-frame-3grad (punkt hz) + (make-frame-from-dir punkt (hz-winkel->xu hz (ssg-cfg-or "vario" "gefaelle_winkel" 3)))) + +;; 20-Meter-Regel: warnt, wenn die VF-Segmente seit der Umlenkstation 20 m +;; ueberschreiten (Prinzipien-Dok Abschnitt 7). v1: nur Hinweis, kein +;; automatisches Einfuegen einer Zwischen-Motorstation. +(defun vfl-20m-check (p-umlenk p-akt / laenge) + (setq laenge (vfl-planar-dist p-umlenk p-akt)) + (if (> laenge 20000.0) + (princ (strcat "\n >>> HINWEIS: Foerderer laenger als 20 m (" + (rtos (/ laenge 1000.0) 2 2) " m) - Motorstation empfohlen!")) + ) +) + +;; Baut EINE reine VarioFoerderer-Einheit als interaktive Sub-Kette: +;; genau EINE Umlenkstation (Eingang) ... beliebig viele Koerper-Sub-Segmente +;; und Vario-Kurven ... genau EINE Motorstation (Ausgang). Siehe Prinzipien-Dok +;; Abschnitt 2+4. Jedes Koerper-Sub-Segment beginnt/endet auf 3-Grad-Neigung. +;; frame: Eingangsrahmen (KS_AUS des Vorgaenger-Elements, i.d.R. AS-Element). +;; hz1/richtung1/winkel1/L_GF1/L_VF1: Daten des ersten (bereits klassifizierten) +;; VF-Linien-Sub-Segments aus vfl-segment-entscheidung. +;; gf-am-ausgang: T => halbe Staustrecke als GF2 am Ausgang (ohne Separator), +;; nil => gesamte Staustrecke am Einlauf (GF1), kein GF2. +;; Rueckgabe: (frame anzahl-koerper) +(defun vfl-vf-einheit (frame hz1 richtung1 winkel1 L_GF1 L_VF1 gf-am-ausgang / + p-umlenk vf-count antwort L_GF-eff L_GF1-bau L_GF2-bau + linie-mess dL dH hn hzn richtn ent typ w lgf lvf + letzt-hz fertig) + ;; Gesamte Staustrecke (mind. *vfl-gf-min-laenge*) auf Einlauf/Ausgang verteilen. + (setq L_GF-eff (max L_GF1 *vfl-gf-min-laenge*)) + (if gf-am-ausgang + (setq L_GF1-bau (/ L_GF-eff 2.0) L_GF2-bau (/ L_GF-eff 2.0)) ; halbe/halbe + (setq L_GF1-bau L_GF-eff L_GF2-bau 0.0)) ; alles am Einlauf + + ;; --- Eingang: GF1 + Separator + Umlenkstation --- + (setq frame (vfl-frame-3grad (vfs-vf-entry (car frame) L_GF1-bau hz1) hz1)) + (if (> L_GF1-bau 0.1) + (vfl-acc-gf-seg L_GF1-bau (ssg-cfg-or "vario" "gefaelle_winkel" 3))) + (setq *vfl-acc-separator* (1+ *vfl-acc-separator*)) ; Einlauf-Separator (in vfs-vf-entry) + (setq p-umlenk (car frame)) + + ;; --- Erstes Koerper-Sub-Segment (das bereits klassifizierte Linien-Segment) --- + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtung1 winkel1 L_VF1 hz1) hz1)) + (setq vf-count 1 letzt-hz hz1) + (vfl-acc-vf-seg richtung1 winkel1 L_VF1) + (vfl-20m-check p-umlenk (car frame)) + + ;; --- Fortsetzungs-Schleife bis Foerderer-Ende --- + (setq fertig nil) + (while (not fertig) + (princ "\n\nIst der Endpunkt der Foerderer?") + (princ "\n 1 - Ja (Motorstation setzen)") + (princ "\n 2 - Nein (weiterbauen)") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (if (= antwort "1") + (setq fertig t) + (progn + (princ "\n\nNaechstes Element im VarioFoerderer:") + (princ "\n 1 - Horizontaler Foerderer (Linie ohne Hoehendifferenz)") + (princ "\n 2 - Vario-Kurve (30/60/90 Grad)") + (princ "\n 3 - Auf/Ab-Foerderer (Linie mit Hoehendifferenz)") + (setq antwort (getstring "\nIhre Wahl (1/2/3) [3]: ")) + (cond + ;; --- Vario-Kurve (aendert hz) --- + ((= antwort "2") + (setq frame (vfl-insert-vario-kurve frame)) + (setq letzt-hz (car (frame->hz-winkel frame))) + ) + ;; --- Horizontaler Foerderer (folgt der aktuellen Fahrtrichtung) --- + ((= antwort "1") + (setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz)) + (if linie-mess + (progn + (setq dL (car linie-mess) hzn (cadr linie-mess)) + (if (> dL 1.0) + (progn + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) "Ab" 0 dL hzn) hzn)) + (setq vf-count (1+ vf-count) letzt-hz hzn) + (vfl-acc-vf-seg "horizontal" 0 dL) + (vfl-20m-check p-umlenk (car frame)) + ) + (princ "\n Linie zu kurz (oder entgegen der Fahrtrichtung) - uebersprungen.") + ) + ) + ) + ) + ;; --- Auf/Ab-Foerderer (folgt der aktuellen Fahrtrichtung) --- + (t + (setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz)) + (if linie-mess + (progn + (setq dL (car linie-mess) hzn (cadr linie-mess)) + (if (> dL 1.0) + (progn + (setq hn (getreal (strcat "\nHoehe (Z) des Endpunkts [" + (rtos (caddr (car frame)) 2 1) "]: "))) + (if (null hn) (setq hn (caddr (car frame)))) + (setq dH (- hn (caddr (car frame)))) + (setq richtn (if (>= dH 0.0) "Auf" "Ab")) + (setq dH (abs dH)) + (setq ent (vfl-segment-entscheidung dL dH richtn)) + (setq typ (nth 0 ent) w (nth 1 ent) lgf (nth 2 ent) lvf (nth 3 ent)) + (if (and typ (= typ "VF")) + (progn + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn w lvf hzn) hzn)) + (setq vf-count (1+ vf-count) letzt-hz hzn) + (vfl-acc-vf-seg richtn w lvf) + (vfl-20m-check p-umlenk (car frame)) + ) + (princ (strcat "\n Segment waere GF (zu steil) oder nicht baubar - " + "innerhalb einer VF-Einheit nicht erlaubt.\n" + " Bitte flacher/steigend zeichnen oder Foerderer beenden.")) + ) + ) + (princ "\n Linie zu kurz - uebersprungen.") + ) + ) + ) + ) + ) + ) + ) + ) + + ;; --- Ausgang: Motorstation [+ GF2 je nach Wahl], KEIN Separator --- + ;; Der Separator sitzt erst vor dem ES-Element (Kettenende) bzw. optional + ;; zwischen zwei Foerderern - nicht hier. + ;; Motorseite erfassen: derzeit immer "rechts" (nur diese DWG vorhanden; + ;; die "links"-Einzel-DWG wird spaeter ergaenzt). + (setq *vfl-acc-motorseite* (append *vfl-acc-motorseite* (list "rechts"))) + (setq frame (vfl-frame-3grad + (vfs-vf-exit (car frame) L_GF2-bau letzt-hz nil) letzt-hz)) + (if (> L_GF2-bau 0.1) + (vfl-acc-gf-seg L_GF2-bau (ssg-cfg-or "vario" "gefaelle_winkel" 3))) + (list frame vf-count) +) + +;; GF-Bogen (horizontale Kurve, Neigung bleibt wie im aktuellen Frame). +;; Fragt Winkel (30/60/90) und Seite interaktiv ab. +(defun vfl-insert-gf-bogen (frame / bwinkel bseite antwort blockname) + (princ "\n\nGF-Bogen - Winkel waehlen:") + (princ "\n 1 - 30 Grad\n 2 - 60 Grad\n 3 - 90 Grad") + (setq antwort (getint "\nIhre Wahl (1/2/3) [3]: ")) + (if (null antwort) (setq antwort 3)) + (setq bwinkel (cond ((= antwort 1) 30) ((= antwort 2) 60) (t 90))) + (princ "\nGF-Bogen - Seite waehlen:") + (princ "\n 1 - Links\n 2 - Rechts") + (setq antwort (getstring "\nIhre Wahl (1/2) [1]: ")) + (setq bseite (if (= antwort "2") "rechts" "links")) + (setq blockname (gf-bogen-blockname bwinkel bseite)) + (princ (strcat "\n Fuege " blockname " ein ...")) + ;; GF-Bogen zaehlen (Seite L/R + Winkel) -> Attribut GF_Bogen_L/R_xx + (setq *vfl-acc-gfbogen* + (vfl-inc-count *vfl-acc-gfbogen* + (strcat (if (= bseite "rechts") "R" "L") "_" (itoa bwinkel)))) + (insert-block-ks-to-ks blockname frame) +) + +;; Vario-Kurve-Blockname (links/rechts x 30/60/90 x TEF aussen/innen). +(defun vfl-kurve-blockname (kwinkel kseite kvariante) + (strcat "Vario_Kurve_" kseite "_" (itoa kwinkel) "_TEF_" kvariante) +) + +;; Vario-Kurve (horizontale Kurve, Neigung bleibt wie im aktuellen Frame). +;; Die Vario_Kurve-Bloecke fuehren KS_EIN/KS_AUS als KSYS_EIN/KSYS_AUS - +;; extract-ks-from-block-raw normalisiert das (siehe ks-normalize-name). +(defun vfl-insert-vario-kurve (frame / kwinkel kseite kvariante antwort blockname + hz flach-frame) + (princ "\n\nVario-Kurve - Winkel waehlen:") + (princ "\n 1 - 30 Grad\n 2 - 60 Grad\n 3 - 90 Grad") + (setq antwort (getint "\nIhre Wahl (1/2/3) [3]: ")) + (if (null antwort) (setq antwort 3)) + (setq kwinkel (cond ((= antwort 1) 30) ((= antwort 2) 60) (t 90))) + (princ "\nVario-Kurve - Seite waehlen:") + (princ "\n 1 - Links\n 2 - Rechts") + (setq antwort (getstring "\nIhre Wahl (1/2) [1]: ")) + (setq kseite (if (= antwort "2") "rechts" "links")) + (princ "\nVario-Kurve - Variante waehlen:") + (princ "\n 1 - Aussen\n 2 - Innen") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (setq kvariante (if (= antwort "1") "aussen" "innen")) + (setq blockname (vfl-kurve-blockname kwinkel kseite kvariante)) + (princ (strcat "\n Fuege " blockname " ein ...")) + ;; Vario-Kurve zaehlen (Variante A=aussen/I=innen + Winkel) -> VF_Bogen_A/I_xx + (setq *vfl-acc-variokurve* + (vfl-inc-count *vfl-acc-variokurve* + (strcat (if (= kvariante "aussen") "A" "I") "_" (itoa kwinkel)))) + ;; Vario-Kurve ist ein HORIZONTALER Richtungswechsel (KEINE Neigung). + ;; Der aktuelle Frame traegt die 3-Grad-Basisneigung - fuer die Kurve wird er + ;; daher auf 0 Grad abgeflacht (Position + Fahrtrichtung bleiben erhalten). + (setq hz (car (frame->hz-winkel frame))) + (setq flach-frame (make-frame-from-dir (car frame) (hz-winkel->xu hz 0.0))) + (insert-block-ks-to-ks blockname flach-frame) +) + +;; ============================================================ +;; TEIL 3: KETTENANFANG (AS) / KETTENENDE (ES) +;; ============================================================ + +;; AS-Element am Kettenanfang. typ="GF": AS wird gekippt eingefuegt (KS_EIN +;; auf den Neigungswinkel des ersten Segments ausgerichtet), wie in +;; Gefaellestrecke. typ="VF": AS wird flach eingefuegt (nur hz, keine +;; Neigung), wie im Standard-VarioFoerderer (vfs-as-teil). +;; Rueckgabe: Frame am AS-Ausgang (KS_AUS). +(defun vfl-insert-as-element (typ startpunkt hz winkel as-seite / + ein-hz-as rad-h rad-v xu-ein) + (if (= typ "GF") + (progn + (setq ein-hz-as (if (= as-seite "rechts") (+ hz 90.0) (- hz 90.0))) + (setq rad-h (* (float ein-hz-as) (/ pi 180.0))) + (setq rad-v (* (float winkel) (/ pi 180.0))) + (setq xu-ein (list (* (cos rad-h)(cos rad-v)) + (* (sin rad-h)(cos rad-v)) + (- (sin rad-v)))) + (insert-block-mixed-to-ks + (strcat "AS_Element_90_" as-seite) + (make-frame-from-dir startpunkt xu-ein) + (caddr startpunkt) nil "KS_EIN") + ) + (make-frame-from-dir (vfs-as-teil startpunkt as-seite hz) (hz-winkel->xu hz 0.0)) + ) +) + +;; ES-Seite erst am Kettenende abfragen (Links/Rechts). +(defun vfl-frage-es-seite ( / antwort) + (princ "\n\nEIN-Element (ES_Element_90_*) - Seite waehlen:") + (princ "\n 1 - Links\n 2 - Rechts") + (setq antwort (getstring "\nIhre Wahl (1/2) [1]: ")) + (if (= antwort "2") "rechts" "links") +) + +;; Separator (300mm) an der aktuellen Stelle einfuegen (bei 3-Grad-Neigung, +;; Frame-Kettung ueber KS). Rueckgabe: neuer Frame am Separator-Ausgang. +;; Genutzt fuer den optionalen Zwischen-Separator zwischen zwei Foerderern. +(defun vfl-insert-separator (frame / hz w sep-endpunkt) + (setq hz (car (frame->hz-winkel frame))) + (setq w (ssg-cfg-or "vario" "gefaelle_winkel" 3)) + (setq sep-endpunkt + (gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm" (car frame) hz w 300 0)) + (setq *vfl-acc-separator* (1+ *vfl-acc-separator*)) + (make-frame-from-dir sep-endpunkt (hz-winkel->xu hz w)) +) + +;; ES-Element am Kettenende: Separator (300mm) + ES-Element. +;; typ="GF": Neigung = winkel des GF-Segments. +;; typ="VF": Neigung = 3 Grad (Auslauf einer VF-Einheit endet auf 3 Grad). +;; Das ES-Element wird rein per KS-zu-KS an den Separator angekettet +;; (`insert-block-ks-to-ks`) - sein KS_EIN folgt exakt dem Separator-Ausgang +;; (Position + Neigung). KEIN Z-Ziel (anders als im Standalone-Gefaelle, wo eine +;; feste Endhoehe erzwungen wird): der Linienzug laeuft frei aus, ein Z-Ziel +;; wuerde die ES-Hoehe kuenstlich verschieben -> Versatz. hoehe-ziel wird daher +;; hier nicht mehr verwendet (bleibt fuer Signatur-Kompatibilitaet). +(defun vfl-insert-es-element (typ frame hz winkel hoehe-ziel es-seite / + w-eff sep-endpunkt sep-frame) + (setq w-eff (if (= typ "GF") winkel (ssg-cfg-or "vario" "gefaelle_winkel" 3))) + (setq sep-endpunkt + (gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm" (car frame) hz w-eff 300 0)) + (setq sep-frame (make-frame-from-dir sep-endpunkt (hz-winkel->xu hz w-eff))) + (if (boundp '*vfl-acc-separator*) + (setq *vfl-acc-separator* (1+ *vfl-acc-separator*))) ; Separator vor ES + (insert-block-ks-to-ks (strcat "ES_Element_90_" es-seite) sep-frame) +) + +;; ============================================================ +;; TEIL 4: BLOCK-ERSTELLUNG (Nummerierung + Attribute) +;; ============================================================ +(defun vfl-block-erstellen (vfl-nummer anzahl-gf anzahl-vf hoehe-von hoehe-bis + delta-l-total as-seite es-seite startpunkt lastEnt / + vfl-bname vfl-ss ent vfl-insert typ-str) + ;; TYP: einsegmentige Gefaellestrecke ohne Bogen -> "Gefaellestrecke"; + ;; mit VF, Bogen oder mehreren GF-Segmenten -> "Streckengruppe". + (setq typ-str + (if (or (> anzahl-vf 0) + (> anzahl-gf 1) + (> (length *vfl-acc-gfbogen*) 0) + (> (length *vfl-acc-variokurve*) 0)) + "Streckengruppe" + "Gefaellestrecke")) + (setq vfl-bname (strcat "VF_" (itoa vfl-nummer))) + ;; ATTDEFs nach gemeinsamem Strecken-Schema (Reihenfolge!) + (foreach def (ssg-strecke-attrib-defs typ-str) + (entmake + (list '(0 . "ATTDEF") + (cons 10 startpunkt) + (cons 11 startpunkt) + '(40 . 50.0) + (cons 1 (cadr def)) + (cons 2 (car def)) + (cons 3 (car def)) + '(70 . 1) + '(72 . 0) + '(74 . 0))) + ) + (setq vfl-ss (ssadd)) + (setq ent (if lastEnt (entnext lastEnt) (entnext))) + (while ent + (ssadd ent vfl-ss) + (setq ent (entnext ent)) + ) + (setvar "ATTREQ" 0) + (setvar "ATTDIA" 0) + (command "_.-BLOCK" vfl-bname startpunkt vfl-ss "") + (command "_.INSERT" vfl-bname startpunkt 1.0 1.0 0.0) + (setvar "ATTREQ" 1) + (setq vfl-insert (entlast)) + ;; Werte (volle Liste; nicht vorhandene Tags ignoriert ssg-attrib-set-on) + (ssg-attrib-set-on vfl-insert + (list + (cons "Bezeichnung" vfl-bname) + (cons "MONTAGEHOEHE_m" (rtos (/ (+ hoehe-von hoehe-bis) 2000.0) 2 3)) + (cons "HOEHE_VON" (itoa (fix hoehe-von))) + (cons "HOEHE_BIS" (itoa (fix hoehe-bis))) + (cons "DELTA_H" (itoa (fix (abs (- hoehe-bis hoehe-von))))) + (cons "DELTA_L" (itoa (fix delta-l-total))) + (cons "TYP" typ-str) + (cons "SEITE_AS" as-seite) + (cons "SEITE_ES" es-seite) + (cons "ANZAHL_GF" (itoa anzahl-gf)) + (cons "L_GF_m" (vfl-join-komma *vfl-acc-lgf*)) + (cons "GF_WINKEL" (vfl-join-komma *vfl-acc-gfwinkel*)) + ;; GF-Boegen (Richtungswechsel im GF-Teil), gezaehlt nach Seite+Winkel + (cons "GF_Bogen_L_90" (itoa (vfl-get-count *vfl-acc-gfbogen* "L_90"))) + (cons "GF_Bogen_L_60" (itoa (vfl-get-count *vfl-acc-gfbogen* "L_60"))) + (cons "GF_Bogen_L_30" (itoa (vfl-get-count *vfl-acc-gfbogen* "L_30"))) + (cons "GF_Bogen_R_90" (itoa (vfl-get-count *vfl-acc-gfbogen* "R_90"))) + (cons "GF_Bogen_R_60" (itoa (vfl-get-count *vfl-acc-gfbogen* "R_60"))) + (cons "GF_Bogen_R_30" (itoa (vfl-get-count *vfl-acc-gfbogen* "R_30"))) + (cons "ANZAHL_VF" (itoa anzahl-vf)) + (cons "MOTORSEITE" (vfl-join-komma *vfl-acc-motorseite*)) + (cons "L_VF_m" (vfl-join-komma *vfl-acc-lvf*)) + (cons "ANTRIEBFAHRTRICHTUNG" (vfl-join-komma *vfl-acc-richtung*)) + (cons "VF_WINKEL" (vfl-join-komma *vfl-acc-winkel*)) + ;; Vario-Kurven (Richtungswechsel im VF-Teil), A=aussen / I=innen + (cons "VF_Bogen_A_90" (itoa (vfl-get-count *vfl-acc-variokurve* "A_90"))) + (cons "VF_Bogen_A_60" (itoa (vfl-get-count *vfl-acc-variokurve* "A_60"))) + (cons "VF_Bogen_A_30" (itoa (vfl-get-count *vfl-acc-variokurve* "A_30"))) + (cons "VF_Bogen_I_90" (itoa (vfl-get-count *vfl-acc-variokurve* "I_90"))) + (cons "VF_Bogen_I_60" (itoa (vfl-get-count *vfl-acc-variokurve* "I_60"))) + (cons "VF_Bogen_I_30" (itoa (vfl-get-count *vfl-acc-variokurve* "I_30"))) + (cons "ANZAHL_SEPARATOR" (itoa *vfl-acc-separator*)) + ) + ) + ;; Aufsteigende, eindeutige ID vergeben (wie beim Kreisel), falls verfuegbar. + (if (car (atoms-family 1 '("SSG-ID-GENERATE"))) + (ssg-id-generate vfl-insert)) + (princ (strcat "\n>>> Block '" vfl-bname "' (" typ-str ") erstellt und eingefuegt.")) + vfl-insert +) + +;; ============================================================ +;; TEIL 5: HAUPTBEFEHL - MODUS 1 (MANUELLE EINGABE) +;; ============================================================ +(defun vf-linienzug-modus ( / startpunkt start-hoehe as-seite es-seite antwort wahl + p-aktuell linie-mess hoehe-neu hoehe-bis deltaL deltaH richtung hz-neu + entscheidung typ winkel L_GF L_VF vf-einheit-res + frame letzter-typ fertig + anzahl-gf anzahl-vf vfl-nummer lastEnt) + (princ "\n\n=========================================") + (princ "\n VF-LINIENZUG - Modus 1: Manuelle Eingabe") + (princ "\n=========================================") + (princ "\nGemischte Gefaellestrecke/VarioFoerderer-Kette entlang eines frei") + (princ "\ngezeichneten Pfades. Kette beginnt immer mit AS_Element, endet immer") + (princ "\nmit ES_Element.") + + ;; Abhaengigkeit: die GF-Segmente/-Boegen nutzen Funktionen aus + ;; Gefaellestrecke.lsp (gf-insert-hz-incl-scaled, gf-bogen-blockname, ...). + ;; Bei reiner VarioFoerderer-Ladung ohne Gefaellestrecke wuerde der GF-Zweig + ;; fehlschlagen - deshalb hier pruefen. + (if (null (car (atoms-family 1 '("GF-INSERT-HZ-INCL-SCALED")))) + (progn + (alert (strcat "Gefaellestrecke-Modul nicht geladen!\n" + "Der Linienzug-Typ benoetigt Gefaellestrecke.lsp\n" + "(GF-Segmente und GF-Boegen). Bitte Menue laden.")) + (exit) + ) + ) + + (if (not *lib-initialized*) (init-bibliothek)) + + (setq startpunkt (getpoint "\n\nStartpunkt der Kette waehlen: ")) + (if (null startpunkt) (progn (princ "\nAbgebrochen.") (exit))) + ;; Hoehe (Z) des Startpunkts abfragen und in die Z-Koordinate uebernehmen. + (setq start-hoehe + (getreal (strcat "\nHoehe (Z) des Startpunkts [" (rtos (caddr startpunkt) 2 1) "]: "))) + (if (null start-hoehe) (setq start-hoehe (caddr startpunkt))) + (setq startpunkt (list (car startpunkt) (cadr startpunkt) start-hoehe)) + + (princ "\n\nAUS-Element (AS_Element_90_*) - Seite waehlen:") + (princ "\n 1 - Links\n 2 - Rechts") + (setq antwort (getstring "\nIhre Wahl (1/2) [1]: ")) + (setq as-seite (if (= antwort "2") "rechts" "links")) + ;; ES-Seite wird erst am Kettenende abgefragt (siehe vfl-frage-es-seite), + ;; da das ES-Element erst dort eingefuegt wird. + + (setq vfl-nummer (vf-next-number)) + (setq lastEnt (vf-lastent-ohne-attribute)) + (setq p-aktuell startpunkt) + (setq letzter-typ nil fertig nil frame nil) + (setq anzahl-gf 0 anzahl-vf 0) + (vfl-acc-reset) + + (while (not fertig) + ;; --- Naechstes Element bestimmen --- + ;; Nach einem GF-Segment ODER einer geschlossenen VF-Einheit (beide enden + ;; auf 3-Grad-Neigung) darf ein GF-Bogen oder eine neue Linie folgen. Nur + ;; am Kettenanfang folgt direkt eine neue Linie. Vario-Kurven werden + ;; INNERHALB der VF-Einheit behandelt (vfl-vf-einheit). + (setq wahl "Linie") + (if (or (= letzter-typ "GF") (= letzter-typ "VF")) + (progn + (princ "\n\nNaechstes Element waehlen:") + (princ "\n 1 - GF-Bogen (horizontale Kurve)") + (princ "\n 2 - Neue Linie") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (setq wahl (if (= antwort "1") "GF-Bogen" "Linie")) + ) + ) + + (cond + ((= wahl "GF-Bogen") + (setq frame (vfl-insert-gf-bogen frame)) + (setq p-aktuell (car frame)) + ) + (t ;; --- Neue Linie --- + ;; Fahrtrichtung folgt dem vorherigen Element (Frame); nur das erste + ;; Segment (frame=nil) definiert die Richtung frei. + (setq linie-mess (vfl-neue-linie-messen p-aktuell + (if frame (car (frame->hz-winkel frame)) nil))) + (if (null linie-mess) (progn (princ "\nAbgebrochen.") (exit))) + (setq deltaL (car linie-mess) hz-neu (cadr linie-mess)) + (if (< deltaL 1.0) + (princ "\nFEHLER: Linie zu kurz (oder entgegen der Fahrtrichtung) - bitte erneut waehlen.") + (progn + ;; Segment-Typ bestimmen. Kurzsegment (deltaL < 1000 mm): KEINE + ;; Hoehenabfrage - automatisch 3-Grad-Gefaellestrecke. Grund: ein VF + ;; braucht >= 1000 mm deltaL (Umlenk- + Motorstation = 500+500 mm), + ;; und eine GF ist mindestens 3 Grad geneigt. Die Endhoehe ergibt + ;; sich damit fest aus deltaH = deltaL*tan(3 Grad). + (if (< deltaL 1000.0) + (progn + (setq typ "GF" winkel 3.0 richtung "Ab" L_GF nil L_VF nil) + (setq deltaH (* deltaL (/ (sin (* 3.0 (/ pi 180.0))) + (cos (* 3.0 (/ pi 180.0)))))) + (setq hoehe-neu (- (caddr p-aktuell) deltaH)) + (princ (strcat "\n>>> Kurzes Segment (deltaL=" (rtos deltaL 2 0) + " mm < 1000): automatisch 3-Grad-Gefaellestrecke" + " (keine Hoehenabfrage, deltaH=" (rtos deltaH 2 1) " mm).")) + ) + (progn + (setq hoehe-neu + (getreal (strcat "\nHoehe (Z) des Linienendpunkts [" (rtos (caddr p-aktuell) 2 1) "]: "))) + (if (null hoehe-neu) (setq hoehe-neu (caddr p-aktuell))) + (setq deltaH (- hoehe-neu (caddr p-aktuell))) + (setq richtung (if (>= deltaH 0.0) "Auf" "Ab")) + (setq deltaH (abs deltaH)) + (setq entscheidung (vfl-segment-entscheidung deltaL deltaH richtung)) + (setq typ (nth 0 entscheidung) winkel (nth 1 entscheidung) + L_GF (nth 2 entscheidung) L_VF (nth 3 entscheidung)) + ) + ) + + (if (null typ) + (alert (strcat "Segment geometrisch nicht baubar!\n" + "deltaL=" (rtos deltaL 2 0) " mm, deltaH=" (rtos deltaH 2 0) + " mm, Richtung=" richtung + "\nBitte anderen Endpunkt/Hoehe waehlen.")) + (progn + ;; Kettenanfang: AS-Element passend zum ersten Segment-Typ einfuegen + (if (null frame) + (setq frame + (vfl-insert-as-element typ p-aktuell hz-neu + (if (= typ "GF") winkel 0.0) as-seite)) + ) + + (if (= typ "GF") + (progn + ;; --- reine Gefaellestrecke --- + (setq frame (vfl-insert-gf-segment (car frame) hz-neu deltaL winkel)) + (setq anzahl-gf (1+ anzahl-gf)) + ;; GF-Segment erfassen (Schraeglaenge = deltaL/cos(winkel)) + (vfl-acc-gf-seg (/ deltaL (cos (* (float winkel) (/ pi 180.0)))) winkel) + (setq letzter-typ "GF") + (setq p-aktuell (car frame)) + (princ "\nIst das das Kettenende?") + (princ "\n 1 - Ja (ES-Element setzen)") + (princ "\n 2 - Nein (weiterbauen)") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (if (= antwort "1") + (progn + (setq es-seite (vfl-frage-es-seite)) + (setq frame (vfl-insert-es-element "GF" frame hz-neu winkel + (caddr (car frame)) es-seite)) + (setq fertig t) + ) + ) + ) + (progn + ;; --- VarioFoerderer-Einheit (eine Umlenk- bis eine + ;; Motorstation, mehrsegmentig; interaktiv in vfl-vf-einheit) --- + ;; GF-Verteilung fragen: halbe Staustrecke als GF2 am Ausgang + ;; oder gesamte Staustrecke am Einlauf. + (princ "\n\nStaustrecke (GF) - Verteilung waehlen:") + (princ "\n 1 - Haelfte am Ausgang (GF2), Haelfte am Einlauf (GF1)") + (princ "\n 2 - Gesamte Staustrecke am Einlauf (GF1), kein GF2") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (setq vf-einheit-res + (vfl-vf-einheit frame hz-neu richtung winkel L_GF L_VF + (= antwort "1"))) + (setq frame (nth 0 vf-einheit-res)) + (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) + (setq letzter-typ "VF") + (setq p-aktuell (car frame)) + ;; Kettenende? Zwischen einem AS und ES koennen mehrere + ;; Foerderer (Motoren) liegen -> bei Nein optional einen + ;; Separator einfuegen und weiterbauen. + (princ "\n\nIst das das Kettenende (ES-Element)?") + (princ "\n 1 - Ja (Separator + ES-Element setzen)") + (princ "\n 2 - Nein (weiterbauen)") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (if (= antwort "1") + (progn + (setq es-seite (vfl-frage-es-seite)) + (setq frame (vfl-insert-es-element "VF" frame + (car (frame->hz-winkel frame)) 0.0 + (caddr (car frame)) es-seite)) + (setq fertig t) + ) + (progn + (princ "\n\nZusaetzlichen Separator an dieser Stelle einfuegen?") + (princ "\n 1 - Ja\n 2 - Nein") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (if (= antwort "1") + (progn + (setq frame (vfl-insert-separator frame)) + (setq p-aktuell (car frame)) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + + (setq hoehe-bis (caddr (car frame))) + ;; DELTA_L: planare Gesamtdistanz Start -> Kettenende + (vfl-block-erstellen vfl-nummer anzahl-gf anzahl-vf (caddr startpunkt) hoehe-bis + (vfl-planar-dist startpunkt (car frame)) as-seite es-seite + startpunkt lastEnt) + + (princ "\n\n=========================================") + (princ "\n>>> VF-Linienzug-Kette eingefuegt! <<<") + (princ "\n=========================================") + (princ) +) + +;; ============================================================ +;; DIAGNOSE: Blockstruktur (KS_EIN/KS_AUS) untersuchen +;; ============================================================ +;; Zeigt, wie ein Block intern aufgebaut ist - insbesondere, ob KS_EIN/KS_AUS +;; auf der ersten Explode-Ebene als Unterbloecke liegen und welche Laengen ihre +;; Achslinien haben (ks-line-axis erwartet X~1/~100, Y~2, Z~3). Damit laesst +;; sich klaeren, warum extract-ks-from-block bei den Vario_Kurve-Bloecken +;; "KS_EIN/KS_AUS fehlen" meldet. +;; Aufruf in BricsCAD: VFL_KS_DIAG -> Blockname eingeben. +(defun c:VFL_KS_DIAG ( / bname obj subs s nm inner il ilnm ps pe len) + (setq bname (getstring "\nBlockname fuer KS-Diagnose: ")) + (ensure-block-loaded bname) + (if (not (tblsearch "BLOCK" bname)) + (progn (princ (strcat "\nBlock '" bname "' nicht gefunden.")) (exit))) + (setq obj (vla-InsertBlock modelspace (vlax-3D-point '(0 0 0)) bname 1.0 1.0 1.0 0)) + (princ (strcat "\n==================================================")) + (princ (strcat "\n=== Struktur von '" bname "' (Ebene 1) ===")) + (setq subs (vlax-invoke obj 'Explode)) + (foreach s subs + (if (not (vlax-erased-p s)) + (progn + (setq nm (vla-get-ObjectName s)) + (if (= nm "AcDbBlockReference") + (progn + (princ (strcat "\n BlockRef: '" (vla-get-Name s) "' (Ebene 2:)")) + (setq inner (vlax-invoke s 'Explode)) + (foreach il inner + (if (not (vlax-erased-p il)) + (progn + (setq ilnm (vla-get-ObjectName il)) + (cond + ((= ilnm "AcDbLine") + (setq ps (vlax-safearray->list (vlax-variant-value (vla-get-StartPoint il)))) + (setq pe (vlax-safearray->list (vlax-variant-value (vla-get-EndPoint il)))) + (setq len (vec-length (list (- (car pe)(car ps)) + (- (cadr pe)(cadr ps)) + (- (caddr pe)(caddr ps))))) + (princ (strcat "\n Line len=" (rtos len 2 3) + " axis=" (if (ks-line-axis len) (ks-line-axis len) "?")))) + ((= ilnm "AcDbBlockReference") + (princ (strcat "\n BlockRef(verschachtelt): '" (vla-get-Name il) "'"))) + (t (princ (strcat "\n " ilnm))) + ) + (vla-Delete il) + ) + ) + ) + ) + (princ (strcat "\n " nm)) + ) + (vla-Delete s) + ) + ) + ) + (princ "\n=== Ende Diagnose ===") + (princ "\n==================================================") + (princ) +) + +;; ============================================================ +;; REGISTRIERUNG +;; ============================================================ +;; berechne-fn/einfuege-fn werden fuer "linienzug" NICHT im normalen Schema +;; verwendet (eigener Befehlsablauf, siehe Kommentar am Dateianfang) - beide +;; sind daher nur Platzhalter, die c:VarioFoerderer nie aufruft (Dispatch +;; erfolgt dort direkt auf vf-linienzug-modus). +(defun vfl-berechne-platzhalter (deltaL deltaH richtung seite) + (princ "\n[vf_linienzug] FEHLER: berechne-fn sollte fuer Typ 'linienzug' nie aufgerufen werden.") + (list nil nil nil nil) +) +(defun vfl-einfuege-platzhalter (deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz) + (princ "\n[vf_linienzug] FEHLER: einfuege-fn sollte fuer Typ 'linienzug' nie aufgerufen werden.") + startpunkt +) +(vf-typ-registrieren + "linienzug" + 'vfl-berechne-platzhalter + 'vfl-einfuege-platzhalter + "3D-Linienzug (gemischte Gefaellestrecke/VarioFoerderer-Kette, Modus 1: manuelle Eingabe)") + +(princ "\n>>> vf_linienzug.lsp geladen - Typ 'linienzug' registriert") +(princ) diff --git a/Lisp/vf_standard.lsp b/Lisp/vf_standard.lsp index 5f70396..a7007e4 100644 --- a/Lisp/vf_standard.lsp +++ b/Lisp/vf_standard.lsp @@ -259,7 +259,10 @@ ;; ============================================================ ;; TEIL 4: WINKELBERECHNUNG ;; ============================================================ -(defun berechne-alle-winkel (deltaL deltaH richtung / +;; feste-hz: horizontales Budget der festen Elemente (Umlenk+Motor+Separatoren). +;; nil => Standardwert FESTE_HORIZONTAL (1600, Standard-VF mit 2 Separatoren). +;; Der Linienzug uebergibt 1300 (nur 1 Separator am Einlauf, siehe vf_linienzug). +(defun berechne-alle-winkel (deltaL deltaH richtung feste-hz / winkel-list ergebnis-liste A B cos3 sin3 bogen-x1 bogen-z1 bogen-x2 bogen-z2 abs-dz-AUS abs-dz-EIN L_GF L_VF cosα sinα @@ -273,6 +276,7 @@ (exit) ) ) + (if (null feste-hz) (setq feste-hz FESTE_HORIZONTAL)) (setq abs-dz-AUS (abs aus-dz) abs-dz-EIN (abs ein-dz)) (setq winkel-list (ssg-cfg-or "vario" "bogen_winkel" @@ -312,16 +316,16 @@ (setq cosα (cos (* winkel (/ pi 180.0))) sinα (sin (* winkel (/ pi 180.0)))) (setq A (- deltaL aus-dx ein-dx bogen-x1 bogen-x2 - (* FESTE_HORIZONTAL cos3))) + (* feste-hz cos3))) (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)))) (if (= richtung "Auf") (setq B (+ deltaH abs-dz-AUS abs-dz-EIN (- bogen-z1) (- bogen-z2) - (* FESTE_HORIZONTAL sin3))) + (* feste-hz sin3))) (setq B (+ (- deltaH abs-dz-AUS abs-dz-EIN - (* FESTE_HORIZONTAL sin3)) + (* feste-hz sin3)) bogen-z1 bogen-z2)) ) (if (> (abs sinα) 0.0001) @@ -442,7 +446,7 @@ (setq FESTE_HORIZONTAL (ssg-cfg-or "vario" "feste_horizontal" 1600)) (setq staustrecke-basis (ssg-cfg-or "vario" "staustrecke_basis" 1000)) (init-bibliothek) - (berechne-alle-winkel deltaL deltaH richtung) + (berechne-alle-winkel deltaL deltaH richtung nil) ) ;; ============================================================ @@ -453,48 +457,64 @@ ;; hz: horizontale Fahrtrichtung in Grad (0=Ost/+X, 90=Nord/+Y, 180=West/-X, ;; 270=Sued/-Y). Default 0, falls vom Aufrufer nicht mitgegeben (Etage- ;; Registry-Kompatibilitaet - siehe c:VarioFoerderer). -(defun variofoerderer-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) +;; --- Teil 1/3: AS-Element (Kettenanfang) --- +;; Eigenstaendig, damit vf-linienzug (Gemischte GF/VF-Kette) ein VF-Segment +;; auch OHNE eigenes AS/ES mitten in der Kette einfuegen kann (vfs-mitte-teil +;; alleine), waehrend ein normaler Standard-VarioFoerderer weiterhin alle 3 +;; Teile nacheinander durchlaeuft (siehe variofoerderer-einfuegen unten). +(defun vfs-as-teil (startpunkt seite hz / as-block) (if (null hz) (setq hz 0.0)) (setq hz (float hz)) - (setq aktueller-punkt startpunkt) (setq as-block (strcat "AS_Element_90_" seite)) - (setq es-block (strcat "ES_Element_90_" seite)) (if (not *lib-initialized*) (init-bibliothek)) - - ;; 1. AUS_Element (princ (strcat "\n\n1/11: " as-block " (hz=" (rtos hz 2 1) grad-zeichen ")")) - (setq aktueller-punkt (insert-block-by-ks as-block aktueller-punkt hz)) + (insert-block-by-ks as-block startpunkt hz) +) - ;; 2. 1. Gefaellestrecke +;; --- Teil 2a: VF-Eingang (GF1 + Separator + Umlenkstation) --- +;; Beginnt eine reine VF-Einheit: bringt die Kette von der (3-Grad-geneigten) +;; AS-Seite ueber GF1 + Separator bis zur Umlenkstation. Wird pro VF-Einheit +;; GENAU EINMAL aufgerufen (auch wenn danach mehrere Sub-Segmente folgen). +(defun vfs-vf-entry (aktueller-punkt L_GF1 hz) + (if (null hz) (setq hz 0.0)) + (setq hz (float hz)) + ;; 1. Gefaellestrecke GF1 (verbindet AS-Neigung mit Umlenkstation) (if (> L_GF1 0.1) (progn - (princ (strcat "\n\n2/11: Gefaellestrecke (3 Grad, L=" (rtos L_GF1 2 2) " mm)")) + (princ (strcat "\n\n[VF-Eingang] Gefaellestrecke GF1 (3 Grad, L=" (rtos L_GF1 2 2) " mm)")) (setq aktueller-punkt (insert-inclined-scaled-block "Staustrecke_SP_1000_mm" aktueller-punkt L_GF1 (ssg-cfg-or "vario" "gefaelle_winkel" 3) hz)) ) - (princ "\n\n2/11: (uebersprungen)") + (princ "\n\n[VF-Eingang] Gefaellestrecke GF1 (uebersprungen)") ) - - ;; 3. Separator - (princ "\n\n3/11: Separator (300 mm, 3 Grad geneigt)") + ;; 2. Separator (am GF1-Ende / vor Umlenkstation) + (princ "\n[VF-Eingang] Separator (300 mm, 3 Grad geneigt)") (setq aktueller-punkt (insert-rotated-block-with-ks "Staustrecke_Separator_SP_300_mm" aktueller-punkt (ssg-cfg-or "vario" "gefaelle_winkel" 3) (ssg-cfg-or "vario" "separator_block_dx" 300) (ssg-cfg-or "vario" "separator_block_dz" 0) hz)) - - ;; 4. Umlenkstation - (princ "\n\n4/11: Umlenkstation (500 mm, 3 Grad geneigt)") + ;; 3. Umlenkstation + (princ "\n[VF-Eingang] Umlenkstation (500 mm, 3 Grad geneigt)") (setq aktueller-punkt (insert-rotated-block-with-ks "Vario_Umlenkstation_500mm" aktueller-punkt (ssg-cfg-or "vario" "gefaelle_winkel" 3) (ssg-cfg-or "vario" "station_block_dx" 500) (ssg-cfg-or "vario" "station_block_dz" 0) hz)) + aktueller-punkt +) + +;; --- Teil 2b: VF-Koerper (Vertikalbogen + L_VF + Vertikalbogen) --- +;; EIN reines VF-Sub-Segment. Wichtig: es BEGINNT und ENDET auf der +;; 3-Grad-Basisneigung, weil Ein- und Ausgangsbogen denselben Winkel haben. +;; Dadurch lassen sich mehrere Sub-Segmente (und Vario-Kurven dazwischen) +;; beliebig aneinanderreihen (siehe doc/VarioFoerderer_Linienzug_Prinzipien.md). +;; best-winkel=0 => horizontales Sub-Segment (Boegen fest auf_3/ab_3). +(defun vfs-vf-koerper (aktueller-punkt richtung best-winkel L_VF hz / + bogen-name bogen-mass bogen-dx bogen-dz) + (if (null hz) (setq hz 0.0)) + (setq hz (float hz)) ;; 5. 1. Vertikalbogen ;; Sonderfall best-winkel=0 (horizontale Zwischenstrecke, siehe @@ -598,36 +618,84 @@ ) ) - ;; 8. Motorstation - (princ "\n\n8/11: Motorstation (500 mm, 3 Grad geneigt)") + aktueller-punkt +) + +;; --- Teil 2c: VF-Ausgang (Motorstation [+ GF2] [+ Separator]) --- +;; Schliesst eine reine VF-Einheit mit der Motorstation. +;; L_GF2 > 0.1 => GF2 (3 Grad) wird angehaengt (verbindet Motorstation +;; mit der Auslauf-Neigung). +;; mit-separator T => Separator (300 mm) am Ende (Standard-VF: vor ES). +;; Standard-VF: L_GF2 = L_GF/2, mit-separator = t. +;; Linienzug: Separator wird NICHT hier gebaut (er sitzt erst vor dem ES bzw. +;; optional zwischen Foerderern) -> mit-separator = nil; GF2 je nach Wahl. +(defun vfs-vf-exit (aktueller-punkt L_GF2 hz mit-separator) + (if (null hz) (setq hz 0.0)) + (setq hz (float hz)) + ;; Motorstation + (princ "\n\n[VF-Ausgang] Motorstation (500 mm, 3 Grad geneigt)") (setq aktueller-punkt (insert-rotated-block-with-ks "Vario_Motorstation_500mm" aktueller-punkt (ssg-cfg-or "vario" "gefaelle_winkel" 3) (ssg-cfg-or "vario" "station_block_dx" 500) (ssg-cfg-or "vario" "station_block_dz" 0) hz)) - - ;; 9. 2. Gefaellestrecke + ;; GF2 (optional, verbindet Motorstation mit der Auslauf-Neigung) (if (> L_GF2 0.1) (progn - (princ (strcat "\n\n9/11: Gefaellestrecke (3 Grad, L=" (rtos L_GF2 2 2) " mm)")) + (princ (strcat "\n[VF-Ausgang] Gefaellestrecke GF2 (3 Grad, L=" (rtos L_GF2 2 2) " mm)")) (setq aktueller-punkt (insert-inclined-scaled-block "Staustrecke_SP_1000_mm" aktueller-punkt L_GF2 (ssg-cfg-or "vario" "gefaelle_winkel" 3) hz)) ) - (princ "\n\n9/11: (uebersprungen)") + (princ "\n[VF-Ausgang] GF2 (kein / uebersprungen)") ) + ;; Separator (optional, nur Standard-VF) + (if mit-separator + (progn + (princ "\n[VF-Ausgang] Separator (300 mm, 3 Grad geneigt)") + (setq aktueller-punkt + (insert-rotated-block-with-ks "Staustrecke_Separator_SP_300_mm" aktueller-punkt + (ssg-cfg-or "vario" "gefaelle_winkel" 3) + (ssg-cfg-or "vario" "separator_block_dx" 300) + (ssg-cfg-or "vario" "separator_block_dz" 0) hz)) + ) + ) + aktueller-punkt +) - ;; 10. Separator 2 - (princ "\n\n10/11: Separator (300 mm, 3 Grad geneigt)") - (setq aktueller-punkt - (insert-rotated-block-with-ks "Staustrecke_Separator_SP_300_mm" aktueller-punkt - (ssg-cfg-or "vario" "gefaelle_winkel" 3) - (ssg-cfg-or "vario" "separator_block_dx" 300) - (ssg-cfg-or "vario" "separator_block_dz" 0) hz)) +;; --- Teil 2 (Zusammensetzung): Kettenmitte fuer den Standard-VF --- +;; Entry + EIN Koerper + Exit. Verhalten identisch zur bisherigen Fassung. +(defun vfs-mitte-teil (aktueller-punkt richtung best-winkel L_GF1 L_GF2 L_VF hz) + (if (null hz) (setq hz 0.0)) + (setq hz (float hz)) + (setq aktueller-punkt (vfs-vf-entry aktueller-punkt L_GF1 hz)) + (setq aktueller-punkt (vfs-vf-koerper aktueller-punkt richtung best-winkel L_VF hz)) + (setq aktueller-punkt (vfs-vf-exit aktueller-punkt L_GF2 hz t)) + aktueller-punkt +) - ;; 11. EIN_Element +;; --- Teil 3/3: ES-Element (Kettenende) --- +(defun vfs-es-teil (aktueller-punkt seite hz / es-block) + (if (null hz) (setq hz 0.0)) + (setq hz (float hz)) + (setq es-block (strcat "ES_Element_90_" seite)) (princ (strcat "\n\n11/11: " es-block " (hz=" (rtos hz 2 1) grad-zeichen ")")) - (setq endpunkt (insert-block-by-ks es-block aktueller-punkt hz)) + (insert-block-by-ks es-block aktueller-punkt hz) +) + +;; --- Zusammensetzung: kompletter Standard-VarioFoerderer --- +;; Reine Verkettung von Teil 1+2+3, Verhalten identisch zur bisherigen +;; monolithischen Fassung. deltaL/deltaH werden hier nicht gebraucht (Teil +;; der einfuege-fn-Schnittstelle der Typ-Registry, siehe vf-typ-registrieren). +(defun variofoerderer-einfuegen (deltaL deltaH richtung best-winkel + L_GF1 L_GF2 L_VF startpunkt seite hz + / aktueller-punkt endpunkt) + (if (null hz) (setq hz 0.0)) + (setq hz (float hz)) + (setq aktueller-punkt (vfs-as-teil startpunkt seite hz)) + (setq aktueller-punkt + (vfs-mitte-teil aktueller-punkt richtung best-winkel L_GF1 L_GF2 L_VF hz)) + (setq endpunkt (vfs-es-teil aktueller-punkt seite hz)) (princ "\n\n=========================================") (princ "\n>>> Standard VarioFoerderer eingefuegt!") diff --git a/tests/test_foerderer.lsp b/tests/test_foerderer.lsp index 1b91c43..99ecc06 100644 --- a/tests/test_foerderer.lsp +++ b/tests/test_foerderer.lsp @@ -176,7 +176,7 @@ results-list)) ) (progn - (setq ergebnis (berechne-alle-winkel deltaL deltaH richtung)) + (setq ergebnis (berechne-alle-winkel deltaL deltaH richtung nil)) (setq best-winkel (car ergebnis)) (setq L_GF (cadr ergebnis)) (setq L_VF (caddr ergebnis))