;; ============================================================ ;; 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-ziel-punkt* nil ; Soll-ES-Punkt fuer Option-3-Ist-Ziel-Report *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 (or (not *lib-initialized*) (null bogen-auf)) (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. ;; Punkt-Abfrage mit FESTER 2-Arity (Basispunkt + Prompt). Verhaltensneutraler ;; Wrapper um das variadische Built-in getpoint: mit Basispunkt (base) wird die ;; 2-Argument-Form genutzt (Gummiband), ohne (base=nil) die reine Prompt-Form. ;; Zweck: ein Testmock kann diese feste Signatur ersetzen (getpoint selbst kann ;; als defun nicht 1- UND 2-argumentig gemockt werden). (defun vfl-getpoint (base prompt) (if base (getpoint base prompt) (getpoint prompt))) ;; Rueckgabe: (deltaL hz) oder nil bei Abbruch. (defun vfl-neue-linie-messen (p-akt hz-vorgabe / p2 rad ux uy) (setq p2 (vfl-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. ;; Neigung des Frames aus der xu-Richtung ablesen: T => (nahezu) flach (0 Grad), ;; nil => auf 3-Grad-Basis. Damit wird der Uebergang auf_3/ab_3 nur dann gesetzt, ;; wenn wirklich ein Neigungswechsel noetig ist. (defun vfl-frame-flach-p (frame) (< (abs (cadr (frame->hz-winkel frame))) 1.5)) ;; Separator (300 mm) HORIZONTAL (0 Grad) an einen Punkt anfuegen - fuer das ;; horizontale Stueck, wo der Separator in der 0-Grad-Ebene liegt (NICHT auf der ;; 3-Grad-Basis wie vfl-insert-separator). Rueckgabe: Endpunkt. (defun vfl-sep-hz (pt hz / ep) (princ "\n [horizontal] Separator (300 mm, 0 Grad)") (setq ep (gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm" pt hz 0 300 0)) (setq *vfl-acc-separator* (1+ *vfl-acc-separator*)) ep) ;; Uebergang zurueck auf die 3-Grad-Basis, FALLS der Frame gerade flach (0 Grad) ;; ist: fuegt einen Vario_Bogen_ab_3 ein. Wird vor jedem 3-Grad-Element ;; (gewinkeltes VF, Motorstation, ES) aufgerufen, damit der ab_3-Uebergang erst ;; DANN kommt, wenn er wirklich gebraucht wird (die flache Zone bleibt sonst ;; flach). Ist der Frame schon auf 3-Grad-Basis, bleibt er unveraendert. (defun vfl-nach-3grad (frame / hz m pt) (if (vfl-frame-flach-p frame) (progn (setq hz (car (frame->hz-winkel frame))) (setq m (get-bogen-mass bogen-ab 3)) (princ "\n\nVario_Bogen_ab_3 (Uebergang horizontal -> 3 Grad)") (setq pt (insert-rotated-block-with-ks "Vario_Bogen_ab_3" (car frame) 0 (car m) (caddr m) hz)) (vfl-frame-3grad pt hz)) frame)) ;; Horizontales Sub-Segment bauen. Die flache Zone (0 Grad) wird NICHT mehr ;; automatisch mit ab_3 auf die 3-Grad-Basis zurueckgefuehrt - das Stueck ENDET ;; FLACH. Der ab_3-Uebergang kommt erst, wenn ein 3-Grad-Element folgt ;; (vfl-nach-3grad). Der auf_3-Eintritt wird nur gesetzt, wenn der Frame noch ;; NICHT flach ist (sonst bleibt die laufende flache Zone erhalten). Separatoren ;; VOR/NACH liegen in der 0-Grad-Ebene. Rueckgabe: neuer Frame (flach, 0 Grad). (defun vfl-baue-horizontal-koerper (frame hz dL / pt m1 sep-vor) (setq pt (car frame)) (princ "\n\nZusaetzlichen Separator VOR dem horizontalen Stueck?") (princ "\n 1 - Ja\n 2 - Nein") (setq sep-vor (= (getstring "\nIhre Wahl (1/2) [2]: ") "1")) ;; auf_3-Eintritt nur, wenn noch NICHT flach (sonst flache Zone fortsetzen) (if (not (vfl-frame-flach-p frame)) (progn (setq m1 (get-bogen-mass bogen-auf 3)) (princ "\n\nVario_Bogen_auf_3 (Uebergang 3 Grad -> horizontal)") (setq pt (insert-rotated-block-with-ks "Vario_Bogen_auf_3" pt (ssg-cfg-or "vario" "gefaelle_winkel" 3) (car m1) (caddr m1) hz)))) ;; optionaler Separator VOR - in der horizontalen Ebene (0 Grad) (if sep-vor (setq pt (vfl-sep-hz pt hz))) ;; horizontale Zwischenstrecke (0 Grad) (princ (strcat "\n\nHorizontale Zwischenstrecke (0 Grad, L=" (rtos dL 2 2) " mm)")) (setq pt (insert-inclined-scaled-block "Staustrecke_SP_1000_mm" pt dL 0 hz)) (vfl-acc-vf-seg "horizontal" 0 dL) ;; optionaler Separator NACH - ebenfalls horizontal (0 Grad) (princ "\n\nZusaetzlichen Separator NACH dem horizontalen Stueck?") (princ "\n 1 - Ja\n 2 - Nein") (if (= (getstring "\nIhre Wahl (1/2) [2]: ") "1") (setq pt (vfl-sep-hz pt hz))) ;; KEIN ab_3 mehr -> das Stueck ENDET FLACH (0 Grad) (make-frame-from-dir pt (hz-winkel->xu hz 0.0)) ) ;; ============================================================ ;; OPTION 3: KETTENENDE AUS LAUFENDER VF-EINHEIT (ein Motor) ;; ============================================================ ;; Zerlegt den verbleibenden GERADEN Lauf bis zum Ziel-ES mit der bewaehrten ;; STANDARD-Logik (wie aussen), aber fuer den Mid-Body-Fall: ;; - Der Einlauf (AS + GF1 + Separator + Umlenk) ist bereits gebaut und wird ;; NICHT beruecksichtigt -> aus-dx/aus-dz = 0. ;; - Fest vor dem ES stehen nur Motor(500) + Auslauf-Separator(300) ;; -> FESTE_HORIZONTAL = 800. ;; Ergebnis der Standard-Logik (GF2 = GF, VARIIERT mit dem Winkel): ;; steiler als 3 Grad / steigend -> gewinkeltes VF + GF2 (Winkeltabelle), ;; flacher als 3 Grad -> horizontales VF + GF2 (Rest-Laenge ueber ;; das horizontale Stueck ausgeglichen). ;; Die Hoehe kommt also aus VF-Winkel/horizontal + GF2, die ueberschuessige ;; Laenge aus dem horizontalen VF - genau die zweistufige Logik. ;; Rueckgabe: (typ winkel L_GF2 L_VF) - typ "GF"/"VF"/nil (wie ;; vfl-segment-entscheidung; L_GF2 varriert, Mindestwert siehe vfl-body-abschluss). (defun vfl-body-zerlegung (deltaL deltaH richtung / save-ausdx save-ausdz save-feste winkel-natuerlich wahl horizontal-info res) (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) ;; Budget-Globals temporaer auf den Mid-Body-Fall umbiegen (mit Restore). (setq save-ausdx aus-dx save-ausdz aus-dz save-feste FESTE_HORIZONTAL) (setq aus-dx 0.0 aus-dz 0.0 FESTE_HORIZONTAL 800.0) (setq res (cond ;; kuerzer als Motor(500)+Separator(300): kein Abschluss baubar ((< deltaL 800.0) (list nil nil nil nil)) ;; praktisch flach: horizontales VF + GF2 ((< deltaH 1.0) (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)) (list nil nil nil nil))) ;; steigend: nur gewinkeltes VF + GF2 moeglich ((= richtung "Auf") (setq wahl (vfl-waehle-winkel (nth 3 (berechne-alle-winkel deltaL deltaH "Auf" 800.0)))) (if wahl (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) (list nil nil nil nil))) ;; fallend: natuerlichen Neigungswinkel gegen die 3-Grad-Eigenneigung pruefen (t (setq winkel-natuerlich (* (atan (/ deltaH deltaL)) (/ 180.0 pi))) (cond ;; ~3 Grad -> reine GF2 (kein Koerper, GF2 traegt Laenge + 3-Grad-Absenkung) ((<= (abs (- winkel-natuerlich 3.0)) *vfl-gf-winkel-toleranz*) (list "GF" 3.0 nil nil)) ;; steiler als 3 Grad -> Stufe 1: gewinkeltes VF + GF2 (GF2 variiert mit Winkel) ((> winkel-natuerlich 3.0) (setq wahl (vfl-waehle-winkel (nth 3 (berechne-alle-winkel deltaL deltaH "Ab" 800.0)))) (if wahl (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) (list nil nil nil nil))) ;; flacher als 3 Grad -> Stufe 2: horizontales VF + GF2 (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-waehle-winkel (nth 3 (berechne-alle-winkel deltaL deltaH "Ab" 800.0)))) (if wahl (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) (list nil nil nil nil))))))))) ;; Budget-Globals zuruecksetzen (setq aus-dx save-ausdx aus-dz save-ausdz FESTE_HORIZONTAL save-feste) res) ;; Fragt Zielpunkt (XY) + Zielhoehe (Z) ab, zerlegt den Rest (vfl-body- ;; zerlegung, Standard-Logik) und baut den Koerper VOR dem Motor: gewinkeltes ;; VF (winkel>0) oder horizontales VF (winkel=0). GF2 (variiert mit dem Winkel, ;; Mindestwert *vfl-gf-min-laenge*=400) sitzt hinter dem Motor und wird als ;; Laenge zurueckgegeben (der Aufrufer setzt sie beim Auslauf ein und schliesst ;; mit Motor -> GF2 -> Separator -> ES ab). Speichert den Soll-Zielpunkt in ;; *vfl-ziel-punkt* fuer den Ist-Ziel-Report. ;; Rueckgabe: (frame anzahl-koerper letzt-hz gf2-laenge) oder nil. (defun vfl-body-abschluss (frame letzt-hz p-umlenk / linie-mess dL hzn hn dH richtn rad3 dec typ w lgf lvf gf2 cnt p0 tx ty) ;; Der Abschluss-Koerper (vfs-vf-koerper) und der Motor gehen von 3-Grad-Basis ;; aus - eine gerade laufende flache Zone hier mit ab_3 abschliessen. (setq frame (vfl-nach-3grad frame)) (setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz)) (if (null linie-mess) (progn (princ "\n Abgebrochen - kein Zielpunkt gewaehlt.") nil) (progn (setq dL (car linie-mess) hzn (cadr linie-mess)) (setq hn (getreal (strcat "\nHoehe (Z) des Kettenendes (ES-Element) [" (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 rad3 (* (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) (/ pi 180.0))) (princ (strcat "\n>>> Kettenende-Modus: Ziel dL=" (rtos dL 2 0) " mm, dH=" (rtos dH 2 0) " mm (" richtn "). GF2 variiert mit dem Winkel.")) (setq dec (vfl-body-zerlegung dL dH richtn)) (setq typ (nth 0 dec) w (nth 1 dec) lgf (nth 2 dec) lvf (nth 3 dec)) (if (null typ) (progn (princ (strcat "\n FEHLER: Rest nicht baubar (zu steil/zu kurz fuer" " Schwanz Motor+GF2+Separator+ES). Bitte weiter zeichnen oder flacher planen.")) nil) (progn ;; Soll-ES-Punkt fuer den Ist-Ziel-Report merken (Startpunkt + ;; projizierter Laenge entlang der Fahrtrichtung). (setq p0 (car frame)) (setq tx (+ (car p0) (* dL (cos (* hzn (/ pi 180.0)))))) (setq ty (+ (cadr p0) (* dL (sin (* hzn (/ pi 180.0)))))) (setq *vfl-ziel-punkt* (list tx ty hn)) (setq cnt 0) ;; Koerper VOR dem Motor: winkel 0 = horizontales VF, sonst gewinkeltes VF. ;; typ "GF" -> kein Koerper (Rest laeuft komplett ueber GF2). (if (and (= typ "VF") lvf (> lvf 1.0)) (progn (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn w lvf hzn) hzn)) (vfl-acc-vf-seg richtn w lvf) (setq cnt (1+ cnt)))) (setq letzt-hz hzn) (vfl-20m-check p-umlenk (car frame)) ;; GF2-Laenge (hinter dem Motor): aus der Zerlegung (variiert mit Winkel), ;; sonst (typ "GF") aus der 3-Grad-Geometrie abgeleitet. Mindestwert 400. (setq gf2 (cond ((and lgf (> lgf 0.0)) lgf) ((= typ "GF") (max 0.0 (- (/ (- dL (abs (if ein-dx ein-dx 576.0))) (cos rad3)) 800.0))) (t 0.0))) (if (and (> gf2 0.0) (< gf2 *vfl-gf-min-laenge*)) (progn (princ (strcat "\n HINWEIS: berechnetes GF2=" (rtos gf2 2 0) " mm < Minimum " (rtos *vfl-gf-min-laenge* 2 0) " mm -> auf Minimum angehoben (ES endet dadurch etwas weiter; siehe Ist-Ziel-Report).")) (setq gf2 *vfl-gf-min-laenge*))) (list frame cnt hzn gf2) ) ) ) ) ) ;; Rueckgabe: (frame anzahl-koerper ziel-ende) (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 ziel-ende ziel-gf2 res3 gf2-eff) ;; 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 --- ;; winkel1=0 => horizontaler Anfang (Option 3): mit Separator-vor/nach-Abfrage. (if (= (fix winkel1) 0) (setq frame (vfl-baue-horizontal-koerper frame hz1 L_VF1)) (progn (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtung1 winkel1 L_VF1 hz1) hz1)) (vfl-acc-vf-seg richtung1 winkel1 L_VF1) ) ) (setq vf-count 1 letzt-hz hz1) (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 (nur Motorstation setzen)") (princ "\n 2 - Nein (weiterbauen)") (princ "\n 3 - Ja (Motorstation setzen, Kettenende definieren)") (setq antwort (getstring "\nIhre Wahl (1/2/3) [2]: ")) (if (= antwort "1") (setq fertig t) (if (= antwort "3") ;; --- Option 3: Kettenende exakt am Zielpunkt (nur EIN Motor) --- (progn (setq res3 (vfl-body-abschluss frame letzt-hz p-umlenk)) (if res3 (setq frame (nth 0 res3) vf-count (+ vf-count (nth 1 res3)) letzt-hz (nth 2 res3) ziel-gf2 (nth 3 res3) ziel-ende t 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 ;; inkl. Separator-vor/nach-Abfrage (setq frame (vfl-baue-horizontal-koerper frame hzn dL)) (setq vf-count (1+ vf-count) letzt-hz hzn) (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 ;; gewinkeltes VF braucht 3-Grad-Basis -> flache Zone ggf. mit ab_3 beenden (setq frame (vfl-nach-3grad frame)) (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"))) ;; Motorstation braucht 3-Grad-Basis: falls die Kette gerade flach endet ;; (horizontales Stueck ohne ab_3), hier den ab_3-Uebergang nachholen. (setq frame (vfl-nach-3grad frame)) ;; Auslauf-GF2: im Kettenende-Modus (Option 3) neu berechnet (ziel-gf2), ;; sonst aus der GF-Verteilungs-Frage (L_GF2-bau). (setq gf2-eff (if ziel-ende ziel-gf2 L_GF2-bau)) (setq frame (vfl-frame-3grad (vfs-vf-exit (car frame) gf2-eff letzt-hz nil) letzt-hz)) (if (> gf2-eff 0.1) (vfl-acc-gf-seg gf2-eff (ssg-cfg-or "vario" "gefaelle_winkel" 3))) (list frame vf-count ziel-ende) ) ;; 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 - 90 Grad\n 2 - 60 Grad\n 3 - 30 Grad") (setq antwort (getint "\nIhre Wahl (1/2/3) [1]: ")) (if (null antwort) (setq antwort 1)) (setq kwinkel (cond ((= antwort 1) 90) ((= antwort 2) 60) (t 30))) (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_mm" (itoa (fix hoehe-von))) (cons "HOEHE_BIS_mm" (itoa (fix hoehe-bis))) (cons "DELTA_H_mm" (itoa (fix (abs (- hoehe-bis hoehe-von))))) (cons "DELTA_L_mm" (itoa (fix delta-l-total))) (cons "TYP" typ-str) (cons "SEITE_AS" as-seite) (cons "SEITE_ES" es-seite) ;; ANZAHL_GF = alle GF-Stuecke: eigenstaendige GF-Segmente UND die GF1/GF2 ;; jeder VF-Einheit (alle ueber vfl-acc-gf-seg in *vfl-acc-lgf* gesammelt). (cons "ANZAHL_GF" (itoa (length *vfl-acc-lgf*))) (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 ) ;; Baut eine komplette VarioFoerderer-Einheit und schliesst sie ab: ;; GF-Verteilung fragen -> vfl-vf-einheit (Umlenk..Motor, mehrsegmentig) ;; -> Kettenende? Ja: Separator+ES (Ende); Nein: optionaler Zwischen-Separator. ;; winkel1=0 => horizontaler Anfangs-Koerper (Option "Neue horizontal VF"). ;; auto-ende: T => keine Kettenende-Frage, es wird direkt Separator + ES ;; gesetzt (Option "Neue Linie BIS Kettenende"). ;; Rueckgabe: (frame anzahl-koerper ende-flag es-seite-oder-nil). (defun vfl-vf-einheit-abschluss (frame hz richtung winkel L_GF L_VF auto-ende / antwort res es-s ende) ;; GF-Verteilung: halbe Staustrecke am Ausgang (GF2) oder alles am Einlauf. (princ "\n\nStaustrecke (GF) - Verteilung waehlen:") (princ "\n 1 - 1/2GF am Ausgang (GF2), 1/2GF am Einlauf (GF1)") (princ "\n 2 - Gesamte Staustrecke am Einlauf (GF1), kein GF2") (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) (setq res (vfl-vf-einheit frame hz richtung winkel L_GF L_VF (= antwort "1"))) (setq frame (nth 0 res)) ;; Kettenende? Bei auto-ende (Kettenende-Modus) ohne Frage direkt ES setzen. ;; Sonst fragen - zwischen einem AS und ES koennen mehrere Foerderer liegen. ;; auto-ende (Option 4) ODER ziel-ende aus der laufenden Einheit (Option 3): ;; ohne Frage direkt Separator + ES setzen. (if (or auto-ende (nth 2 res)) (setq antwort "1") (progn (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-s (vfl-frage-es-seite)) (setq frame (vfl-insert-es-element "VF" frame (car (frame->hz-winkel frame)) 0.0 (caddr (car frame)) es-s)) (setq ende t) ;; Ist-Ziel-Report (Option 3): Soll-ES (aus vfl-body-abschluss) vs. Ist-ES. (if *vfl-ziel-punkt* (progn (princ "\n\n>>> IST-ZIEL-VERGLEICH (ES-Element):") (princ (strcat "\n Soll: X=" (rtos (car *vfl-ziel-punkt*) 2 1) " Y=" (rtos (cadr *vfl-ziel-punkt*) 2 1) " Z=" (rtos (caddr *vfl-ziel-punkt*) 2 1))) (princ (strcat "\n Ist : X=" (rtos (car (car frame)) 2 1) " Y=" (rtos (cadr (car frame)) 2 1) " Z=" (rtos (caddr (car frame)) 2 1))) (princ (strcat "\n Abweichung: dX=" (rtos (- (car (car frame)) (car *vfl-ziel-punkt*)) 2 1) " dY=" (rtos (- (cadr (car frame)) (cadr *vfl-ziel-punkt*)) 2 1) " dZ=" (rtos (- (caddr (car frame)) (caddr *vfl-ziel-punkt*)) 2 1) " mm")) (setq *vfl-ziel-punkt* nil) ) ) ) (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") (setq frame (vfl-insert-separator frame))) ) ) (list frame (nth 1 res) ende es-s) ) ;; ============================================================ ;; 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 linie-ende-modus rad3 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 (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) (setq startpunkt (vfl-getpoint nil "\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). ;; Menue immer anzeigen; GF-Bogen nur, wenn bereits ein Frame existiert ;; (am Kettenanfang gibt es keinen Vorgaenger-Rahmen fuer einen Bogen). ;; Lueckenlose Nummerierung: GF-Bogen (nur mit Frame) ist immer Option 1; ;; ohne Frame ruecken die uebrigen Optionen auf 1..3 auf. (princ "\n\nNaechstes Element waehlen:") (setq linie-ende-modus nil) (if frame (progn (princ "\n 1 - GF-Bogen (horizontale Kurve)") (princ "\n 2 - Neue Linie (GF, Ab/Auf VF)") (princ "\n 3 - Neue VF mit horizontalem Anfang (Horizontal-Stueck)") (princ "\n 4 - Neue Linie BIS Kettenende (danach Separator + ES-Element)") (setq antwort (getstring "\nIhre Wahl (1-4) [2]: ")) (setq wahl (cond ((= antwort "1") "GF-Bogen") ((= antwort "3") "Horizontal-VF") ((= antwort "4") (setq linie-ende-modus t) "Linie") (t "Linie"))) ; "2" oder leer ) (progn (princ "\n 1 - Neue Linie (GF, Ab/Auf VF)") (princ "\n 2 - Neue VF mit horizontalem Anfang (Horizontal-Stueck)") (princ "\n 3 - Neue Linie BIS Kettenende (danach Separator + ES-Element)") (setq antwort (getstring "\nIhre Wahl (1-3) [1]: ")) (setq wahl (cond ((= antwort "2") "Horizontal-VF") ((= antwort "3") (setq linie-ende-modus t) "Linie") (t "Linie"))) ; "1" oder leer ) ) (cond ((= wahl "GF-Bogen") (setq frame (vfl-insert-gf-bogen frame)) (setq p-aktuell (car frame)) ) ;; --- Neue horizontal VF: VF-Einheit mit horizontalem Anfangs-Koerper --- ((= wahl "Horizontal-VF") (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 1000.0) (princ "\nFEHLER: Horizontaler VF braucht >= 1000 mm (Umlenk + Motor) - laenger zeichnen.") (progn ;; Kettenanfang: AS-Element (flach, wie beim VF) einfuegen (if (null frame) (setq frame (vfl-insert-as-element "VF" p-aktuell hz-neu 0.0 as-seite))) ;; VF-Einheit mit horizontalem ersten Koerper (winkel=0, L_VF=deltaL, ;; L_GF = Mindestlaenge fuer den Einlauf-Anschluss). (setq vf-einheit-res (vfl-vf-einheit-abschluss frame hz-neu "Ab" 0 *vfl-gf-min-laenge* deltaL nil)) (setq frame (nth 0 vf-einheit-res)) (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) (if (nth 2 vf-einheit-res) (setq fertig t)) (if (nth 3 vf-einheit-res) (setq es-seite (nth 3 vf-einheit-res))) (setq letzter-typ "VF") (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). ;; Im Kettenende-Modus (Option 4) wird IMMER nach der Zielhoehe ;; gefragt (Kurzsegment-Automatik hier ueberspringen). (if (and (< deltaL 1000.0) (not linie-ende-modus)) (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)) ;; Kettenende-Modus (Option 4): Der abgefragte Endpunkt (XY+Z) ;; ist das exakte Ende der Kette NACH Separator + ES-Element. ;; Deren Footprint (Separator 300 mm bei ~3 Grad + ES-Element ;; ein-dx/ein-dz) muss vom baubaren GF/VF-Segment reserviert ;; (abgezogen) werden, damit das ES-Element genau am Zielpunkt ;; endet. Naeherung: Separator-Neigung = 3 Grad (Auslauf). (if linie-ende-modus (progn (setq rad3 (* (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) (/ pi 180.0))) (setq deltaL (max 1.0 (- deltaL (* 300.0 (cos rad3)) (abs (if ein-dx ein-dx 0.0))))) (setq deltaH (max 0.0 (- deltaH (* 300.0 (sin rad3)) (abs (if ein-dz ein-dz 0.0))))) (princ (strcat "\n>>> Kettenende-Modus: Footprint fuer Separator + ES" " reserviert -> baubar deltaL=" (rtos deltaL 2 0) " mm, deltaH=" (rtos deltaH 2 0) " mm.")) ) ) (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)) ;; Kettenende-Modus (Option 4): ohne Frage direkt ES setzen. ;; Sonst nachfragen, ob die Kette hier endet. (if linie-ende-modus (setq antwort "1") (progn (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 (Umlenk..Motor, mehrsegmentig) --- ;; linie-ende-modus=T -> direkt Kettenende (Separator + ES). (setq vf-einheit-res (vfl-vf-einheit-abschluss frame hz-neu richtung winkel L_GF L_VF linie-ende-modus)) (setq frame (nth 0 vf-einheit-res)) (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) (if (nth 2 vf-einheit-res) (setq fertig t)) (if (nth 3 vf-einheit-res) (setq es-seite (nth 3 vf-einheit-res))) (setq letzter-typ "VF") (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)