;; ============================================================ ;; VF_CORE - VarioFoerderer Kern-Modul ;; Version: 26.0 ;; Architektur: Plugin/Dispatcher ;; Laedt automatisch: vf_standard.lsp, vf_etage.lsp, vf_linienzug.lsp ;; Befehle: VarioFoerderer (Alias: FOERDERANLAGE, ETAGEVARIOFOERDERER) ;; ============================================================ (vl-load-com) ;; ============================================================ ;; TEIL 1: ABHAENGIGKEITEN ;; ============================================================ ;; Bloecke liegen seit dem Flach-Refactor direkt unter data/ils/ (die Dimension ;; steckt im Dateinamen bzw. Blocknamen als Suffix _2D/_3D, siehe ssg_core.lsp). ;; VF folgt der aktuellen Dimension (ssg-ils-dim-aktuell) mit 3D-Fallback; das ;; Laden erfolgt zentral ueber ensure-block-loaded/ssg-ils-block-laden. ;; modul-pfad/block-pfad dienen nur noch der Info-Ausgabe (Basisverzeichnis). (setq modul-pfad (cond ((and (getenv "DXFMAKRO") (= (type (getenv "DXFMAKRO")) 'STR)) (strcat (vl-string-right-trim "/" (vl-string-translate "\\" "/" (getenv "DXFMAKRO"))) "/data/ils/")) ((and (boundp '*ssg-lisp-pfad*) (= (type *ssg-lisp-pfad*) 'STR) (vl-string-search "/Lisp" *ssg-lisp-pfad*)) (strcat (substr *ssg-lisp-pfad* 1 (vl-string-search "/Lisp" *ssg-lisp-pfad*)) "/data/ils/")) (t (princ "\n[vf_core] WARNUNG: Block-Pfad nicht ermittelbar!") nil) ) ) (setq block-pfad modul-pfad) ;; ssg_core laden falls noch nicht geschehen: 1) DXFM_LISP, 2) *ssg-lisp-pfad* (if (not *ssg-core-loaded*) (progn (setq *vf-core-lisp-pfad* (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-core-lisp-pfad* (progn (load (strcat *vf-core-lisp-pfad* "/ssg_core.lsp")) (ssg-load-config) (setq *ssg-core-loaded* t) ) (princ "\n[vf_core] WARNUNG: DXFM_LISP nicht gesetzt - ssg_core konnte nicht geladen werden!") ) ) ) ;; Gemeinsame KS-Extraktion + Einfuegeprimitiven (ssg_ks_insert.lsp) sicherstellen. ;; Im MNL-Fluss bereits vorgeladen; dieser Guard deckt das isolierte Laden von ;; vf_core (Test/Dev) ab. ssg-lisp-datei-pfad stammt aus dem oben geladenen ssg_core. (if (and (not (car (atoms-family 1 '("INSERT-BLOCK-BY-KS")))) (car (atoms-family 1 '("SSG-LISP-DATEI-PFAD")))) (progn (setq *vf-ksins-pfad* (ssg-lisp-datei-pfad "ssg_ks_insert.lsp")) (if (and *vf-ksins-pfad* (findfile *vf-ksins-pfad*)) (load *vf-ksins-pfad*) (princ "\n[vf_core] WARNUNG: ssg_ks_insert.lsp nicht gefunden!")) ) ) (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) (setq modelspace (vla-get-ModelSpace doc)) ;; ============================================================ ;; TEIL 2: TYP-REGISTRY ;; ============================================================ (if (null *vf-typ-registry*) (setq *vf-typ-registry* nil)) (if (null *vf-vorauswahl-typ*) (setq *vf-vorauswahl-typ* nil)) ;; Registriert einen Foerderanlagen-Typ. ;; berechne-fn: '(lambda (deltaL deltaH richtung seite) ...) ;; Ruft init-Bibliothek und Winkelberechnung auf. ;; Gibt zurueck: (list best-winkel best-L_GF best-L_VF ergebnis-liste) ;; einfuege-fn: '(lambda (deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite) ...) ;; Fuegt alle Bloecke ein. Gibt endpunkt zurueck. (defun vf-typ-registrieren (typ-name berechne-fn einfuege-fn beschreibung) (if (assoc typ-name *vf-typ-registry*) (princ (strcat "\n Typ '" typ-name "' bereits registriert.")) (setq *vf-typ-registry* (append *vf-typ-registry* (list (list typ-name berechne-fn einfuege-fn beschreibung)))) ) ) ;; ============================================================ ;; TEIL 3: GLOBALE VARIABLEN ;; ============================================================ (if (null #VF_LetzteNr) (setq #VF_LetzteNr 0)) (setq *library-loaded* nil) (setq *ks-cache* nil) (setq grad-zeichen (chr 176)) ;; 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 ;; ============================================================ ;; vec-length, ks-line-axis, ks-normalize-name, ks-relativize, ks-absolutize ;; liegen jetzt zentral in ssg_ks_insert.lsp (gemeinsam mit Gefaellestrecke, ;; siehe dort). Hier nur noch die vf_core-spezifischen Helfer. (defun fmt (x) (if (and (numberp x) (not (equal x nil))) (rtos x 2 2) "---")) (defun punkt-differenz (p1 p2) (list (- (car p2) (car p1)) (- (cadr p2) (cadr p1)) (- (caddr p2) (caddr p1)))) ;; ============================================================ ;; TEIL 4b: KS-FRAME MATHEMATIK ;; ============================================================ ;; Vektorkreuzprodukt a x b (defun vec3-cross (a b) (list (- (* (cadr a)(caddr b)) (* (caddr a)(cadr b))) (- (* (caddr a)(car b)) (* (car a)(caddr b))) (- (* (car a)(cadr b)) (* (cadr a)(car b))))) ;; Vektor auf Einheitslaenge normieren (defun vec3-normalize (v / len) (setq len (vec-length v)) (if (> len 1e-10) (list (/ (car v) len) (/ (cadr v) len) (/ (caddr v) len)) '(1.0 0.0 0.0))) ;; 3x3-Rotationsmatrix (Zeilenliste) mal 3D-Vektor (defun mat3-mul-vec3 (R v / r0 r1 r2) (setq r0 (car R) r1 (cadr R) r2 (caddr R)) (list (+ (* (car r0)(car v)) (* (cadr r0)(cadr v)) (* (caddr r0)(caddr v))) (+ (* (car r1)(car v)) (* (cadr r1)(cadr v)) (* (caddr r1)(caddr v))) (+ (* (car r2)(car v)) (* (cadr r2)(cadr v)) (* (caddr r2)(caddr v))))) ;; Rotationsmatrix R sodass: R*xe=xt, R*ye=yt, R*ze=zt ;; (xt yt zt) = Ziel-Achsen; (xe ye ze) = Quell-Achsen (je normierte Einheitsvektoren) ;; Formel: R = M_target * M_source^T ;; R[i][j] = xt[i]*xe[j] + yt[i]*ye[j] + zt[i]*ze[j] (defun mat3-from-frames (xt yt zt xe ye ze) (list (list (+ (* (car xt)(car xe)) (* (car yt)(car ye)) (* (car zt)(car ze))) (+ (* (car xt)(cadr xe)) (* (car yt)(cadr ye)) (* (car zt)(cadr ze))) (+ (* (car xt)(caddr xe)) (* (car yt)(caddr ye)) (* (car zt)(caddr ze)))) (list (+ (* (cadr xt)(car xe)) (* (cadr yt)(car ye)) (* (cadr zt)(car ze))) (+ (* (cadr xt)(cadr xe)) (* (cadr yt)(cadr ye)) (* (cadr zt)(cadr ze))) (+ (* (cadr xt)(caddr xe)) (* (cadr yt)(caddr ye)) (* (cadr zt)(caddr ze)))) (list (+ (* (caddr xt)(car xe)) (* (caddr yt)(car ye)) (* (caddr zt)(car ze))) (+ (* (caddr xt)(cadr xe)) (* (caddr yt)(cadr ye)) (* (caddr zt)(cadr ze))) (+ (* (caddr xt)(caddr xe)) (* (caddr yt)(caddr ye)) (* (caddr zt)(caddr ze)))))) ;; Normierter KS-Rahmen aus rohen KS-Daten (origin x-end y-end z-end) ;; Eingabe: Teilliste aus extract-ks-from-block-raw, z.B. (cadr (assoc "KS_EIN" ks-data)) ;; Rueckgabe: (P xu yu zu) - Ursprung + 3 normierte Einheitsvektoren (defun ks-frame-extract (ks-raw / origin x-end y-end z-end) (setq origin (car ks-raw) x-end (cadr ks-raw) y-end (caddr ks-raw) z-end (cadddr ks-raw)) (list origin (vec3-normalize (list (- (car x-end)(car origin)) (- (cadr x-end)(cadr origin)) (- (caddr x-end)(caddr origin)))) (vec3-normalize (list (- (car y-end)(car origin)) (- (cadr y-end)(cadr origin)) (- (caddr y-end)(caddr origin)))) (vec3-normalize (list (- (car z-end)(car origin)) (- (cadr z-end)(cadr origin)) (- (caddr z-end)(caddr origin)))))) ;; KS-Rahmen aus normiertem Fahrtrichtungsvektor mit horizontaler Senkrechtachse ;; Rueckgabe: (P xu yu zu) ;; Konvention: yu = horizontal senkrecht zu xu (links der Fahrtrichtung) ;; zu = xu x yu (senkrecht zur Gurtoberflaeche, nach oben) (defun make-frame-from-dir (P xu-unit / hlen yu zu) (setq hlen (sqrt (+ (* (car xu-unit)(car xu-unit)) (* (cadr xu-unit)(cadr xu-unit))))) (setq yu (if (> hlen 1e-6) (list (/ (- (cadr xu-unit)) hlen) (/ (car xu-unit) hlen) 0.0) '(1.0 0.0 0.0))) (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))) ) ;; Hinweis: Die Rz(hz)*Ry(vert)-Rotationsmatrix (vf-rot-matrix) liegt zentral ;; in ssg_core.lsp (ssg-rot-matrix-zy), da sie dependency-frei ist und auch von ;; Gefaellestrecke/TEF genutzt wird. Duenner Alias fuer die VF-Aufrufstellen: (defun vf-rot-matrix (chz shz cv sv) (ssg-rot-matrix-zy chz shz cv sv)) (defun punkt-hz-winkel->frame (punkt hz winkel) (make-frame-from-dir punkt (hz-winkel->xu hz winkel)) ) ;; ------------------------------------------------------------ ;; MOTORSEITE (Motor-/Umlenkstation links|rechts) ;; ------------------------------------------------------------ ;; Wird vom Bau-Ablauf (Dialog/Edit) vor dem Einfuegen gesetzt und von ;; vfs-vf-entry/vfs-vf-exit (bzw. Etage) beim Blocknamen gelesen. Analog zum ;; Dimensions-Override *ssg-ils-dim*, damit die Seite nicht durch saemtliche ;; Funktionssignaturen gefaedelt werden muss. Default "rechts". (if (not (boundp '*vf-motorseite*)) (setq *vf-motorseite* nil)) (defun vf-motorseite-aktuell ( / ) (if (member *vf-motorseite* '("links" "rechts")) *vf-motorseite* "rechts")) ;; ============================================================ ;; TEIL 5: KS_EIN/KS_AUS EXTRAKTION ;; ============================================================ ;; ensure-block-loaded, extract-ks-from-block-raw und extract-ks-from-block ;; liegen jetzt zentral in ssg_ks_insert.lsp (gemeinsam mit den Einfuege- ;; primitiven und Gefaellestrecke). Wird ueber die MNL vor vf_core geladen. ;; ============================================================ ;; TEIL 6: PUNKTE-AUSWAHL ;; ============================================================ (defun get-3d-point-from-object (msg / ent obj pt variant) (princ msg) (princ (ssg-text "vfc-objekt-block-waehlen")) (setq ent (entsel)) (if ent (progn (setq obj (vlax-ename->vla-object (car ent))) (if (vlax-property-available-p obj 'InsertionPoint) (progn (setq variant (vla-get-InsertionPoint obj)) (setq pt (vlax-safearray->list (vlax-variant-value variant))) (princ (ssg-textf "vfc-block-einfuegepunkt" (list (rtos (car pt) 2 3) (rtos (cadr pt) 2 3) (rtos (caddr pt) 2 3)))) ) (progn (setq pt (cadr ent)) (princ (ssg-textf "vfc-punkt-auf-objekt" (list (rtos (caddr pt) 2 2)))) ) ) ) (progn (princ (ssg-text "vfc-kein-objekt-gewaehlt")) (setq pt nil) ) ) pt ) (defun get-line-start-end-points (msg / ent obj obj-name start-pt end-pt) (princ msg) (princ (ssg-text "vfc-linie-polylinie-waehlen")) (setq ent (entsel)) (if ent (progn (setq obj (vlax-ename->vla-object (car ent))) (setq obj-name (vla-get-ObjectName obj)) (cond ((= obj-name "AcDbLine") (setq start-pt (vlax-safearray->list (vlax-variant-value (vla-get-StartPoint obj)))) (setq end-pt (vlax-safearray->list (vlax-variant-value (vla-get-EndPoint obj)))) (princ (ssg-textf "vfc-3dlinie-start-ende" (list (rtos (car start-pt) 2 2) (rtos (cadr start-pt) 2 2) (rtos (caddr start-pt) 2 2) (rtos (car end-pt) 2 2) (rtos (cadr end-pt) 2 2) (rtos (caddr end-pt) 2 2)))) ) ((= obj-name "AcDbPolyline") (setq start-pt (vlax-curve-getStartPoint obj)) (setq end-pt (vlax-curve-getEndPoint obj)) (princ (ssg-textf "vfc-3dpolylinie-start-ende" (list (rtos (caddr start-pt) 2 2) (rtos (caddr end-pt) 2 2)))) ) (t (princ (ssg-text "vfc-fehler-keine-linie")) (setq start-pt nil end-pt nil) ) ) ) (progn (princ (ssg-text "vfc-kein-objekt-gewaehlt")) (setq start-pt nil end-pt nil) ) ) (if (and start-pt end-pt) (list start-pt end-pt) nil) ) ;; ============================================================ ;; TEIL 7: VF-NUMMERIERUNG ;; ============================================================ ;; 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 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)) ) ) ) (setq #VF_LetzteNr (max #VF_LetzteNr maxnr)) (setq #VF_LetzteNr (1+ #VF_LetzteNr)) ;; Nummern ueberspringen, deren Block-DEFINITION bereits existiert (z.B. Rest ;; eines frueher fehlgeschlagenen Baus, der eine VF_n-Definition ohne INSERT ;; hinterliess). Sonst trifft das spaetere _.-BLOCK auf einen vorhandenen ;; Namen -> "Neu definieren?"-Abfrage verschluckt die Folge-Eingaben -> ;; Block wird nicht korrekt erzeugt ("Eintrag kann nicht erkannt werden"). (while (tblsearch "BLOCK" (strcat "VF_" (itoa #VF_LetzteNr))) (princ (strcat "\n [VF-Nr] VF_" (itoa #VF_LetzteNr) " existiert bereits, naechste...")) (setq #VF_LetzteNr (1+ #VF_LetzteNr))) #VF_LetzteNr ) ;; ============================================================ ;; TEIL 8: BLOCK-EINFUEGEFUNKTIONEN (gemeinsam fuer alle Typen) ;; ============================================================ ;; insert-block-by-ks (nur Rz), insert-inclined-scaled-block und ;; insert-rotated-block-with-ks (beide Rz*Ry) liegen jetzt zentral in ;; ssg_ks_insert.lsp (gemeinsam mit Gefaellestrecke). Die folgenden ;; Frame-basierten Einfuegefunktionen bleiben vf_core-spezifisch. ;; Fuegt Block ein und richtet KS_EIN am Ziel-Rahmen aus (volle 3D-Rotation). ;; target-frame : (P xt yt zt) - Ziel-Rahmen, z.B. KS_AUS des Vorgaenger-Elements ;; Rueckgabe : (P xu yu zu) - KS_AUS-Rahmen des eingefuegten Blocks (defun insert-block-ks-to-ks (blockname target-frame / block-obj temp-obj ks-data ks-ein-raw ks-aus-raw f-ein f-aus P-t xt yt zt P-ein xe ye ze P-aus xu-aus yu-aus zu-aus R R-Pein R-Paus tx ty tz T4 P-out xu-out yu-out zu-out) (setq blockname (ensure-block-loaded blockname)) (if (not (tblsearch "BLOCK" blockname)) (progn (princ (ssg-textf "vfc-fehler-block-nicht-in-bibliothek" (list blockname))) (exit))) ;; Ziel-Rahmen auspacken (setq P-t (car target-frame) xt (cadr target-frame) yt (caddr target-frame) zt (cadddr target-frame)) ;; KS_EIN/KS_AUS ueber EIGENES Temp-Objekt ermitteln (nicht am spaeter ;; tatsaechlich platzierten block-obj - extract-ks-from-block exploded ;; das uebergebene Objekt intern; das darf nicht das Objekt sein, das ;; anschliessend transformiert in der Zeichnung bleibt). (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-raw (cadr (assoc "KS_EIN" ks-data))) (setq ks-aus-raw (cadr (assoc "KS_AUS" ks-data))) (if (not (and ks-ein-raw ks-aus-raw)) (progn (princ (ssg-textf "vfc-fehler-ks-fehlen" (list blockname))) (exit))) ;; Block am Ursprung einfuegen (Rotation=0, Massstab=1) - dieses Objekt ;; bleibt tatsaechlich in der Zeichnung. (setq block-obj (vla-InsertBlock modelspace (vlax-3D-point '(0 0 0)) blockname 1.0 1.0 1.0 0)) (ssg-ils-block-auf-ebene block-obj blockname) ;; Normierte Rahmen (P xu yu zu) aus rohen KS-Daten berechnen (setq f-ein (ks-frame-extract ks-ein-raw) f-aus (ks-frame-extract ks-aus-raw)) (setq P-ein (car f-ein) xe (cadr f-ein) ye (caddr f-ein) ze (cadddr f-ein)) (setq P-aus (car f-aus) xu-aus (cadr f-aus) yu-aus (caddr f-aus) zu-aus (cadddr f-aus)) ;; Rotationsmatrix: R * xe = xt, R * ye = yt, R * ze = zt (setq R (mat3-from-frames xt yt zt xe ye ze)) ;; Translation: t = P_target - R * P_ein (setq R-Pein (mat3-mul-vec3 R P-ein)) (setq tx (- (car P-t) (car R-Pein)) ty (- (cadr P-t) (cadr R-Pein)) tz (- (caddr P-t) (caddr R-Pein))) ;; 4x4-Transformationsmatrix aufbauen und auf Block anwenden (setq T4 (vlax-tmatrix (list (list (car (car R)) (cadr (car R)) (caddr (car R)) tx) (list (car (cadr R)) (cadr (cadr R)) (caddr (cadr R)) ty) (list (car (caddr R)) (cadr (caddr R)) (caddr (caddr R)) tz) (list 0.0 0.0 0.0 1.0)))) (vla-TransformBy block-obj T4) ;; Ausgabe-Rahmen fuer KS_AUS mathematisch berechnen (kein Re-Extrahieren noetig) (setq R-Paus (mat3-mul-vec3 R P-aus)) (setq P-out (list (+ (car R-Paus) tx) (+ (cadr R-Paus) ty) (+ (caddr R-Paus) tz))) (setq xu-out (mat3-mul-vec3 R xu-aus) yu-out (mat3-mul-vec3 R yu-aus) zu-out (mat3-mul-vec3 R zu-aus)) (princ (ssg-textf "vfc-block-eingefuegt-ksaus" (list blockname (rtos (car P-out) 2 2) (rtos (cadr P-out) 2 2) (rtos (caddr P-out) 2 2)))) (list P-out xu-out yu-out zu-out)) ;; Fuegt Block ein: Orientierung wie insert-block-ks-to-ks (KS_EIN-Achsen werden ;; am Ziel-Rahmen ausgerichtet). Die POSITION wird jedoch fuer XY und Z aus ;; ZWEI VERSCHIEDENEN lokalen Referenzpunkten abgeleitet: ;; xy-ref : welcher lokale Punkt in X/Y exakt auf target-frame's P treffen soll ;; z-ref : welcher lokale Punkt in Z exakt auf z-ziel treffen soll ;; Werte fuer xy-ref/z-ref: "KS_EIN", "KS_AUS" oder nil (=Block-Ursprung 0,0,0). ;; Hintergrund: Bei seitlich angebundenen Elementen (z.B. AS_Element/ES_Element) ;; liegt KS_EIN bzw. KS_AUS seitlich versetzt vom Block-Ursprung (Drehteller- ;; Anschluss). Die Mittelachse der Foerderstrecke soll durch den Block-Ursprung ;; laufen (xy-ref), waehrend die Anschlusshoehe weiterhin exakt am jeweiligen ;; KS-Punkt (z-ref) gemessen werden muss (konsistent mit Modus 1+2 und der ;; Gefaellewinkel-Korrektur in gf-messe-dz-block). ;; target-frame : (P xt yt zt) - Ziel-Rahmen (Punkt auf der Mittelachse + Fahrtrichtung) ;; z-ziel : gewuenschte Welt-Z-Koordinate des z-ref-Punktes ;; Rueckgabe : (P xu yu zu) - KS_AUS-Rahmen des eingefuegten Blocks (defun insert-block-mixed-to-ks (blockname target-frame z-ziel xy-ref z-ref / block-obj temp-obj ks-data ks-ein-raw ks-aus-raw f-ein f-aus P-t xt yt zt xe ye ze P-ein P-aus xu-aus yu-aus zu-aus R P-xyref P-zref R-xyref R-zref tx ty tz T4 R-Paus P-out xu-out yu-out zu-out) (setq blockname (ensure-block-loaded blockname)) (if (not (tblsearch "BLOCK" blockname)) (progn (princ (ssg-textf "vfc-fehler-block-nicht-in-bibliothek" (list blockname))) (exit))) ;; Ziel-Rahmen auspacken (setq P-t (car target-frame) xt (cadr target-frame) yt (caddr target-frame) zt (cadddr target-frame)) ;; KS_EIN/KS_AUS ueber EIGENES Temp-Objekt ermitteln (nicht am spaeter ;; tatsaechlich platzierten block-obj - extract-ks-from-block exploded ;; das uebergebene Objekt intern; das darf nicht das Objekt sein, das ;; anschliessend transformiert in der Zeichnung bleibt). (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-raw (cadr (assoc "KS_EIN" ks-data))) (setq ks-aus-raw (cadr (assoc "KS_AUS" ks-data))) (if (not (and ks-ein-raw ks-aus-raw)) (progn (princ (ssg-textf "vfc-fehler-ks-fehlen" (list blockname))) (exit))) ;; Block am Ursprung einfuegen (Rotation=0, Massstab=1) - dieses Objekt ;; bleibt tatsaechlich in der Zeichnung. (setq block-obj (vla-InsertBlock modelspace (vlax-3D-point '(0 0 0)) blockname 1.0 1.0 1.0 0)) (ssg-ils-block-auf-ebene block-obj blockname) ;; Normierte Rahmen (P xu yu zu) aus rohen KS-Daten berechnen (setq f-ein (ks-frame-extract ks-ein-raw) f-aus (ks-frame-extract ks-aus-raw)) (setq xe (cadr f-ein) ye (caddr f-ein) ze (cadddr f-ein)) (setq P-ein (car f-ein)) (setq P-aus (car f-aus) xu-aus (cadr f-aus) yu-aus (caddr f-aus) zu-aus (cadddr f-aus)) ;; Rotation identisch zu insert-block-ks-to-ks: R * xe = xt, R * ye = yt, R * ze = zt (setq R (mat3-from-frames xt yt zt xe ye ze)) ;; Lokale Referenzpunkte fuer XY- bzw. Z-Zielvorgabe bestimmen (setq P-xyref (cond ((= xy-ref "KS_EIN") P-ein) ((= xy-ref "KS_AUS") P-aus) (t '(0.0 0.0 0.0)))) (setq P-zref (cond ((= z-ref "KS_EIN") P-ein) ((= z-ref "KS_AUS") P-aus) (t '(0.0 0.0 0.0)))) (setq R-xyref (mat3-mul-vec3 R P-xyref)) (setq R-zref (mat3-mul-vec3 R P-zref)) ;; Translation: xy-ref -> P-t (X/Y), z-ref -> z-ziel (Z) (setq tx (- (car P-t) (car R-xyref))) (setq ty (- (cadr P-t) (cadr R-xyref))) (setq tz (- (float z-ziel) (caddr R-zref))) ;; 4x4-Transformationsmatrix aufbauen und auf Block anwenden (setq T4 (vlax-tmatrix (list (list (car (car R)) (cadr (car R)) (caddr (car R)) tx) (list (car (cadr R)) (cadr (cadr R)) (caddr (cadr R)) ty) (list (car (caddr R)) (cadr (caddr R)) (caddr (caddr R)) tz) (list 0.0 0.0 0.0 1.0)))) (vla-TransformBy block-obj T4) ;; Ausgabe-Rahmen fuer KS_AUS mathematisch berechnen (kein Re-Extrahieren noetig) (setq R-Paus (mat3-mul-vec3 R P-aus)) (setq P-out (list (+ (car R-Paus) tx) (+ (cadr R-Paus) ty) (+ (caddr R-Paus) tz))) (setq xu-out (mat3-mul-vec3 R xu-aus) yu-out (mat3-mul-vec3 R yu-aus) zu-out (mat3-mul-vec3 R zu-aus)) (princ (ssg-textf "vfc-block-eingefuegt-xyzref-ksaus" (list blockname (if xy-ref xy-ref (ssg-text "vfc-ursprung")) (if z-ref z-ref (ssg-text "vfc-ursprung")) (rtos (car P-out) 2 2) (rtos (cadr P-out) 2 2) (rtos (caddr P-out) 2 2)))) (list P-out xu-out yu-out zu-out)) ;; ============================================================ ;; AS-/ES-Element: Winkel-Variante (30 / 90 Grad) ;; ============================================================ ;; Masse (KS_EIN->KS_AUS als (dx dy dz)) eines Elements aus dem Block ziehen. (defun vf-element-masse (blockname / temp-obj ks-data ke ka) (setq blockname (ensure-block-loaded blockname)) (if (not (tblsearch "BLOCK" blockname)) nil (progn (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 ke (cadr (assoc "KS_EIN" ks-data)) ka (cadr (assoc "KS_AUS" ks-data))) (if (and ke ka) (list (- (caar ka) (caar ke)) (- (cadr (car ka)) (cadr (car ke))) (- (caddr (car ka)) (caddr (car ke)))) nil)))) ;; Ebenen-Schwenk (Grad) eines Elements: Winkel von KS_EIN.xu nach KS_AUS.xu in ;; der XY-Ebene, direkt aus dem Block gemessen (unabhaengig davon, ob 30/90-Turn ;; oder gerade). Wird genutzt, um KS_EIN so zu drehen, dass KS_AUS entlang hz ;; zeigt: ein-hz = hz - plan-turn. Fallback 90 (Vorgabe fuer 90-Grad-Element). (defun vf-element-plan-turn (blockname / temp-obj ks-data fe fa) (setq blockname (ensure-block-loaded blockname)) (if (not (tblsearch "BLOCK" blockname)) 90.0 (progn (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 fe (ks-frame-extract (cadr (assoc "KS_EIN" ks-data)))) (setq fa (ks-frame-extract (cadr (assoc "KS_AUS" ks-data)))) (if (and fe fa) (- (* (atan (cadr (cadr fa)) (car (cadr fa))) (/ 180.0 pi)) (* (atan (cadr (cadr fe)) (car (cadr fe))) (/ 180.0 pi))) 90.0)))) ;; KS-Info eines Elements (im Block, Einbau-Rotation 0): ;; Rueckgabe (ein-ang aus-ang vx vy) ;; ein-ang / aus-ang : Ebenen-Winkel (Grad) von KS_EIN.xu bzw. KS_AUS.xu ;; vx / vy : KS_AUS.origin - KS_EIN.origin (Block-XY) ;; Damit laesst sich die KS_AUS-Lage + Achsrichtung analytisch vorausberechnen. (defun vf-element-ks-info (blockname / temp-obj ks-data fe fa) (setq blockname (ensure-block-loaded blockname)) (if (not (tblsearch "BLOCK" blockname)) nil (progn (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 fe (ks-frame-extract (cadr (assoc "KS_EIN" ks-data)))) (setq fa (ks-frame-extract (cadr (assoc "KS_AUS" ks-data)))) (if (and fe fa) (list (* (atan (cadr (cadr fe)) (car (cadr fe))) (/ 180.0 pi)) (* (atan (cadr (cadr fa)) (car (cadr fa))) (/ 180.0 pi)) (- (car (car fa)) (car (car fe))) (- (cadr (car fa)) (cadr (car fe)))) nil)))) ;; Loest fuer einen Kandidatenwinkel L_GF/L_VF aus den vorberechneten ;; Groessen A/B (horizontales/vertikales Rest-Budget). Identisch fuer Standard ;; und Etage - nur die Berechnung von A/B unterscheidet sich (daher als ;; Parameter). Rueckgabe: Ergebnistabellen-Eintrag (winkel L_GF L_VF gueltig) ;; bzw. (winkel nil nil nil), wenn keine sinnvolle Loesung existiert. ;; sin3/cos3 = sin/cos des Basis-Gefaellewinkels (3 Grad); sinEff/cosEff des ;; effektiven Winkels; sinα des Kandidatenwinkels. (defun vf-winkel-solve (winkel richtung A B sinα cosEff sinEff sin3 cos3 / L_GF L_VF gueltig) (if (> (abs sinα) 0.0001) (progn (setq L_GF (/ (- (* A sinEff) (* B cosEff)) sinα)) (if (= richtung "Auf") (setq L_VF (/ (+ (* A sin3) (* B cos3)) sinα)) (setq L_VF (/ (- (* B cos3) (* A sin3)) sinα)) ) (if (and (numberp L_GF) (numberp L_VF)) (progn (setq gueltig (and (>= L_GF 0) (>= L_VF 0))) (list winkel L_GF L_VF gueltig)) (list winkel nil nil nil)) ) (list winkel nil nil nil)) ) ;; 30/90-Wahl fuer ein AS/ES-Element. Rueckgabe: "30" oder "90" (Vorgabe 90). ;; headerkey: i18n-Key fuer die Kopfzeile ("vf-winkel-aus-header" oder ;; "vf-winkel-ein-header"). Rueckgabe: "30" oder "90" (Default 90). ;; Wizard: nutzt vflw-wahl (dcl/vf_linienzug_wizard.dcl, definiert in ;; vf_linienzug.lsp) falls dieses Modul geladen und *vfl-wizard-mode* aktiv ;; ist - sonst (oder bei jedem Fehler) unveraendert die Konsolen-Fragen ;; (princ + getstring, de/en ueber ssg-text). Gemeinsam genutzt von ;; Gefaellestrecke.lsp UND vf_linienzug.lsp. (defun vf-frage-element-winkel (headerkey / antwort) ;; Wizard-Dialog nur, wenn Wizard-Modus aktiv, vflw-wahl geladen UND die GUI ;; global nicht abgeschaltet ist (Tests: (ssg-gui-aus)) - sonst Konsole/getstring. (if (and (boundp '*vfl-wizard-mode*) *vfl-wizard-mode* (car (atoms-family 1 '("VFLW-WAHL"))) (or (not (car (atoms-family 1 '("SSG-GUI-P")))) (ssg-gui-p))) (progn (setq antwort (vl-catch-all-apply 'vflw-wahl (list (ssg-text headerkey) (list (ssg-text "vf-winkel-90") (ssg-text "vf-winkel-30")) 1))) (if (or (vl-catch-all-error-p antwort) (null antwort) (= antwort "")) (setq antwort "1")) ) (progn (princ (ssg-text headerkey)) (princ (ssg-text "vf-winkel-90")) (princ (ssg-text "vf-winkel-30")) (setq antwort (getstring (ssg-text "prompt-wahl-1-2"))) ) ) (if (= antwort "2") "30" "90")) ;; AS-/ES-Masse-Globals (aus-dx/dy/dz bzw. ein-dx/dy/dz) fuer die gewaehlte ;; Winkel-/Seiten-Variante neu setzen (fuer berechne-alle-winkel, GF-Winkel, ;; Trimmungen). Ohne gueltigen Block bleiben die Werte unveraendert. (defun vf-set-as-masse (winkel seite / m) (setq m (vf-element-masse (strcat "AS_Element_" winkel "_" seite))) (if m (setq aus-dx (car m) aus-dy (cadr m) aus-dz (caddr m)))) (defun vf-set-es-masse (winkel seite / m) (setq m (vf-element-masse (strcat "ES_Element_" winkel "_" seite))) (if m (setq ein-dx (car m) ein-dy (cadr m) ein-dz (caddr m)))) ;; ============================================================ ;; TEIL 9: GEMEINSAME EINGABE-ABFRAGE ;; ============================================================ ;; Feld-Reihenfolge der Basis-Dialog-Ergebnisliste (vfs-dialog-eingabe-basis). ;; EINE zentrale Definition statt vierfach denselben (nth i eingabe)-Block. (setq *vf-basis-eingabe-felder* '(deltaL deltaH richtung z-start hz seite dim geruest-einzelmodul geruest-typ motorseite)) ;; Entpackt die Basis-Dialog-Ergebnisliste in die gleichnamigen (dynamisch ;; skopten) Lokalvariablen des Aufrufers. Ersetzt den 10-zeiligen ;; (setq deltaL (nth 0 eingabe) ...)-Block. Nutzt (set 'sym wert): die Symbole ;; sind in der /-Deklaration des aufrufenden defun als Locals gebunden, daher ;; wirkt das Setzen nur dort (AutoLISP dynamic scoping). (defun vf-basis-eingabe-entpacken (eingabe / i) (setq i 0) (foreach feld *vf-basis-eingabe-felder* (set feld (nth i eingabe)) (setq i (1+ i))) eingabe) (defun vf-eingabe-abfragen ( / eingabe-modus line-points startpunkt endpunkt differenz deltaL deltaH deltaX deltaY richtung antwort hz-winkel) (princ (ssg-text "vfc-eingabemodus-header")) (princ (ssg-text "vfc-eingabemodus-3dlinie")) (princ (ssg-text "vfc-eingabemodus-werte")) (setq antwort (getstring (ssg-text "vfc-prompt-wahl-1-2-ohne-default"))) (cond ((= antwort "1") (setq eingabe-modus "Linie")) ((= antwort "2") (setq eingabe-modus "Wert")) (t (setq eingabe-modus "Linie") (princ (ssg-text "vfc-modus-3dlinie-fallback"))) ) (if (= eingabe-modus "Linie") (progn (princ (ssg-text "vfc-modus-3dlinie-header")) (command "BKS" "W") (princ (ssg-text "vfc-bks-weltkoordinaten")) (setq line-points (get-line-start-end-points (ssg-text "vfc-bitte-3dlinie-foerderstrecke"))) (if (null line-points) (progn (alert (ssg-text "vfc-alert-keine-gueltige-linie")) (exit)) ) (setq startpunkt (car line-points) endpunkt (cadr line-points)) (setq differenz (punkt-differenz startpunkt endpunkt)) (setq deltaX (abs (car differenz)) deltaY (abs (cadr differenz)) deltaH (caddr differenz)) (setq deltaL (max deltaX deltaY)) ;; Fahrtrichtung aus der Linie ableiten (0=Ost, 90=Nord, ...) (setq hz-winkel (* (angle '(0.0 0.0) (list (car differenz) (cadr differenz))) (/ 180.0 pi))) (princ (ssg-textf "vfc-delta-l-h-richtung" (list (chr 916) (rtos deltaL 2 2) (rtos deltaH 2 2) (rtos hz-winkel 2 1) grad-zeichen))) (if (>= deltaH 0) (progn (setq richtung "Auf") (princ (ssg-text "vfc-foerderrichtung-auf"))) (progn (setq richtung "Ab") (setq deltaH (abs deltaH)) (princ (ssg-text "vfc-foerderrichtung-ab"))) ) ) (progn (princ (ssg-text "vfc-modus-werteingabe-header")) (setq deltaL (getreal (ssg-textf "vfc-prompt-delta-l" (list (chr 916))))) (if (null deltaL) (setq deltaL 15000)) (setq deltaH (getreal (ssg-textf "vfc-prompt-delta-h" (list (chr 916))))) (if (null deltaH) (setq deltaH 3000)) ;; Foerderrichtung bleibt eine einfache Auf/Ab-Frage - die horizontale ;; Zwischenstrecke ist geometrisch NUR bei "Ab" ueberhaupt erreichbar ;; (die festen 3-Grad-Elemente wirken immer absenkend) und wird daher ;; erst SPAETER, nach Bibliotheks-Init, als Zusatzoption in der ;; Winkel-Auswahl angeboten (siehe c:VarioFoerderer) - einheitlich fuer ;; Werteingabe UND 3D-Linie. (princ (ssg-text "vfc-foerderrichtung-menu")) (setq antwort (getstring (ssg-text "vfc-prompt-wahl-1-2-ohne-default"))) (if (= antwort "2") (setq richtung "Ab") (setq richtung "Auf")) (setq startpunkt (getpoint (ssg-text "vfc-prompt-startpunkt-aus"))) (if (null startpunkt) (setq startpunkt '(0 0 0))) ;; Horizontale Fahrtrichtung waehlen (analog Gefaellestrecke Modus 1) (princ (ssg-text "vfc-fahrtrichtung-header")) (princ (ssg-textf "vfc-fahrtrichtung-0" (list grad-zeichen))) (princ (ssg-textf "vfc-fahrtrichtung-90" (list grad-zeichen))) (princ (ssg-textf "vfc-fahrtrichtung-180" (list grad-zeichen))) (princ (ssg-textf "vfc-fahrtrichtung-270" (list grad-zeichen))) (setq antwort (getstring (ssg-text "prompt-wahl-1-2-3-4"))) (cond ((= antwort "2") (setq hz-winkel 90.0)) ((= antwort "3") (setq hz-winkel 180.0)) ((= antwort "4") (setq hz-winkel 270.0)) (t (setq hz-winkel 0.0)) ) (princ (ssg-textf "vfc-fahrtrichtung-ergebnis" (list (rtos hz-winkel 2 1) grad-zeichen))) ) ) (list deltaL deltaH richtung startpunkt hz-winkel eingabe-modus) ) ;; ============================================================ ;; TEIL 10: VF_*-BLOCK-SYSTEM (gemeinsam fuer alle Typen) ;; ============================================================ ;; hz: horizontale Fahrtrichtung in Grad (0=Ost/+X, 90=Nord/+Y, ...) - siehe ;; variofoerderer-einfuegen. Beschriftungsposition wird ENTLANG dieser ;; Richtung (halbe deltaL) und SENKRECHT dazu (200mm) versetzt, statt fest ;; von +X auszugehen. Der Textwinkel rastet wie bei Gefaellestrecke immer ;; auf 0 Grad (X-Achse) oder 90 Grad (Y-Achse) ein, damit er nie auf dem ;; Kopf steht - unabhaengig von der tatsaechlichen Fahrtrichtung. ;; L_GF1/L_GF2: die beiden Gefaellestrecken-Segmente (vorne/hinten) - werden ;; als kommagetrennte Meter-Werte dargestellt (Punkt=Dezimaltrennzeichen, ;; Komma=Segment-Trenner, analog L-gf-str in Gefaellestrecke.lsp), damit ;; beide Werte eindeutig unterscheidbar bleiben. (defun vf-make-label (vf-nummer hoehe-von hoehe-bis deltaH deltaL L_VF L_GF1 L_GF2 richtung best-winkel n-separator n-scanner startpunkt hz / label-txt label-pt L_VF_str L_GF_str delta-sym chz-l shz-l halb basis-winkel) (setq delta-sym (chr 916)) (setq L_VF_str (vl-string-subst "," "." (rtos (/ L_VF 1000.0) 2 3))) (setq L_GF_str (strcat (rtos (/ L_GF1 1000.0) 2 3) "," (rtos (/ L_GF2 1000.0) 2 3))) (setq label-txt (strcat "VF" (itoa vf-nummer) (ssg-textf "vfc-label-von-bis" (list (itoa (fix hoehe-von)) (itoa (fix hoehe-bis)))) delta-sym "H=" (itoa (fix deltaH)) "mm; " delta-sym "L=" (itoa (fix deltaL)) "mm; " "VF:" L_VF_str "m; GF:" L_GF_str "m; " richtung ": " (itoa best-winkel) grad-zeichen "; " "Sep: " (itoa n-separator) "; Scan: " (itoa n-scanner))) (setq chz-l (cos (* (float hz) (/ pi 180.0)))) (setq shz-l (sin (* (float hz) (/ pi 180.0)))) (setq halb (/ (float deltaL) 2.0)) (setq label-pt (list (+ (car startpunkt) (* halb chz-l) (* 200.0 shz-l)) (+ (cadr startpunkt) (* halb shz-l) (* -200.0 chz-l)) (caddr startpunkt))) ;; Textwinkel auf 0/90 Grad einrasten (nie kopfueber, unabhaengig von hz) (setq basis-winkel (float hz)) (while (< basis-winkel 0.0) (setq basis-winkel (+ basis-winkel 360.0))) (while (>= basis-winkel 360.0) (setq basis-winkel (- basis-winkel 360.0))) (if (>= basis-winkel 180.0) (setq basis-winkel (- basis-winkel 180.0))) (setq basis-winkel (if (or (< basis-winkel 45.0) (>= basis-winkel 135.0)) 0.0 90.0)) (entmake (list '(0 . "TEXT") (cons 8 "VF_Beschriftung") '(67 . 0) (cons 10 label-pt) (cons 40 100.0) (cons 1 label-txt) (cons 50 (* basis-winkel (/ pi 180.0))) '(72 . 1) '(73 . 2) (cons 11 label-pt) )) ) ;; Ermittelt den "letzten Entity" VOR einer neuen Einfuegung, so dass ;; nachfolgende Attribut-Entities/SEQEND eines vorherigen INSERT (z.B. eines ;; vorangegangenen VF_N-Blocks) nicht versehentlich in die naechste ;; vf-block-erstellen-Sammlung hineinfallen. Muss von JEDEM Aufrufer von ;; vf-block-erstellen anstelle eines nackten (entlast) verwendet werden. (defun vf-lastent-ohne-attribute ( / ent) (setq ent (entlast)) (if (and ent (= (cdr (assoc 0 (entget ent))) "INSERT") (= (cdr (assoc 66 (entget ent))) 1)) (progn (setq ent (entnext ent)) (while (and ent (/= (cdr (assoc 0 (entget ent))) "SEQEND")) (setq ent (entnext ent)) ) ) ) ent ) ;; Erstellt VF_N-Block aus allen seit lastEnt erzeugten Entities. ;; Setzt Attributwerte entsprechend Typ, Seite und Geometrieparametern. (defun vf-block-erstellen (anlage-typ seite vf-nummer n-scanner hoehe-von hoehe-bis deltaH deltaL L_VF L_GF1 L_GF2 richtung best-winkel startpunkt lastEnt hz geruest-einzelmodul geruest-typ / vf-bname vf-ss vf-e vf-insert montagehoehe-m attdef-ypos L_GF_m-str) (if (null hz) (setq hz 0.0)) (if (null geruest-einzelmodul) (setq geruest-einzelmodul "1")) (if (null geruest-typ) (setq geruest-typ (ssg-geruest-idx-to-typ (ssg-geruest-typ-to-idx nil)))) ;; Beschriftungstext erzeugen (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 (gemeinsames Strecken-Schema; ;; VarioFoerderer ist immer TYP "Streckengruppe" -> voller Attributsatz). (setq attdef-ypos (cadr startpunkt)) (foreach def (ssg-strecke-attrib-defs "Streckengruppe") (entmake (list '(0 . "ATTDEF") '(8 . "0") '(67 . 0) (cons 10 (list (car startpunkt) attdef-ypos (caddr startpunkt))) '(40 . 50.0) (cons 1 (cadr def)) (cons 3 (car def)) (cons 2 (car def)) '(70 . 1) )) (setq attdef-ypos (- attdef-ypos 100.0)) ) ;; Alle neuen Entities seit lastEnt sammeln (setq vf-ss (ssadd)) (if lastEnt (setq vf-e (entnext lastEnt)) (setq vf-e (entnext)) ) (while vf-e (ssadd vf-e vf-ss) (setq vf-e (entnext vf-e)) ) ;; Block erstellen, einfuegen, Attribute setzen (setq vf-bname (strcat "VF_" (itoa vf-nummer))) (if (> (sslength vf-ss) 0) (progn ;; Block definieren und einfuegen mit garantiert weltparallelem BKS. ;; startpunkt ist ein Welt-Punkt; ssg-block-wrap-welt setzt das BKS ;; temporaer auf Welt (verhindert den 31.95mm-Z-Versatz bei ;; abweichendem BKS) und stellt es danach wieder her. (setq vf-insert (ssg-block-wrap-welt vf-bname startpunkt vf-ss)) (setq montagehoehe-m (/ (+ hoehe-von hoehe-bis) 2000.0)) ;; L_GF_m: kommagetrennte Segmentlaengen in Metern (vorne,hinten), ;; analog L_GF_m in Gefaellestrecke.lsp. (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 "Bezeichnung" vf-bname) ;; ARTINR bleibt auf Schema-Vorgabe "6220" (cons "MONTAGEHOEHE_m" (rtos montagehoehe-m 2 3)) (cons "HOEHE_VON_mm" (itoa (fix hoehe-von))) (cons "HOEHE_BIS_mm" (itoa (fix hoehe-bis))) (cons "DELTA_H_mm" (itoa (fix deltaH))) (cons "DELTA_L_mm" (itoa (fix deltaL))) (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" (vf-motorseite-aktuell)) (cons "L_VF_m" (rtos (/ L_VF 1000.0) 2 3)) (cons "ANTRIEBFAHRTRICHTUNG" richtung) (cons "VF_WINKEL" (itoa best-winkel)) (cons "ANZAHL_SEPARATOR" "2") (cons "ANZAHL_SCANNER" (itoa n-scanner)) (cons "GERUEST_EINZELMODUL" geruest-einzelmodul) (cons "GERUEST_TYP" geruest-typ) ) ) ;; Typ-spezifische Bogen-Zaehler (Etage: 2x Gefaellebogen je Seite) (if (= anlage-typ "etage") (if (= seite "rechts") (ssg-attrib-set-on vf-insert (list (cons "GF_Bogen_R_60" "2"))) (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)) ;; Dimension (2D/3D) merken -> Doppelklick-Edit erkennt sie automatisch. (if (car (atoms-family 1 '("SSG-DIM-XDATA-SCHREIBEN"))) (ssg-dim-xdata-schreiben vf-insert (ssg-ils-dim-aktuell))) (princ (ssg-textf "vfc-block-erstellt-eingefuegt" (list vf-bname))) vf-insert ) (progn (princ (ssg-text "vfc-fehler-keine-entities")) nil) ) ) ;; ============================================================ ;; TEIL 10b: GEMEINSAMER DIALOG-ABLAUF (Standard + Etage) ;; ============================================================ ;; Standard- und Etage-Typ teilen denselben Neuanlage-/Berechnen-Einfuege- ;; Ablauf; sie unterscheiden sich nur in Berechnungs-/Einfuege-Funktion (aus ;; der Typ-Registry), im XDATA-Marker und darin, ob die horizontale ;; Zwischenstrecke (nur "Ab") als Zusatzoption angeboten wird. ;; berechne-fn/einfuege-fn kommen aus *vf-typ-registry* (via typ). (defun vf-registry-berechne-fn (typ) (nth 1 (assoc typ *vf-typ-registry*))) (defun vf-registry-einfuege-fn (typ) (nth 2 (assoc typ *vf-typ-registry*))) ;; Gemeinsame Berechnung + Winkel/Verteilung-Dialog + Einfuegung fuer einen ;; Registry-Typ. horizontal-p = T: horizontale Zwischenstrecke (Winkel 0) als ;; Zusatzoption bei "Ab" anbieten (Standard); nil: nicht (Etage). ;; Rueckgabe: (vf-insert vf-endpunkt), bei Abbruch (nil nil). (defun vf-dialog-berechnen-einfuegen (typ vf-nummer deltaL deltaH richtung startpunkt hz seite winkel-prefill geruest-einzelmodul geruest-typ horizontal-p / berechne-fn einfuege-fn ergebnis ergebnis-liste gueltige-winkel horizontal-info winkelwahl best-winkel L_GF L_VF L_GF1 L_GF2 verteilung-modus n-scanner hoehe-von hoehe-bis lastEnt vf-insert vf-endpunkt staustrecke-basis) (setq berechne-fn (vf-registry-berechne-fn typ)) (setq einfuege-fn (vf-registry-einfuege-fn typ)) (setq ergebnis (apply berechne-fn (list deltaL deltaH richtung seite))) (setq ergebnis-liste (cadddr ergebnis)) (setq staustrecke-basis (ssg-cfg-or "vario" "staustrecke_basis" 1000)) ;; Horizontale Zwischenstrecke als Zusatzoption (nur bei "Ab", nur wenn ;; horizontal-p). Winkel-Sentinel 0. (setq horizontal-info nil) (if (and horizontal-p (= richtung "Ab")) (progn (setq horizontal-info (berechne-horizontale-mitte deltaL deltaH "Ab")) (if (and horizontal-info (caddr horizontal-info)) (setq ergebnis-liste (append ergebnis-liste (list (list 0 (car horizontal-info) (cadr horizontal-info) T)))) (setq horizontal-info nil) ) ) ) (setq gueltige-winkel (mapcar 'car (vl-remove-if-not (function (lambda (e) (and (nth 3 e) (numberp (nth 1 e)) (numberp (nth 2 e)) (>= (nth 1 e) 0) (>= (nth 2 e) 0)))) ergebnis-liste))) (if (null gueltige-winkel) (alert (ssg-textf "vfc-alert-kein-winkel" (list (chr 916) (rtos deltaL 2 0) (rtos deltaH 2 0) richtung))) (progn (setq winkelwahl (vfs-dialog-winkel-verteilung gueltige-winkel ergebnis-liste staustrecke-basis winkel-prefill)) (if (null winkelwahl) (princ (ssg-text "vfc-vorgang-abgebrochen")) (progn (setq best-winkel (nth 0 winkelwahl) L_GF (nth 1 winkelwahl) L_VF (nth 2 winkelwahl) L_GF1 (nth 3 winkelwahl) L_GF2 (nth 4 winkelwahl) verteilung-modus (nth 5 winkelwahl)) (setq n-scanner 0) (setq hoehe-von (caddr startpunkt)) (setq hoehe-bis (cond ((= richtung "Auf") (+ hoehe-von deltaH)) ((= richtung "Ab") (- hoehe-von deltaH)) (t hoehe-von))) (setq lastEnt (vf-lastent-ohne-attribute)) (setq vf-endpunkt (apply einfuege-fn (list deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz))) (setq vf-insert (vf-block-erstellen typ seite vf-nummer n-scanner hoehe-von hoehe-bis deltaH deltaL L_VF L_GF1 L_GF2 richtung best-winkel startpunkt lastEnt hz geruest-einzelmodul geruest-typ)) (if vf-insert (vf-edit-xdata-schreiben vf-insert typ hz)) (princ "\n=========================================") ) ) ) ) (list vf-insert vf-endpunkt) ) ;; Neuanlage (Menue/Konsole -> Dialog) fuer einen Registry-Typ: Startpunkt ;; (X/Y) picken, Basis-Dialog, dann vf-dialog-berechnen-einfuegen. (defun vf-dialog-ablauf (typ vf-nummer horizontal-p / eingabe deltaL deltaH richtung z-start hz seite startpunkt startpunkt-xy dim geruest-einzelmodul geruest-typ motorseite) (setq startpunkt-xy (getpoint (ssg-text "vfc-prompt-startpunkt-aus"))) (if (null startpunkt-xy) (setq startpunkt-xy '(0 0 0))) (setq eingabe (vfs-dialog-eingabe-basis nil)) (if (null eingabe) (princ (ssg-text "vfc-vorgang-abgebrochen")) (progn (vf-basis-eingabe-entpacken eingabe) (setq startpunkt (list (car startpunkt-xy) (cadr startpunkt-xy) z-start)) ;; Gewaehlte Dimension (2D/3D) + Motorseite fuer den Bau erzwingen, danach zurueck. (setq *ssg-ils-dim* dim) (setq *vf-motorseite* motorseite) (vf-dialog-berechnen-einfuegen typ vf-nummer deltaL deltaH richtung startpunkt hz seite nil geruest-einzelmodul geruest-typ horizontal-p) (setq *ssg-ils-dim* nil) (setq *vf-motorseite* nil) ) ) ) ;; ============================================================ ;; TEIL 11: UNTERMODULE LADEN ;; ============================================================ ;; Guard: Untermodule nur beim ersten Laden von vf_core.lsp ausfuehren. (if (not *ssg-vf-submodule-loaded*) (progn (princ "\n>>> VARIOFOERDERER v26.0 geladen <<<") ;; Lisp-Pfad zentral ueber ssg_core (ssg_core ist hier bereits geladen). (setq *vf-lisp-pfad* (ssg-lisp-verzeichnis)) (if *vf-lisp-pfad* (progn (load (strcat *vf-lisp-pfad* "/vf_konstanten.lsp")) (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 (ssg-text "vfc-warnung-untermodule-nicht-geladen")) ) ) ) ;; ============================================================ ;; TEIL 12: DISPATCHER c:VarioFoerderer ;; ============================================================ (defun c:VarioFoerderer ( / typ-eintr anlage-typ berechne-fn einfuege-fn eingabe deltaL deltaH richtung startpunkt seite hz eingabe-modus ergebnis ergebnis-liste gueltige-winkel horizontal-info best-winkel L_GF L_GF1 L_GF2 L_VF idx eintrag wahl antwort verteilung-modus n-scanner vf-nummer hoehe-von hoehe-bis lastEnt staustrecke-basis vf-insert) (princ "\n=========================================") (princ (ssg-text "vfc-banner-titel")) (princ "\n=========================================") ;; 1. Typ aus Registry waehlen (if (null *vf-typ-registry*) (progn (alert (ssg-text "vfc-alert-keine-typen")) (exit) ) ) (if (and *vf-vorauswahl-typ* (assoc *vf-vorauswahl-typ* *vf-typ-registry*)) (progn (setq typ-eintr (assoc *vf-vorauswahl-typ* *vf-typ-registry*)) (princ (ssg-textf "vfc-typ-vorgewaehlt" (list *vf-vorauswahl-typ*))) (setq *vf-vorauswahl-typ* nil) ) (progn (setq *vf-vorauswahl-typ* nil) (princ (ssg-text "vfc-anlagen-typ-header")) (setq idx 1) (foreach eintr *vf-typ-registry* (princ (ssg-textf "vfc-typ-option" (list idx (nth 3 eintr)))) (setq idx (1+ idx)) ) (setq wahl (getint (ssg-textf "vfc-prompt-wahl-1-n" (list (length *vf-typ-registry*))))) (if (or (null wahl) (< wahl 1) (> wahl (length *vf-typ-registry*))) (setq wahl 1)) (setq typ-eintr (nth (1- wahl) *vf-typ-registry*)) ) ) (setq anlage-typ (nth 0 typ-eintr)) (setq berechne-fn (nth 1 typ-eintr)) (setq einfuege-fn (nth 2 typ-eintr)) (princ (ssg-textf "vfc-typ-gewaehlt" (list 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 (princ (ssg-text "vfc-linienzug-modus-header")) (princ (ssg-text "vfc-linienzug-modus-1")) (princ (ssg-text "vfc-linienzug-modus-2")) (princ (ssg-text "vfc-linienzug-modus-3")) (setq wahl (getint (ssg-text "prompt-wahl-1-2-3"))) ;; Konsolen-Nummer -> interne Funktion: ;; 2 = fertiger Ziel-Hoehe-Modus (intern vf-linienzug-modus2) ;; 3 = alter Vorwaerts-Nachbau (intern vf-linienzug-modus3, wird umgebaut) ;; 1/Default = frische manuelle Eingabe (Journal vorher zuruecksetzen). Ein ;; abgebrochener Bau bleibt als VF_n-Block stehen und wird per Doppelklick ;; fortgesetzt/editiert (vfl-edit-ent), kein separater Menuepunkt noetig. (cond ((= wahl 2) (vf-linienzug-modus2)) ((= wahl 3) (vf-linienzug-modus3)) (t (vfl-journal-reset) (vf-linienzug-modus))) (exit) ) ) ;; 2. VF-Nummer automatisch ermitteln (setq vf-nummer (vf-next-number)) (princ (ssg-textf "vfc-naechste-vf-nummer" (list (itoa vf-nummer)))) ;; 2b. Standard- ODER Etage-Typ + Werteingabe -> DCL-Dialog ;; (dcl/variofoerderer.dcl; Standard: vfs-standard-dialog-ablauf in ;; vf_standard.lsp, Etage: vfe-etage-dialog-ablauf in vf_etage.lsp - beide ;; nutzen dieselben Dialoge vfs-dialog-eingabe-basis/-winkel-verteilung). ;; 3D-Linie-Eingabe und der Typ Linienzug bleiben unveraendert interaktiv ;; (fallen unten in den Konsolen-Ablauf, der die Eingabemodus-Frage erneut ;; stellt - siehe vf-eingabe-abfragen). (if (member anlage-typ '("standard" "etage")) (progn (princ (ssg-text "vfc-eingabemodus-header")) (princ (ssg-text "vfc-eingabemodus-3dlinie")) (princ (ssg-text "vfc-eingabemodus-werte")) (setq antwort (getstring (ssg-text "vfc-prompt-wahl-1-2-ohne-default"))) (if (= antwort "2") (progn (if (= anlage-typ "etage") (vfe-etage-dialog-ablauf vf-nummer) (vfs-standard-dialog-ablauf vf-nummer)) (exit) ) ) ) ) ;; 3. Gemeinsame Eingabe (deltaL, deltaH, richtung, startpunkt) - richtung ;; steht bei beiden Eingabemodi (Werteingabe UND 3D-Linie) bereits fest. (setq eingabe (vf-eingabe-abfragen)) (setq deltaL (nth 0 eingabe) deltaH (nth 1 eingabe) richtung (nth 2 eingabe) startpunkt (nth 3 eingabe) hz (nth 4 eingabe) eingabe-modus (nth 5 eingabe)) ;; 4. Seite-Auswahl (vor Berechnung, weil Etage-Typ sie fuer Geometrie braucht) (princ (ssg-text "vfc-seite-header")) (princ (ssg-text "vfc-seite-rechts")) (princ (ssg-text "vfc-seite-links")) (setq antwort (getstring (ssg-text "prompt-wahl-1-2"))) (if (= antwort "2") (setq seite "links") (setq seite "rechts")) (princ (ssg-textf "vfc-seite-gewaehlt" (list seite))) ;; 5. Typ-spezifische Berechnung (init + Winkelsuche) (setq ergebnis (apply berechne-fn (list deltaL deltaH richtung seite))) (setq ergebnis-liste (cadddr ergebnis)) ;; 5b. Horizontale Zwischenstrecke als Zusatzoption: geometrisch NUR bei ;; "Ab" ueberhaupt erreichbar (die festen 3-Grad-Elemente wirken immer ;; absenkend) - "Auf" wird deshalb gar nicht erst geprueft. Gilt einheitlich ;; fuer Werteingabe UND 3D-Linie, da richtung hier in jedem Fall feststeht. ;; Bibliothek ist durch den berechne-fn-Aufruf oben bereits initialisiert. (if (and (= anlage-typ "standard") (= richtung "Ab")) (progn (setq horizontal-info (berechne-horizontale-mitte deltaL deltaH "Ab")) (if (and horizontal-info (caddr horizontal-info)) (progn (setq ergebnis-liste (append ergebnis-liste (list (list 0 (car horizontal-info) (cadr horizontal-info) T)))) (princ (ssg-textf "vfc-hinweis-horizontal-moeglich" (list (chr 176) (chr 916)))) ) (setq horizontal-info nil) ) ) ) ;; Konfiguration (nach ssg_core-Init verfuegbar). (setq staustrecke-basis (ssg-cfg-or "vario" "staustrecke_basis" 1000)) ;; 7. Gueltige Winkel ermitteln (setq gueltige-winkel (mapcar 'car (vl-remove-if-not (function (lambda (e) (and (nth 3 e) (numberp (nth 1 e)) (numberp (nth 2 e)) (>= (nth 1 e) 0) (>= (nth 2 e) 0)))) ergebnis-liste))) (if (null gueltige-winkel) (progn (alert (ssg-textf "vfc-alert-kein-winkel" (list (chr 916) (rtos deltaL 2 0) (rtos deltaH 2 0) richtung))) (exit) ) ) ;; 8. Winkelauswahl (automatisch oder interaktiv) ;; Sentinel-Winkel 0 = horizontale Zwischenstrecke (Schritt 6/11 bei 0 ;; Grad, Boegen fest auf_3/ab_3) - siehe berechne-horizontale-mitte. (if (= (length gueltige-winkel) 1) (progn (setq best-winkel (car gueltige-winkel)) (setq eintrag (assoc best-winkel ergebnis-liste)) (setq L_GF (nth 1 eintrag) L_VF (nth 2 eintrag)) (if (= best-winkel 0) (princ (ssg-text "vfc-einzige-variante-horizontal")) (princ (ssg-textf "vfc-einziger-winkel" (list (itoa best-winkel) (chr 176)))) ) ) (progn (princ (ssg-text "vfc-mehrere-winkel-header")) (setq idx 1) (foreach w gueltige-winkel (setq eintrag (assoc w ergebnis-liste)) (if (= w 0) (princ (ssg-textf "vfc-winkel-option-horizontal" (list idx (chr 176) (rtos (nth 1 eintrag) 2 1) (rtos (nth 2 eintrag) 2 1)))) (princ (ssg-textf "vfc-winkel-option" (list idx (itoa w) (chr 176) (rtos (nth 1 eintrag) 2 1) (rtos (nth 2 eintrag) 2 1)))) ) (setq idx (1+ idx)) ) (setq wahl (getint (ssg-textf "vfc-prompt-winkel-waehlen" (list (length gueltige-winkel))))) (if (or (null wahl) (< wahl 1) (> wahl (length gueltige-winkel))) (setq wahl 1)) (setq best-winkel (nth (1- wahl) gueltige-winkel)) (setq eintrag (assoc best-winkel ergebnis-liste)) (setq L_GF (nth 1 eintrag) L_VF (nth 2 eintrag)) ) ) (princ "\n\n=========================================") (if (= best-winkel 0) (princ (ssg-textf "vfc-zus-horizontal" (list (rtos L_GF 2 2) (rtos L_VF 2 2)))) (princ (ssg-textf "vfc-zus-winkel" (list (itoa best-winkel) (chr 176) (rtos L_GF 2 2) (rtos L_VF 2 2)))) ) (princ "\n=========================================") ;; 9. L_GF Verteilung (princ (ssg-text "vfc-gf-verteilung-header")) (princ (ssg-text "vfc-gf-verteilung-1")) (princ (ssg-text "vfc-gf-verteilung-2")) (princ (ssg-text "vfc-gf-verteilung-3")) (princ (ssg-text "vfc-gf-verteilung-4")) (setq verteilung-modus (getstring (ssg-text "prompt-wahl-1-2-3-4"))) (if (= verteilung-modus "") (setq verteilung-modus "1")) (cond ((= verteilung-modus "1") (setq L_GF1 (/ L_GF 2.0) L_GF2 (/ L_GF 2.0))) ((= verteilung-modus "2") (setq L_GF1 (float staustrecke-basis)) (setq L_GF2 (max 0.0 (- L_GF (float staustrecke-basis))))) ((= verteilung-modus "3") (setq L_GF2 (float staustrecke-basis)) (setq L_GF1 (max 0.0 (- L_GF (float staustrecke-basis))))) ((= verteilung-modus "4") (setq L_GF1 (getreal (ssg-textf "vfc-prompt-lgf1-vorne" (list (rtos (/ L_GF 2.0) 2 2))))) (if (null L_GF1) (setq L_GF1 (/ L_GF 2.0))) (setq L_GF2 (max 0.0 (- L_GF L_GF1))) (princ (ssg-textf "vfc-lgf2-hinten" (list (rtos L_GF2 2 2))))) (t (setq L_GF1 (/ L_GF 2.0) L_GF2 (/ L_GF 2.0))) ) (princ (ssg-textf "vfc-zus-lgf" (list (rtos L_GF1 2 2) (rtos L_GF2 2 2)))) ;; Scanner-Anzahl wird nicht mehr abgefragt (Attribut ANZAHL_SCANNER bleibt 0). (setq n-scanner 0) ;; 11. Bestaetigung (princ (ssg-text "vfc-einfuegen-frage")) (princ (ssg-text "vfc-einfuegen-optionen")) (setq antwort (getstring (ssg-text "vfc-prompt-wahl-1-2-ohne-default"))) (if (= antwort "1") (progn ;; Hoehenangaben fuer Attribute und Beschriftung (setq hoehe-von (caddr startpunkt)) (setq hoehe-bis (cond ((= richtung "Auf") (+ hoehe-von deltaH)) ((= richtung "Ab") (- hoehe-von deltaH)) (t hoehe-von))) ;; lastEnt VOR der Einfuegung merken (setq lastEnt (vf-lastent-ohne-attribute)) ;; Typ-spezifische Einfuegung (apply einfuege-fn (list deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz)) ;; Gemeinsame VF_*-Block-Erstellung ;; Geruest fuer Einzelmodul/Geruestoption sind in diesem interaktiven ;; Konsolen-Ablauf (Etage/Linienzug) nicht abfragbar - bleiben auf ;; Schema-Default (analog Kreisel: nur ueber Dialog editierbar). (setq vf-insert (vf-block-erstellen anlage-typ seite vf-nummer n-scanner hoehe-von hoehe-bis deltaH deltaL L_VF L_GF1 L_GF2 richtung best-winkel startpunkt lastEnt hz "0" "Schoenenberger Geruest")) ;; Standard- UND Etage-Bloecke aus dem interaktiven Konsolen-Ablauf mit der ;; SSG_VF_EDIT-XDATA markieren - sie sind (wie die Dialog-Variante) aus ;; Attributen + hz verlustfrei rekonstruierbar, also per Doppelklick ;; (c:VARIOFOERDERER_EDIT) editierbar. Linienzug bleibt unmarkiert. (if (and vf-insert (member anlage-typ '("standard" "etage"))) (vf-edit-xdata-schreiben vf-insert anlage-typ hz)) (princ "\n=========================================") ) (princ (ssg-text "vfc-vorgang-abgebrochen")) ) nil ) ;; ============================================================ ;; START & ALIASES ;; ============================================================ (defun c:Foerderanlage () (c:VarioFoerderer)) (defun c:EtageVarioFoerderer () (setq *vf-vorauswahl-typ* "etage") (c:VarioFoerderer) ) ;; ssg-ensure-Flags fuer beide Wrapper-Dateien setzen, ;; damit ein zweiter Ladeaufruf uebersprungen wird. (setq *ssg-VarioFoerderer-loaded* t) (setq *ssg-EtageVarioFoerderer-loaded* t) (princ "\n>>> Befehle: VarioFoerderer, Foerderanlage, EtageVarioFoerderer") (princ "\n Typen:") (setq *vf-typ-idx* 1) (foreach eintr *vf-typ-registry* (princ (strcat "\n " (itoa *vf-typ-idx*) ". " (car eintr) " - " (nth 3 eintr))) (setq *vf-typ-idx* (1+ *vf-typ-idx*)) ) (if block-pfad (princ (strcat "\n Block-Pfad: " block-pfad))) (princ "\n=========================================") (princ)