Merge remote-tracking branch 'origin/master'
Konflikt in menu/SSG_LIB.cui (generiert): Stand von origin/master uebernommen, nur der Menuepunkt "Testanlage test_system" (MM_00210) ergaenzt. SSG_LIB.mnu ohne Konflikt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2496,6 +2496,8 @@
|
||||
es-winkel (nth 3 xd)
|
||||
z-start (atof (nth 4 xd))
|
||||
deltaL (atof (nth 5 xd)))
|
||||
;; aktuelle Z statt XDATA (Block evtl. in Z verschoben)
|
||||
(if (caddr startpunkt) (setq z-start (caddr startpunkt)))
|
||||
(setq as-seite (cond ((cdr (assoc "SEITE_AS" attribs))) ("links")))
|
||||
(setq es-seite (cond ((cdr (assoc "SEITE_ES" attribs))) ("links")))
|
||||
;; bestehende Geruest-Einstellungen erhalten
|
||||
@@ -2599,6 +2601,10 @@
|
||||
z-start (atof (cond ((cdr (assoc "HOEHE_VON_mm" attribs))) ("0")))
|
||||
deltaL (atof (cond ((cdr (assoc "DELTA_L_mm" attribs))) ("0"))))
|
||||
)
|
||||
;; Einfuegehoehe = aktuelle Z des Einfuegepunkts (= Kettenstart). XDATA und
|
||||
;; HOEHE_VON_mm sind veraltet, wenn der Block in Z verschoben wurde - OK im
|
||||
;; Dialog baut dann direkt auf der neuen Hoehe neu auf (wie Kreisel/Eckrad).
|
||||
(if (caddr startpunkt) (setq z-start (caddr startpunkt)))
|
||||
|
||||
(setq prefill
|
||||
(list (cons "deltaL" deltaL)
|
||||
@@ -2616,7 +2622,10 @@
|
||||
|
||||
;; AS-Seite + untere Hoehe fuer den optionalen parallelen VF-Bau
|
||||
(setq as-seite (cond ((cdr (assoc "SEITE_AS" attribs))) ("links")))
|
||||
(setq hoehe-bis (atof (cond ((cdr (assoc "HOEHE_BIS_mm" attribs))) ("0"))))
|
||||
;; um dieselbe Z-Verschiebung korrigiert: BIS = Z - (VON_alt - BIS_alt)
|
||||
(setq hoehe-bis (- z-start
|
||||
(- (atof (cond ((cdr (assoc "HOEHE_VON_mm" attribs))) ("0")))
|
||||
(atof (cond ((cdr (assoc "HOEHE_BIS_mm" attribs))) ("0"))))))
|
||||
|
||||
;; 2. Argument (hoehe-vorbelegung) explizit nil: BricsCAD verlangt die exakte
|
||||
;; Argumentzahl (setzt fehlende NICHT automatisch auf nil). Im Edit kommt die
|
||||
|
||||
+606
-15
@@ -193,10 +193,35 @@
|
||||
(dbgreturn result)
|
||||
)
|
||||
|
||||
;; Beschriftungstext (reiner Kern, in tests/test_unit.lsp getestet):
|
||||
;; "KREISEL 1: Pufferkreisel PIN_4000" - mit eigenem Namen
|
||||
;; "KREISEL 1: PIN_4000" - Name leer oder Vorgabe "Kreisel1"
|
||||
(defun kreisel-label-text (nummer name kreiselart abstand)
|
||||
(strcat "KREISEL " (itoa nummer) ": "
|
||||
(if (or (null name) (= name "") (= name (strcat "Kreisel" (itoa nummer))))
|
||||
""
|
||||
(strcat name " "))
|
||||
kreiselart "_" (rtos abstand 2 0))
|
||||
)
|
||||
|
||||
;; Beschriftung in einer BESTEHENDEN Blockdefinition auf textStr setzen.
|
||||
;; draw-module verwendet eine vorhandene Definition KREISEL_n_TYP_ABSTAND
|
||||
;; wieder (Edit/Redraw) - ein geaenderter Name kaeme sonst nie in die
|
||||
;; Beschriftung. Wirkt auf alle Referenzen dieser Definition.
|
||||
(defun kreisel-label-aktualisieren (bname textStr / e ed)
|
||||
(setq e (tblobjname "BLOCK" bname))
|
||||
(while (setq e (entnext e))
|
||||
(setq ed (entget e))
|
||||
(if (and (= (cdr (assoc 0 ed)) "TEXT")
|
||||
(/= (cdr (assoc 1 ed)) textStr))
|
||||
(entmod (subst (cons 1 textStr) (assoc 1 ed) ed))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; Beschriftungstext erzeugen
|
||||
(defun kreisel-make-label (nummer abstand kreiselart / textStr insertPoint radius mitteX mitteY textHoehe lay)
|
||||
;; Text: "KREISEL 1: PIN_4000"
|
||||
(setq textStr (strcat "KREISEL " (itoa nummer) ": " kreiselart "_" (rtos abstand 2 0)))
|
||||
(defun kreisel-make-label (nummer abstand kreiselart name / textStr insertPoint radius mitteX mitteY textHoehe lay)
|
||||
(setq textStr (kreisel-label-text nummer name kreiselart abstand))
|
||||
|
||||
;; Geometrie berechnen
|
||||
(setq radius (/ *kreisel-durchmesser* 2.0))
|
||||
@@ -344,9 +369,11 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; Bezeichnung automatisch setzen: "KreiselN"
|
||||
;; Bezeichnung: eigener Name (KreiselEdit) bleibt erhalten, sonst Vorgabe "KreiselN"
|
||||
(if (member (cdr (assoc "Bezeichnung" attribs)) '(nil ""))
|
||||
(setq attribs (subst (cons "Bezeichnung" (strcat "Kreisel" (itoa kreisel-nummer)))
|
||||
(assoc "Bezeichnung" attribs) attribs))
|
||||
)
|
||||
|
||||
;; --- HÖHE BESTIMMEN ---
|
||||
;; Priorität 1: Aus basePoint (wenn 3D Punkt mit Z > 0)
|
||||
@@ -445,7 +472,8 @@
|
||||
)
|
||||
|
||||
;; BESCHRIFTUNG einfügen (vor Block-Erzeugung, damit sie im Block ist)
|
||||
(kreisel-make-label kreisel-nummer abstand (if is-pin "PIN" "STANDARD") )
|
||||
(kreisel-make-label kreisel-nummer abstand (if is-pin "PIN" "STANDARD")
|
||||
(cdr (assoc "Bezeichnung" attribs)))
|
||||
|
||||
;; Unsichtbare Attribut-Definitionen erzeugen
|
||||
(ssg-attrib-make-defs *kreisel-attrib-defs* 50.0)
|
||||
@@ -472,7 +500,13 @@
|
||||
|
||||
(princ (ssg-textf "kreisel-neue-blockdef" (list bname)))
|
||||
)
|
||||
(progn
|
||||
(princ (ssg-textf "kreisel-bestehende-blockdef" (list bname)))
|
||||
;; Beschriftung der wiederverwendeten Definition an den Namen anpassen
|
||||
(kreisel-label-aktualisieren bname
|
||||
(kreisel-label-text kreisel-nummer (cdr (assoc "Bezeichnung" attribs))
|
||||
(if is-pin "PIN" "STANDARD") abstand))
|
||||
)
|
||||
)
|
||||
|
||||
;; --- Block am Zielpunkt einfuegen (ohne Attribut-Dialog) ---
|
||||
@@ -1071,7 +1105,7 @@
|
||||
;; 4. Hoehe aus Z-Koordinate oder manuell
|
||||
;; --------------------------------------------
|
||||
(defun c:ILS_Eckrad ( / ptTangent ptDir dx dy dist ux uy radius
|
||||
centerPt rotation hoehe
|
||||
centerPt rotation hoehe hoehe-aus-z attribs ergebnis
|
||||
geruest-einzelmodul geruest-typ)
|
||||
(dbgf "c:ILS_Eckrad")
|
||||
|
||||
@@ -1114,30 +1148,205 @@
|
||||
(setq rotation (* (/ 180.0 pi) (atan dy dx)))
|
||||
(dbg 'rotation)
|
||||
|
||||
;; 4. Hoehe bestimmen
|
||||
(if (and (> (length ptTangent) 2) (caddr ptTangent) (> (caddr ptTangent) 0))
|
||||
;; 4. Hoehe vorbelegen: aus Z des Beruehrpunkts, sonst Default
|
||||
(setq hoehe-aus-z (and (> (length ptTangent) 2) (caddr ptTangent) (> (caddr ptTangent) 0)))
|
||||
(if hoehe-aus-z
|
||||
(progn
|
||||
(setq hoehe (caddr ptTangent))
|
||||
(princ (ssg-textf "kreisel-hoehe-aus-3dpunkt" (list (rtos hoehe 2 0))))
|
||||
)
|
||||
(setq hoehe *eckrad-default-hoehe*)
|
||||
)
|
||||
(dbg 'hoehe)
|
||||
|
||||
(if (ssg-gui-p)
|
||||
;; 5a. Dialog: Bezeichnung, Hoehe, Drehung, Drehrichtung, Geruest
|
||||
(progn
|
||||
(setq attribs (ssg-attrib-merge (list (cons "HOEHE" (rtos hoehe 2 0)))
|
||||
*eckrad-attrib-defs*))
|
||||
(setq ergebnis (eckrad-dialog attribs rotation))
|
||||
(if (null ergebnis)
|
||||
(progn (princ (ssg-text "kreisel-abgebrochen")) (dbgreturn nil) (exit))
|
||||
)
|
||||
(setq attribs (car ergebnis)
|
||||
rotation (cdr ergebnis)
|
||||
hoehe (atof (cdr (assoc "HOEHE" attribs))))
|
||||
)
|
||||
;; 5b. Ohne GUI (Tests): Konsolenabfrage wie bisher
|
||||
(progn
|
||||
(if (not hoehe-aus-z)
|
||||
(progn
|
||||
(setq hoehe (getreal (ssg-textf "kreisel-prompt-hoehe-z" (list (rtos *eckrad-default-hoehe* 2 0)))))
|
||||
(if (null hoehe) (setq hoehe *eckrad-default-hoehe*))
|
||||
)
|
||||
)
|
||||
(dbg 'hoehe)
|
||||
|
||||
;; 5. Geruest fuer Einzelmodul + Geruestoption abfragen
|
||||
(setq geruest-einzelmodul (if (ssg-ques (ssg-text "kreisel-eckrad-geruest-frage") T) "1" "0"))
|
||||
(setq geruest-typ (ssg-ask-geruest-typ))
|
||||
(setq attribs (list (cons "GERUEST_EINZELMODUL" geruest-einzelmodul)
|
||||
(cons "GERUEST_TYP" geruest-typ)))
|
||||
)
|
||||
)
|
||||
|
||||
;; 6. Einfuegen (centerPt ist der Kreismittelpunkt)
|
||||
(ils-eckrad-insert (list (car centerPt) (cadr centerPt) hoehe) rotation
|
||||
(list (cons "GERUEST_EINZELMODUL" geruest-einzelmodul)
|
||||
(cons "GERUEST_TYP" geruest-typ)))
|
||||
(ils-eckrad-insert (list (car centerPt) (cadr centerPt) hoehe) rotation attribs)
|
||||
(dbgreturn nil)
|
||||
)
|
||||
|
||||
|
||||
;; --------------------------------------------
|
||||
;; eckrad-dialog-werte: Dialogeingaben -> (attribs . rotation)
|
||||
;; Reiner Kern (ohne DCL/Zeichnung, in tests/test_unit.lsp getestet).
|
||||
;; Parameter:
|
||||
;; attribs - bisherige Attribut-alist ((TAG . WERT) ...)
|
||||
;; name - Bezeichnung (String)
|
||||
;; hoehe-str - Hoehe in mm (String aus edit_box)
|
||||
;; drehung-str - Drehung in Grad (String aus edit_box)
|
||||
;; drehr-idx - popup_list-Index Drehrichtung ("0"=UZS, "1"=GUZ)
|
||||
;; einzelmodul - toggle-Wert "0"/"1"
|
||||
;; geruest-idx - popup_list-Index in *ssg-geruest-optionen* (String)
|
||||
;; Rueckgabe: (attribs-neu . rotation) oder nil bei ungueltiger Zahl.
|
||||
;; Drehung wird auf [0, 360) normiert.
|
||||
;; --------------------------------------------
|
||||
(defun eckrad-dialog-werte (attribs name hoehe-str drehung-str drehr-idx
|
||||
einzelmodul geruest-idx / hoehe drehung neu)
|
||||
(setq hoehe (distof hoehe-str 2)
|
||||
drehung (distof drehung-str 2))
|
||||
(if (and hoehe drehung)
|
||||
(progn
|
||||
(setq drehung (rem (+ (rem drehung 360.0) 360.0) 360.0))
|
||||
(setq neu (list (cons "Bezeichnung" name)
|
||||
(cons "HOEHE" (rtos hoehe 2 0))
|
||||
(cons "DREHUNG" (rtos drehung 2 1))
|
||||
(cons "DREHRICHTUNG" (if (= drehr-idx "1") "GUZ" "UZS"))
|
||||
(cons "GERUEST_EINZELMODUL" (if (= einzelmodul "1") "1" "0"))
|
||||
(cons "GERUEST_TYP" (ssg-geruest-idx-to-typ (atoi geruest-idx)))))
|
||||
;; Neue Werte ueber die bestehenden legen (attribs als Default-Schema)
|
||||
(cons (ssg-attrib-merge neu (mapcar '(lambda (p) (list (car p) (cdr p))) attribs))
|
||||
drehung)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; --------------------------------------------
|
||||
;; eckrad-dialog: DCL-Dialog fuer Einfuegen und Bearbeiten
|
||||
;; Parameter:
|
||||
;; attribs - Attribut-alist zur Vorbelegung (mit *eckrad-attrib-defs* gemergt)
|
||||
;; rotation - aktuelle Drehung in Grad
|
||||
;; Rueckgabe: (attribs-neu . rotation-neu) oder nil (Abbruch/DCL fehlt)
|
||||
;; --------------------------------------------
|
||||
(defun eckrad-dialog (attribs rotation / dcl-pfad dat eckrad-dlg-ergebnis)
|
||||
(setq attribs (ssg-attrib-merge attribs *eckrad-attrib-defs*))
|
||||
(setq dcl-pfad (strcat (getenv "DXFM_DCL") "/eckrad_edit.dcl"))
|
||||
(setq dat (load_dialog dcl-pfad))
|
||||
(if (or (< dat 0) (not (new_dialog "eckrad_edit" dat)))
|
||||
(progn
|
||||
(if (>= dat 0) (unload_dialog dat))
|
||||
(alert (ssg-textf "kreisel-edit-dialog-fehlt" (list dcl-pfad)))
|
||||
nil
|
||||
)
|
||||
(progn
|
||||
(set_tile "name" (cdr (assoc "Bezeichnung" attribs)))
|
||||
(set_tile "hoehe" (cdr (assoc "HOEHE" attribs)))
|
||||
(set_tile "drehung" (rtos rotation 2 1))
|
||||
|
||||
(start_list "drehrichtung")
|
||||
(add_list "UZS")
|
||||
(add_list "GUZ")
|
||||
(end_list)
|
||||
(set_tile "drehrichtung" (if (equal (cdr (assoc "DREHRICHTUNG" attribs)) "GUZ") "1" "0"))
|
||||
|
||||
(set_tile "geruest_einzelmodul"
|
||||
(if (equal (cdr (assoc "GERUEST_EINZELMODUL" attribs)) "1") "1" "0"))
|
||||
|
||||
(start_list "geruest_typ")
|
||||
(foreach opt *ssg-geruest-optionen* (add_list opt))
|
||||
(end_list)
|
||||
(set_tile "geruest_typ" (itoa (ssg-geruest-typ-to-idx (cdr (assoc "GERUEST_TYP" attribs)))))
|
||||
|
||||
;; OK nur bei gueltigen Zahlen, sonst Fehlerzeile und Dialog bleibt offen
|
||||
(action_tile "accept"
|
||||
(strcat
|
||||
"(setq eckrad-dlg-ergebnis (eckrad-dialog-werte attribs"
|
||||
" (get_tile \"name\") (get_tile \"hoehe\") (get_tile \"drehung\")"
|
||||
" (get_tile \"drehrichtung\") (get_tile \"geruest_einzelmodul\")"
|
||||
" (get_tile \"geruest_typ\")))"
|
||||
"(if eckrad-dlg-ergebnis (done_dialog 1)"
|
||||
" (set_tile \"error\" (ssg-text \"kreisel-eckrad-ungueltige-zahl\")))"
|
||||
)
|
||||
)
|
||||
(action_tile "cancel" "(setq eckrad-dlg-ergebnis nil) (done_dialog 0)")
|
||||
|
||||
(start_dialog)
|
||||
(unload_dialog dat)
|
||||
eckrad-dlg-ergebnis
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; -----------------------------------------------
|
||||
;; EckradEdit: Bestehendes Eckrad per Dialog bearbeiten
|
||||
;; (Doppelklick auf ECKRAD_n ueber SSG_BLOCKEDIT)
|
||||
;;
|
||||
;; Ablauf:
|
||||
;; 1. Eckrad-Block auswaehlen (oder vorselektiert)
|
||||
;; 2. Dialog mit aktuellen Werten oeffnen
|
||||
;; 3. Bei OK: Block loeschen und mit gleicher Nummer neu einfuegen
|
||||
;; -----------------------------------------------
|
||||
(defun c:EckradEdit ( / ss sel ent ed attribs basePoint rotation ergebnis)
|
||||
|
||||
;; Implied Selection VOR ssg-start pruefen (ssg-start hebt Selektion auf)
|
||||
(setq ss (ssget "I"))
|
||||
(if (and ss (= (sslength ss) 1)
|
||||
(= (cdr (assoc 0 (entget (ssname ss 0)))) "INSERT"))
|
||||
(setq ent (ssname ss 0))
|
||||
)
|
||||
|
||||
(ssg-start (ssg-text "kreisel-start-eckrad-edit") '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA")))
|
||||
(setvar "OSMODE" 0)
|
||||
|
||||
(if (null ent)
|
||||
(progn
|
||||
(setq sel (entsel (ssg-text "kreisel-prompt-eckrad-auswaehlen")))
|
||||
(if sel (setq ent (car sel)))
|
||||
)
|
||||
)
|
||||
(if (null ent) (progn (ssg-end) (exit)))
|
||||
|
||||
(setq ed (entget ent))
|
||||
(if (or (/= (cdr (assoc 0 ed)) "INSERT")
|
||||
(not (wcmatch (strcase (cdr (assoc 2 ed))) "ECKRAD_*")))
|
||||
(progn (ssg-emsg (ssg-text "kreisel-emsg-kein-eckrad-block")) (ssg-end) (exit))
|
||||
)
|
||||
|
||||
;; Bestehende Werte auslesen, fehlende mit Defaults auffuellen
|
||||
(setq attribs (ssg-attrib-merge (ssg-attrib-read ent) *eckrad-attrib-defs*))
|
||||
(setq basePoint (cdr (assoc 10 ed)))
|
||||
(setq rotation (* (/ 180.0 pi) (cdr (assoc 50 ed))))
|
||||
|
||||
;; HOEHE immer aus Z-Koordinate des Einfuegepunkts
|
||||
(setq attribs (subst (cons "HOEHE" (rtos (if (caddr basePoint) (caddr basePoint) 0.0) 2 0))
|
||||
(assoc "HOEHE" attribs) attribs))
|
||||
|
||||
(setq ergebnis (eckrad-dialog attribs rotation))
|
||||
(if ergebnis
|
||||
(progn
|
||||
(setq attribs (car ergebnis)
|
||||
rotation (cdr ergebnis))
|
||||
(command "_.ERASE" ent "")
|
||||
(ils-eckrad-insert (list (car basePoint) (cadr basePoint)
|
||||
(atof (cdr (assoc "HOEHE" attribs))))
|
||||
rotation attribs)
|
||||
(princ (ssg-textf "kreisel-eckrad-edit-aktualisiert"
|
||||
(list (cdr (assoc "Bezeichnung" attribs))
|
||||
(cdr (assoc "HOEHE" attribs))
|
||||
(rtos rotation 2 1))))
|
||||
)
|
||||
(princ (ssg-text "kreisel-abgebrochen"))
|
||||
)
|
||||
|
||||
(ssg-end)
|
||||
)
|
||||
|
||||
;; --------------------------------------------
|
||||
;; ils-eckrad-insert: Erzeugt Eckrad als Compound-Block mit Attributen
|
||||
;; Parameter:
|
||||
@@ -1182,10 +1391,16 @@
|
||||
(dbg 'z-hoehe)
|
||||
(dbg 'block-hoehe)
|
||||
|
||||
;; Automatische Nummer vergeben
|
||||
;; NUMMER: Bestehende beibehalten (EckradEdit), sonst neue vergeben -
|
||||
;; so behaelt ein bearbeitetes Eckrad seinen Blocknamen ECKRAD_n
|
||||
(setq eckrad-nummer (atoi (cdr (assoc "NUMMER" attribs))))
|
||||
(if (<= eckrad-nummer 0)
|
||||
(progn
|
||||
(setq eckrad-nummer (eckrad-next-number))
|
||||
(setq attribs (subst (cons "NUMMER" (itoa eckrad-nummer))
|
||||
(assoc "NUMMER" attribs) attribs))
|
||||
)
|
||||
)
|
||||
(dbg 'eckrad-nummer)
|
||||
|
||||
;; Blockname: ECKRAD_1, ECKRAD_2, ...
|
||||
@@ -1309,5 +1524,381 @@
|
||||
(princ)
|
||||
)
|
||||
|
||||
|
||||
;; ============================================================
|
||||
;; TEIL 5: BERUEHRPUNKTE / WEICHEN ZWISCHEN KREISELN UND ECKRAEDERN
|
||||
;; ============================================================
|
||||
;; Ermittelt, wo sich Kreisel (KREISEL_n) und Eckraeder (ECKRAD_n) gleicher
|
||||
;; Hoehe beruehren - dort sitzt eine ILS-Weiche. Gerechnet wird NUR aus den
|
||||
;; Blockparametern (Einfuegepunkt, Drehung, ABSTAND), nicht aus der
|
||||
;; gezeichneten Geometrie - dasselbe Modell wie draw-module:
|
||||
;; Kreisel: Rad AN8 bei P+R(r,0), Rad SP8 bei P+R(r+abstand,0), Laufprofile
|
||||
;; = die beiden Tangenten bei lokal y=+-r. Frei ist je Rad nur der
|
||||
;; aeussere Halbkreis (die "drei freien Seiten": vorn + beide
|
||||
;; Flanken), die innere Haelfte liegt zwischen den Laufprofilen.
|
||||
;; Eckrad: Vollkreis um P, ganz frei, kein Laufprofil.
|
||||
;; Paar-Logik:
|
||||
;; - Mindestabstand der Achsen (Kapsel-Modell) < 2r - tol -> KOLLISION
|
||||
;; (Rad ragt hinein, Laufprofile kreuzen sich) - keine Weiche.
|
||||
;; - Sonst je Rad: Rad/Rad mit |Abstand - 2r| <= tol bzw. Rad/Laufprofil
|
||||
;; mit |Lot - r| <= tol, Beruehrpunkt auf einer freien Radseite ->
|
||||
;; BERUEHRUNG ("Kreis/Kreis" bzw. "Kreis/Tangente", Namen wie im Python-
|
||||
;; Export export_neighbors.compute_kreisel_touch_switches).
|
||||
;; - Beruehren sich nur Laufprofile (Kreisel liegen Seite an Seite) ->
|
||||
;; KOLLISION "Tangente/Tangente".
|
||||
;; Die reinen Kerne (kreisel-bp-element/-paar/-berechnen) laufen ohne
|
||||
;; Zeichnung und sind in tests/test_unit.lsp getestet.
|
||||
|
||||
(setq *kreisel-bp-app* "SSG_KR_WEICHE")
|
||||
|
||||
;; --- 2D-Vektorhelfer (Punkte als (x y)) ---
|
||||
(defun kreisel-bp-plus (a b) (list (+ (car a) (car b)) (+ (cadr a) (cadr b))))
|
||||
(defun kreisel-bp-minus (a b) (list (- (car a) (car b)) (- (cadr a) (cadr b))))
|
||||
(defun kreisel-bp-mal (a s) (list (* (car a) s) (* (cadr a) s)))
|
||||
(defun kreisel-bp-dot (a b) (+ (* (car a) (car b)) (* (cadr a) (cadr b))))
|
||||
(defun kreisel-bp-abst (a b) (distance (list (car a) (cadr a)) (list (car b) (cadr b))))
|
||||
(defun kreisel-bp-mitte (a b) (kreisel-bp-mal (kreisel-bp-plus a b) 0.5))
|
||||
|
||||
;; Element-Record aus Blockparametern (reiner Kern).
|
||||
;; typ "KREISEL" oder "ECKRAD"
|
||||
;; p Einfuegepunkt (x y [z]) - Kreisel: AN-seitige Tangente, Eckrad: Mitte
|
||||
;; z Hoehe (Einfuege-Z)
|
||||
;; rot Drehung in Grad
|
||||
;; abstand Achsabstand AN8->SP8 (Kreisel), beim Eckrad ignoriert
|
||||
;; r Radradius
|
||||
;; id/handle/name nur zur Ausgabe/Zuordnung
|
||||
;; Rueckgabe: alist mit "raeder" ((label mitte freie-richtung|nil) ...),
|
||||
;; "profile" ((p1 p2) (p1 p2)) und "achse" (a b) fuer den Kapsel-Abstand.
|
||||
(defun kreisel-bp-element (typ p z rot abstand r id handle name / u n p2 a b)
|
||||
(setq u (list (cos (* pi (/ rot 180.0))) (sin (* pi (/ rot 180.0))))
|
||||
n (list (- (cadr u)) (car u))
|
||||
p2 (list (car p) (cadr p)))
|
||||
(if (= typ "KREISEL")
|
||||
(progn
|
||||
(setq a (kreisel-bp-plus p2 (kreisel-bp-mal u r))
|
||||
b (kreisel-bp-plus p2 (kreisel-bp-mal u (+ r abstand))))
|
||||
(list (cons "typ" typ) (cons "id" id) (cons "handle" handle) (cons "name" name)
|
||||
(cons "z" z) (cons "r" r)
|
||||
(cons "achse" (list a b))
|
||||
(cons "raeder" (list (list "AN8" a (kreisel-bp-mal u -1.0))
|
||||
(list "SP8" b u)))
|
||||
(cons "profile" (list (list (kreisel-bp-plus a (kreisel-bp-mal n r))
|
||||
(kreisel-bp-plus b (kreisel-bp-mal n r)))
|
||||
(list (kreisel-bp-minus a (kreisel-bp-mal n r))
|
||||
(kreisel-bp-minus b (kreisel-bp-mal n r)))))))
|
||||
(list (cons "typ" typ) (cons "id" id) (cons "handle" handle) (cons "name" name)
|
||||
(cons "z" z) (cons "r" r)
|
||||
(cons "achse" (list p2 p2))
|
||||
(cons "raeder" (list (list "ECKRAD" p2 nil)))
|
||||
(cons "profile" nil)))
|
||||
)
|
||||
|
||||
(defun kreisel-bp-get (key elem) (cdr (assoc key elem)))
|
||||
|
||||
;; Naechstgelegene Punkte zweier Strecken [p1,q1] und [p2,q2] (Ericson,
|
||||
;; Real-Time Collision Detection - wie export_neighbors._closest_pts_segments).
|
||||
;; Rueckgabe (c1 c2).
|
||||
(defun kreisel-bp-naechste-punkte (p1 q1 p2 q2 / eps d1 d2 rr a e f c b den s tt)
|
||||
(setq eps 1e-9
|
||||
d1 (kreisel-bp-minus q1 p1)
|
||||
d2 (kreisel-bp-minus q2 p2)
|
||||
rr (kreisel-bp-minus p1 p2)
|
||||
a (kreisel-bp-dot d1 d1)
|
||||
e (kreisel-bp-dot d2 d2)
|
||||
f (kreisel-bp-dot d2 rr))
|
||||
(cond
|
||||
((and (<= a eps) (<= e eps)) (setq s 0.0 tt 0.0))
|
||||
((<= a eps) (setq s 0.0 tt (max 0.0 (min 1.0 (/ f e)))))
|
||||
(t
|
||||
(setq c (kreisel-bp-dot d1 rr))
|
||||
(if (<= e eps)
|
||||
(setq tt 0.0 s (max 0.0 (min 1.0 (/ (- c) a))))
|
||||
(progn
|
||||
(setq b (kreisel-bp-dot d1 d2)
|
||||
den (- (* a e) (* b b)))
|
||||
(setq s (if (> den eps) (max 0.0 (min 1.0 (/ (- (* b f) (* c e)) den))) 0.0))
|
||||
(setq tt (/ (+ (* b s) f) e))
|
||||
(cond
|
||||
((< tt 0.0) (setq tt 0.0 s (max 0.0 (min 1.0 (/ (- c) a)))))
|
||||
((> tt 1.0) (setq tt 1.0 s (max 0.0 (min 1.0 (/ (- b c) a)))))
|
||||
)
|
||||
)
|
||||
))
|
||||
)
|
||||
(list (kreisel-bp-plus p1 (kreisel-bp-mal d1 s))
|
||||
(kreisel-bp-plus p2 (kreisel-bp-mal d2 tt)))
|
||||
)
|
||||
|
||||
;; Liegt Punkt pt auf der freien Seite des Rades (label mitte richtung)?
|
||||
;; Richtung nil = Vollkreis (Eckrad). Die Flanken (90 Grad) zaehlen mit.
|
||||
(defun kreisel-bp-frei-p (rad pt tol)
|
||||
(or (null (caddr rad))
|
||||
(>= (kreisel-bp-dot (kreisel-bp-minus pt (cadr rad)) (caddr rad)) (- tol)))
|
||||
)
|
||||
|
||||
;; Liegt pt genau auf einer Flanke (90 Grad zur Achse) eines Kreiselrades?
|
||||
;; Dort beginnt das eigene Laufprofil - eine Beruehrung NUR an der Flanke
|
||||
;; bedeutet parallel aneinanderliegende Kreisel, keine Weiche.
|
||||
(defun kreisel-bp-flanke-p (rad pt tol)
|
||||
(and (caddr rad)
|
||||
(<= (abs (kreisel-bp-dot (kreisel-bp-minus pt (cadr rad)) (caddr rad))) tol))
|
||||
)
|
||||
|
||||
;; Lotfusspunkt von c auf die Strecke (p1 p2). Rueckgabe (t fusspunkt), t
|
||||
;; unbegrenzt (0 = p1, 1 = p2).
|
||||
(defun kreisel-bp-lot (c p1 p2 / d l2 tt)
|
||||
(setq d (kreisel-bp-minus p2 p1)
|
||||
l2 (kreisel-bp-dot d d))
|
||||
(setq tt (if (> l2 1e-12) (/ (kreisel-bp-dot (kreisel-bp-minus c p1) d) l2) 0.0))
|
||||
(list tt (kreisel-bp-plus p1 (kreisel-bp-mal d tt)))
|
||||
)
|
||||
|
||||
;; Ergebnis-Record (reiner Kern)
|
||||
(defun kreisel-bp-treffer (status art ea eb pt rad-elem rad detail)
|
||||
(list (cons "status" status) (cons "art" art)
|
||||
(cons "a" ea) (cons "b" eb)
|
||||
(cons "p" pt)
|
||||
(cons "z" (/ (+ (kreisel-bp-get "z" ea) (kreisel-bp-get "z" eb)) 2.0))
|
||||
(cons "achse_von" rad-elem)
|
||||
;; Ausrichtung der Weiche: Radiale vom beruehrenden Rad zum Punkt
|
||||
(cons "achse_rot" (if rad (* (/ 180.0 pi) (angle (cadr rad) pt)) 0.0))
|
||||
(cons "detail" detail))
|
||||
)
|
||||
|
||||
;; Alle Beruehrungen eines Rades von ew mit Raedern/Profilen von eo.
|
||||
;; Rueckgabe: Liste von (pt art rad detail).
|
||||
(defun kreisel-bp-rad-kontakte (ew eo tol / r res d pt lot tt l)
|
||||
(setq r (kreisel-bp-get "r" ew))
|
||||
(foreach rad (kreisel-bp-get "raeder" ew)
|
||||
;; Rad/Rad
|
||||
(foreach rad2 (kreisel-bp-get "raeder" eo)
|
||||
(setq d (kreisel-bp-abst (cadr rad) (cadr rad2)))
|
||||
(if (<= (abs (- d (+ r (kreisel-bp-get "r" eo)))) tol)
|
||||
(progn
|
||||
(setq pt (kreisel-bp-mitte (cadr rad) (cadr rad2)))
|
||||
;; Beide Kreiselraeder nur an der Flanke = Kreisel Seite an Seite
|
||||
(if (and (kreisel-bp-frei-p rad pt tol) (kreisel-bp-frei-p rad2 pt tol)
|
||||
(not (and (kreisel-bp-flanke-p rad pt tol)
|
||||
(kreisel-bp-flanke-p rad2 pt tol))))
|
||||
(setq res (cons (list pt "Kreis/Kreis" rad
|
||||
(strcat (car rad) "/" (car rad2)))
|
||||
res))))))
|
||||
;; Rad/Laufprofil
|
||||
(foreach pr (kreisel-bp-get "profile" eo)
|
||||
(setq lot (kreisel-bp-lot (cadr rad) (car pr) (cadr pr))
|
||||
tt (car lot)
|
||||
pt (cadr lot)
|
||||
l (kreisel-bp-abst (car pr) (cadr pr)))
|
||||
(if (and (> l 0.0)
|
||||
(>= tt (- (/ tol l))) (<= tt (+ 1.0 (/ tol l)))
|
||||
(<= (abs (- (kreisel-bp-abst (cadr rad) pt) r)) tol)
|
||||
(kreisel-bp-frei-p rad pt tol)
|
||||
;; Flanke an Laufprofil = Profile liegen parallel aufeinander
|
||||
(not (kreisel-bp-flanke-p rad pt tol)))
|
||||
(setq res (cons (list pt "Kreis/Tangente" rad (strcat (car rad) "/Profil")) res))))
|
||||
)
|
||||
(reverse res)
|
||||
)
|
||||
|
||||
;; Ein Paar pruefen (reiner Kern). Rueckgabe: Liste von Treffer-Records
|
||||
;; (leer = kein Kontakt). Unterschiedliche Hoehe wird VORHER ausgefiltert.
|
||||
(defun kreisel-bp-paar (ea eb tol / ac bc cp dmin soll res kont pt doppelt)
|
||||
(setq ac (kreisel-bp-get "achse" ea)
|
||||
bc (kreisel-bp-get "achse" eb)
|
||||
cp (kreisel-bp-naechste-punkte (car ac) (cadr ac) (car bc) (cadr bc))
|
||||
dmin (kreisel-bp-abst (car cp) (cadr cp))
|
||||
soll (+ (kreisel-bp-get "r" ea) (kreisel-bp-get "r" eb)))
|
||||
(cond
|
||||
;; Weit auseinander - nichts
|
||||
((> dmin (+ soll tol)) nil)
|
||||
;; Ueberschneidung - Kollision, keine Weiche
|
||||
((< dmin (- soll tol))
|
||||
(list (kreisel-bp-treffer "kollision" "Ueberschneidung" ea eb
|
||||
(kreisel-bp-mitte (car cp) (cadr cp)) (kreisel-bp-get "id" ea) nil
|
||||
(strcat "Achsabstand " (rtos dmin 2 1)))))
|
||||
;; Beruehrung innerhalb Toleranz - Kontaktpunkte aufzaehlen
|
||||
(t
|
||||
(foreach k (append
|
||||
(mapcar '(lambda (x) (cons ea x)) (kreisel-bp-rad-kontakte ea eb tol))
|
||||
(mapcar '(lambda (x) (cons eb x)) (kreisel-bp-rad-kontakte eb ea tol)))
|
||||
;; k = (elem pt art rad detail); Dubletten (gleicher Punkt) verwerfen
|
||||
(setq pt (cadr k) doppelt nil)
|
||||
(foreach t2 res
|
||||
(if (<= (kreisel-bp-abst pt (kreisel-bp-get "p" t2)) (+ (* 2.0 tol) 1.0))
|
||||
(setq doppelt T)))
|
||||
(if (not doppelt)
|
||||
(setq res (append res
|
||||
(list (kreisel-bp-treffer "beruehrung" (nth 2 k) ea eb pt
|
||||
(kreisel-bp-get "id" (car k)) (nth 3 k) (nth 4 k)))))))
|
||||
(if res
|
||||
res
|
||||
;; Nur Laufprofile liegen aneinander (Seite an Seite) - keine Weiche
|
||||
(list (kreisel-bp-treffer "kollision" "Tangente/Tangente" ea eb
|
||||
(kreisel-bp-mitte (car cp) (cadr cp)) (kreisel-bp-get "id" ea) nil
|
||||
"Laufprofile liegen aneinander"))))
|
||||
)
|
||||
)
|
||||
|
||||
;; Alle Paare auswerten (reiner Kern).
|
||||
;; elemente = Liste von kreisel-bp-element-Records
|
||||
;; Rueckgabe: (beruehrungen kollisionen)
|
||||
(defun kreisel-bp-berechnen (elemente tol tol-z / rest ea ber kol)
|
||||
(setq rest elemente)
|
||||
(while rest
|
||||
(setq ea (car rest) rest (cdr rest))
|
||||
(foreach eb rest
|
||||
(if (<= (abs (- (kreisel-bp-get "z" ea) (kreisel-bp-get "z" eb))) tol-z)
|
||||
(foreach tr (kreisel-bp-paar ea eb tol)
|
||||
(if (= (kreisel-bp-get "status" tr) "kollision")
|
||||
(setq kol (cons tr kol))
|
||||
(setq ber (cons tr ber)))))))
|
||||
(list (reverse ber) (reverse kol))
|
||||
)
|
||||
|
||||
;; ABSTAND eines Kreisels: Attribut, sonst letzter Token des Blocknamens
|
||||
;; KREISEL_n_TYP_ABSTAND (reiner Kern).
|
||||
(defun kreisel-bp-abstand (attr-wert bname / pos)
|
||||
(cond
|
||||
((and attr-wert (> (atof attr-wert) 0.0)) (atof attr-wert))
|
||||
((setq pos (vl-string-position (ascii "_") bname 0 T))
|
||||
(atof (substr bname (+ pos 2))))
|
||||
(t 0.0))
|
||||
)
|
||||
|
||||
;; Toleranzen aus data/json/component_defaults.json (Sektion kreisel)
|
||||
(defun kreisel-bp-toleranz () (float (ssg-cfg-or "kreisel" "toleranz_beruehrung" 1.0)))
|
||||
(defun kreisel-bp-toleranz-z () (float (ssg-cfg-or "kreisel" "toleranz_hoehe" 1.0)))
|
||||
|
||||
;; Alle KREISEL_n/ECKRAD_n im Modellbereich als Element-Records sammeln.
|
||||
;; Bloecke auf gefrorenen Layern bleiben aussen vor (wie im Export).
|
||||
(defun kreisel-bp-elemente-sammeln ( / ss i ent ed bname typ attribs p r res)
|
||||
(setq r (/ *kreisel-durchmesser* 2.0))
|
||||
(if (setq ss (ssget "_X" '((0 . "INSERT") (2 . "KREISEL_*,ECKRAD_*") (410 . "Model"))))
|
||||
(progn
|
||||
(setq i 0)
|
||||
(while (setq ent (ssname ss i))
|
||||
(setq ed (entget ent)
|
||||
bname (strcase (cdr (assoc 2 ed)))
|
||||
typ (if (wcmatch bname "KREISEL_*") "KREISEL" "ECKRAD"))
|
||||
(if (not (and (car (atoms-family 1 '("SSG-ENT-LAYER-GEFROREN-P")))
|
||||
(ssg-ent-layer-gefroren-p ent)))
|
||||
(progn
|
||||
(setq attribs (ssg-attrib-read ent)
|
||||
p (cdr (assoc 10 ed)))
|
||||
(setq res (cons (kreisel-bp-element typ p (if (caddr p) (caddr p) 0.0)
|
||||
(* (/ 180.0 pi) (cdr (assoc 50 ed)))
|
||||
(if (= typ "KREISEL")
|
||||
(kreisel-bp-abstand (cdr (assoc "ABSTAND" attribs)) bname)
|
||||
0.0)
|
||||
r
|
||||
(cond ((cdr (assoc "ID" attribs))) (""))
|
||||
(cdr (assoc 5 ed))
|
||||
(cond ((cdr (assoc "Bezeichnung" attribs))
|
||||
(if (= (cdr (assoc "Bezeichnung" attribs)) "")
|
||||
bname (cdr (assoc "Bezeichnung" attribs))))
|
||||
(bname)))
|
||||
res))))
|
||||
(setq i (1+ i))
|
||||
)
|
||||
)
|
||||
)
|
||||
(reverse res)
|
||||
)
|
||||
|
||||
;; Einstieg fuer Befehl UND Export: (beruehrungen kollisionen) der Zeichnung
|
||||
(defun kreisel-bp-ermitteln ()
|
||||
(kreisel-bp-berechnen (kreisel-bp-elemente-sammeln)
|
||||
(kreisel-bp-toleranz) (kreisel-bp-toleranz-z))
|
||||
)
|
||||
|
||||
;; Anzeigetext eines Elements: "Kreisel3 (ID 0007)" bzw. ohne ID mit Handle
|
||||
(defun kreisel-bp-elem-text (e)
|
||||
(strcat (kreisel-bp-get "name" e)
|
||||
(if (/= (kreisel-bp-get "id" e) "")
|
||||
(strcat " (ID " (kreisel-bp-get "id" e) ")")
|
||||
(strcat " (#" (kreisel-bp-get "handle" e) ")")))
|
||||
)
|
||||
|
||||
;; Alte Marker (Kreise mit XDATA *kreisel-bp-app*) entfernen. Rueckgabe: Anzahl
|
||||
(defun kreisel-bp-marker-loeschen ( / ss i n)
|
||||
(setq n 0)
|
||||
(if (setq ss (ssget "_X" (list '(0 . "CIRCLE") (list -3 (list *kreisel-bp-app*)))))
|
||||
(progn
|
||||
(setq i 0)
|
||||
(while (< i (sslength ss))
|
||||
(entdel (ssname ss i))
|
||||
(setq i (1+ i) n (1+ n)))))
|
||||
n
|
||||
)
|
||||
|
||||
;; Marker-Kreis fuer einen Treffer zeichnen
|
||||
(defun kreisel-bp-marker (tr lay radius)
|
||||
(if (not (tblsearch "APPID" *kreisel-bp-app*)) (regapp *kreisel-bp-app*))
|
||||
(entmake (list '(0 . "CIRCLE") (cons 8 lay)
|
||||
(list 10 (car (kreisel-bp-get "p" tr)) (cadr (kreisel-bp-get "p" tr))
|
||||
(kreisel-bp-get "z" tr))
|
||||
(cons 40 radius)
|
||||
(list -3 (list *kreisel-bp-app*
|
||||
(cons 1000 (kreisel-bp-get "status" tr))
|
||||
(cons 1000 (kreisel-bp-get "art" tr))))))
|
||||
)
|
||||
|
||||
;; Layer fuer Marker: cfg/layer.cfg [allgemein] key, sonst Rueckfall
|
||||
(defun kreisel-bp-layer (key fallback farbe / lay)
|
||||
(if (null (setq lay (ssg-layer-anlegen key nil)))
|
||||
(progn (ssg-make-layer fallback farbe nil) (setq lay fallback)))
|
||||
lay
|
||||
)
|
||||
|
||||
;; Konsolenzeile fuer einen Treffer
|
||||
(defun kreisel-bp-zeile (key nr tr)
|
||||
(princ (ssg-textf key
|
||||
(list nr
|
||||
(kreisel-bp-elem-text (kreisel-bp-get "a" tr))
|
||||
(kreisel-bp-elem-text (kreisel-bp-get "b" tr))
|
||||
(strcat (kreisel-bp-get "art" tr) ", " (kreisel-bp-get "detail" tr))
|
||||
(rtos (car (kreisel-bp-get "p" tr)) 2 1)
|
||||
(rtos (cadr (kreisel-bp-get "p" tr)) 2 1)
|
||||
(rtos (kreisel-bp-get "z" tr) 2 1))))
|
||||
)
|
||||
|
||||
;; --------------------------------------------
|
||||
;; KREISEL_WEICHEN: Beruehrpunkte (Weichen) und Kollisionen aller Kreisel/
|
||||
;; Eckraeder ermitteln, in der Konsole auflisten und als Kreise markieren.
|
||||
;; Ein erneuter Aufruf ersetzt die Marker des vorigen Laufs.
|
||||
;; --------------------------------------------
|
||||
(defun c:KREISEL_WEICHEN ( / erg ber kol nr lay-w lay-k radius)
|
||||
(ssg-start (ssg-text "kreisel-bp-start") nil)
|
||||
(setq erg (kreisel-bp-ermitteln)
|
||||
ber (car erg)
|
||||
kol (cadr erg))
|
||||
(dbgmsg (strcat "KREISEL_WEICHEN: " (itoa (length ber)) " Beruehrungen, "
|
||||
(itoa (length kol)) " Kollisionen"))
|
||||
(kreisel-bp-marker-loeschen)
|
||||
(princ (ssg-textf "kreisel-bp-summe"
|
||||
(list (itoa (length ber)) (itoa (length kol)) (rtos (kreisel-bp-toleranz) 2 1))))
|
||||
(setq radius (float (ssg-cfg-or "kreisel" "weiche_marker_radius" 50.0)))
|
||||
(if ber
|
||||
(setq lay-w (kreisel-bp-layer "kreisel_weiche" "ZZ_Kreisel_Weichen" "4")))
|
||||
(if kol
|
||||
(setq lay-k (kreisel-bp-layer "kreisel_kollision" "ZZ_Kreisel_Kollision" "1")))
|
||||
(setq nr 0)
|
||||
(foreach tr ber
|
||||
(setq nr (1+ nr))
|
||||
(kreisel-bp-zeile "kreisel-bp-zeile" (itoa nr) tr)
|
||||
(kreisel-bp-marker tr lay-w radius))
|
||||
(setq nr 0)
|
||||
(foreach tr kol
|
||||
(setq nr (1+ nr))
|
||||
(kreisel-bp-zeile "kreisel-bp-kollision" (itoa nr) tr)
|
||||
(kreisel-bp-marker tr lay-k (* 2.0 radius)))
|
||||
(if (or ber kol)
|
||||
(princ (ssg-textf "kreisel-bp-marker"
|
||||
(list (itoa (+ (length ber) (length kol)))
|
||||
(cond (lay-w) (lay-k))))))
|
||||
(ssg-end)
|
||||
(princ)
|
||||
)
|
||||
|
||||
(princ "\n>>> KreiselInsert geladen")
|
||||
(princ)
|
||||
|
||||
+4
-2
@@ -684,7 +684,9 @@ AutoLISP-Implementierung fuer ILS Kreisel und Eckrad. Enthaelt alle Befehle fuer
|
||||
| `c:KreiselLabelSetup` | Schriftart, Hoehe und Farbe fuer Kreisel-Beschriftung konfigurieren |
|
||||
| `c:KreiselLabelPos` | Label-Offset (dx/dy) setzen |
|
||||
| `c:KreiselLabelHoehe` | Label-Hoehe aendern |
|
||||
| `c:ILS_Eckrad` | Eckrad an Tangentenpunkt einfuegen |
|
||||
| `c:ILS_Eckrad` | Eckrad an Tangentenpunkt einfuegen, Parameter per DCL-Dialog (`eckrad_edit.dcl`; ohne GUI Konsolen-Rueckfall) |
|
||||
| `c:EckradEdit` | Eckrad ueber DCL-Dialog bearbeiten (Bezeichnung, Hoehe, Drehung, Drehrichtung, Geruest; kein Abstand/Typ), Neuaufbau mit gleicher Nummer |
|
||||
| `c:KREISEL_WEICHEN` | Beruehrpunkte (= ILS-Weichen) aller Kreisel/Eckraeder gleicher Hoehe ermitteln: Rad/Rad und Rad/Laufprofil auf der freien Radseite, Toleranz `toleranz_beruehrung`/`toleranz_hoehe` (component_defaults.json). Ueberschneidungen und Seite-an-Seite-Kreisel werden als Kollision gemeldet. Konsolenliste + Kreis-Marker (Layer `kreisel_weiche`/`kreisel_kollision`), ein neuer Lauf ersetzt die Marker. Derselbe Kern (`kreisel-bp-ermitteln`) liefert beim Export `kreisel_weichen.json` fuer `export_csv.py`/`export_sivas.py` |
|
||||
|
||||
---
|
||||
|
||||
@@ -784,7 +786,7 @@ Erkennt den Blocktyp des selektierten INSERT-Blocks und ruft den passenden Bearb
|
||||
| Blockmuster | Aktion |
|
||||
| --- | --- |
|
||||
| `KREISEL_*` | `KreiselEdit` |
|
||||
| `ECKRAD_*` | `BEDIT` (Block-Editor) |
|
||||
| `ECKRAD_*` | `EckradEdit` |
|
||||
| `GF_*` | `GEFAELLESTRECKE_EDIT` |
|
||||
| `VF_*` | `VARIOFOERDERER_EDIT` |
|
||||
| `GR_*` | Geruest – `BEDIT` (kein eigener Edit-Pfad; siehe `GeruestInsert.lsp`) |
|
||||
|
||||
@@ -345,10 +345,10 @@
|
||||
(c:KreiselEdit)
|
||||
)
|
||||
|
||||
;; Eckrad-Bloecke -> BEDIT (kein eigener Edit-Dialog in LISP)
|
||||
;; Eckrad-Bloecke -> EckradEdit
|
||||
((wcmatch bname "ECKRAD_*")
|
||||
(princ (ssg-textf "cmd-eckrad-bearbeiten" (list bname)))
|
||||
(command "_.BEDIT" bname)
|
||||
(c:EckradEdit)
|
||||
)
|
||||
|
||||
;; Gefaellestrecke-Bloecke (GF_n) -> GEFAELLESTRECKE_EDIT (DCL-Dialog)
|
||||
|
||||
@@ -139,15 +139,18 @@
|
||||
;;; dieselben Muster wie csv:collect-export-blocks/export_csv.py.
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
;; --- Ist nm ein Kreisel/Eckrad-, VarioFoerderer- oder Gefaellestrecke-
|
||||
;; --- Ist nm ein Kreisel-, VarioFoerderer- oder Gefaellestrecke-
|
||||
;; Compound-Block? Alle drei fuehren einheitlich die Attribute
|
||||
;; ANZAHL_SCANNER/ANZAHL_SEPARATOR. ---
|
||||
;; ANZAHL_SCANNER/ANZAHL_SEPARATOR. Das Eckrad faellt zwar unter
|
||||
;; pattern_kreisel, hat diese Attribute aber nicht und zaehlt bewusst
|
||||
;; KEINE Sensoren (Entscheidung 2026-10) - darum ausgenommen; sonst
|
||||
;; "FEHLER beim Schreiben der Anzahl-Attribute an ECKRAD_n". ---
|
||||
(defun cs-carrier-p (nm / up)
|
||||
(setq up (strcase nm))
|
||||
(and (not (wcmatch up (export:pattern "pattern_eckrad" "ECKRAD_*")))
|
||||
(or (wcmatch up (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*"))
|
||||
(wcmatch up (export:pattern "pattern_variofoerderer" "VF_*"))
|
||||
(wcmatch up (export:pattern "pattern_gefaellestrecke" "GF_*"))
|
||||
)
|
||||
(wcmatch up (export:pattern "pattern_gefaellestrecke" "GF_*"))))
|
||||
)
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
|
||||
+147
@@ -149,6 +149,7 @@
|
||||
(setq *export-filenames*
|
||||
(list
|
||||
(cons "raw_json_datei" "export_raw.json")
|
||||
(cons "kreisel_weichen_datei" "kreisel_weichen.json")
|
||||
(cons "sivas_script" "export_sivas.py")
|
||||
(cons "sivas_csv" "sivas.csv")
|
||||
(cons "csv_script" "export_csv.py")
|
||||
@@ -2493,6 +2494,77 @@
|
||||
;; Sammelt relevante Bloecke, schreibt JSON, ruft Python auf.
|
||||
;; label = Anzeigename (z.B. "EXPORTCSV")
|
||||
;; --- Export-Vorbereitung: Zeichnungszustand fuer den Export herrichten ---
|
||||
;; --- Hoehen-Attribute aus der Z-Koordinate neu berechnen ---
|
||||
;; Reiner Kern (in tests/test_unit.lsp getestet). Wird ein Kreisel/Eckrad/VF_n/
|
||||
;; GF_n nachtraeglich in Z verschoben, sind seine Hoehen-Attribute veraltet.
|
||||
;; Rechnet dieselben Formeln/Rundungen wie beim Bau:
|
||||
;; KREISEL: HOEHE = (rtos z 2 0), MONTAGEHOEHE_m = (rtos z/1000 2 3) (draw-module)
|
||||
;; ECKRAD : HOEHE = (rtos z 2 0) (ils-eckrad-insert)
|
||||
;; GF : VON = (fix z), MONTAGEHOEHE_m = (rtos z/1000 2 3) (gf-block-erstellen)
|
||||
;; VF : VON = (fix z), MONTAGEHOEHE_m = (rtos (VON+BIS)/2000 2 3) (vf_core/vf_linienzug)
|
||||
;; GF/VF : BIS = VON - (VON_alt - BIS_alt). NICHT z - DELTA_H_mm: DELTA_H_mm
|
||||
;; ist ohne Vorzeichen (steigende VF!) und separat abgeschnitten.
|
||||
;; DELTA_H_mm nur als Rueckfall, wenn VON/BIS fehlen.
|
||||
;; art = "KREISEL"/"ECKRAD"/"GF"/"VF", z = Einfuege-Z (VF/GF: Kettenstart),
|
||||
;; attribs = bisherige Attribut-alist.
|
||||
;; Rueckgabe: alist der neuen Werte, oder nil, wenn der Block nicht verschoben
|
||||
;; wurde (Attribut passt noch zu z) - dann bleibt er bit-genau beim Bau-Wert.
|
||||
(defun csv:hoehen-werte (art z attribs / alt von von-alt bis-alt diff bis)
|
||||
(cond
|
||||
((member art '("KREISEL" "ECKRAD"))
|
||||
(setq alt (cdr (assoc "HOEHE" attribs)))
|
||||
(if (not (and alt (/= alt "") (equal (atof alt) (atof (rtos z 2 0)) 1e-6)))
|
||||
(if (= art "KREISEL")
|
||||
(list (cons "HOEHE" (rtos z 2 0))
|
||||
(cons "MONTAGEHOEHE_m" (rtos (/ z 1000.0) 2 3)))
|
||||
(list (cons "HOEHE" (rtos z 2 0))))))
|
||||
((member art '("GF" "VF"))
|
||||
(setq von (fix z)
|
||||
von-alt (cdr (assoc "HOEHE_VON_mm" attribs))
|
||||
bis-alt (cdr (assoc "HOEHE_BIS_mm" attribs)))
|
||||
(if (not (and von-alt (/= von-alt "") (= (atoi von-alt) von)))
|
||||
(progn
|
||||
(setq diff (if (and von-alt (/= von-alt "") bis-alt (/= bis-alt ""))
|
||||
(- (atoi von-alt) (atoi bis-alt))
|
||||
(atoi (cond ((cdr (assoc "DELTA_H_mm" attribs))) ("0")))))
|
||||
(setq bis (- von diff))
|
||||
(list (cons "HOEHE_VON_mm" (itoa von))
|
||||
(cons "HOEHE_BIS_mm" (itoa bis))
|
||||
(cons "MONTAGEHOEHE_m"
|
||||
(if (= art "GF")
|
||||
(rtos (/ z 1000.0) 2 3)
|
||||
(rtos (/ (+ von bis) 2000.0) 2 3)))))))
|
||||
)
|
||||
)
|
||||
|
||||
;; Hoehen-Attribute aller Kreisel/Eckrad/VF_n/GF_n pruefen und bei in Z
|
||||
;; verschobenen Bloecken neu setzen (csv:hoehen-werte). Laeuft als erster
|
||||
;; Schritt von csv:export-vorbereiten, gilt also fuer EXPORTSIVAS und EXPORTCSV.
|
||||
(defun csv:hoehen-aktualisieren ( / ss i ent ed bname art werte n)
|
||||
(setq n 0)
|
||||
(if (setq ss (ssget "_X" '((0 . "INSERT") (2 . "KREISEL_*,ECKRAD_*,VF_*,GF_*"))))
|
||||
(progn
|
||||
(setq i 0)
|
||||
(while (setq ent (ssname ss i))
|
||||
(setq ed (entget ent)
|
||||
bname (strcase (cdr (assoc 2 ed))))
|
||||
(setq art (cond ((wcmatch bname "KREISEL_*") "KREISEL")
|
||||
((wcmatch bname "ECKRAD_*") "ECKRAD")
|
||||
((wcmatch bname "GF_*") "GF")
|
||||
((wcmatch bname "VF_*") "VF")))
|
||||
(setq werte (csv:hoehen-werte art (cadddr (assoc 10 ed)) (ssg-attrib-read ent)))
|
||||
(if werte
|
||||
(progn
|
||||
(ssg-attrib-set-on ent werte)
|
||||
(setq n (1+ n))))
|
||||
(setq i (1+ i))
|
||||
)
|
||||
)
|
||||
)
|
||||
(if (> n 0) (princ (ssg-textf "exp-hoehen-aktualisiert" (list n))))
|
||||
n
|
||||
)
|
||||
|
||||
;; Aus csv:run-export ausgelagert: alle Schritte, die VOR dem Sammeln/Schreiben
|
||||
;; den Zeichnungszustand herrichten - IDs sicherstellen, Stellvertreter der
|
||||
;; verpackten Kettenbausteine erzeugen und nummerieren, Omniflo-Attribute
|
||||
@@ -2522,6 +2594,9 @@
|
||||
(setq *csv-res-sammlung* nil)
|
||||
(setq *csv-ketten* nil)
|
||||
(setq *csv-sepliste-id-by-handle* nil)
|
||||
;; 0. Hoehen-Attribute in Z verschobener Kreisel/Eckrad/VF/GF nachziehen,
|
||||
;; damit Sivas- und Raw-Export die aktuelle Hoehe sehen.
|
||||
(csv:hoehen-aktualisieren)
|
||||
;; 1. IDs aller Bloecke sicherstellen und Duplikate korrigieren - hier nur
|
||||
;; fuer die dauerhaften Bloecke, Stellvertreter gibt es noch keine.
|
||||
(princ (ssg-textf "exp-check-ids" (list label)))
|
||||
@@ -2671,6 +2746,74 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; --- Kreisel-Beruehrpunkte (Weichen) fuer den Python-Export -------------
|
||||
;; Schreibt <out-dir>/kreisel_weichen.json aus kreisel-bp-ermitteln
|
||||
;; (KreiselInsert.lsp) - derselbe Kern wie der Befehl KREISEL_WEICHEN, nur
|
||||
;; ohne Marker. Gelesen von export_neighbors.load_lisp_touch_switches
|
||||
;; (export_csv.py) und export_sivas.py (Merkmal 4403). Partner werden ueber
|
||||
;; ihr ID-Attribut (= teileid im Python-Export) UND den Handle genannt.
|
||||
;; Eine alte Datei wird vorher geloescht, damit Python nie veraltete Punkte
|
||||
;; liest, falls KreiselInsert nicht geladen ist (dann rechnet Python selbst).
|
||||
;; Rueckgabe: Pfad der geschriebenen Datei oder nil.
|
||||
(defun csv:kreisel-weichen-json-eintrag (tr / ea eb)
|
||||
(setq ea (cdr (assoc "a" tr))
|
||||
eb (cdr (assoc "b" tr)))
|
||||
(strcat
|
||||
" {\"a\":\"" (csv:json-escape (cdr (assoc "id" ea))) "\""
|
||||
",\"b\":\"" (csv:json-escape (cdr (assoc "id" eb))) "\""
|
||||
",\"a_handle\":\"" (csv:json-escape (cdr (assoc "handle" ea))) "\""
|
||||
",\"b_handle\":\"" (csv:json-escape (cdr (assoc "handle" eb))) "\""
|
||||
",\"a_typ\":\"" (cdr (assoc "typ" ea)) "\""
|
||||
",\"b_typ\":\"" (cdr (assoc "typ" eb)) "\""
|
||||
",\"x\":" (rtos (car (cdr (assoc "p" tr))) 2 4)
|
||||
",\"y\":" (rtos (cadr (cdr (assoc "p" tr))) 2 4)
|
||||
",\"z\":" (rtos (cdr (assoc "z" tr)) 2 4)
|
||||
",\"art\":\"" (csv:json-escape (cdr (assoc "art" tr))) "\""
|
||||
",\"detail\":\"" (csv:json-escape (cdr (assoc "detail" tr))) "\""
|
||||
",\"achse_von\":\"" (csv:json-escape (cdr (assoc "achse_von" tr))) "\""
|
||||
",\"achse_rot_grad\":" (rtos (cdr (assoc "achse_rot" tr)) 2 4)
|
||||
"}")
|
||||
)
|
||||
|
||||
(defun csv:kreisel-weichen-json-liste (fh treffer / first)
|
||||
(setq first T)
|
||||
(foreach tr treffer
|
||||
(if (not first) (write-line "," fh))
|
||||
(princ (csv:kreisel-weichen-json-eintrag tr) fh)
|
||||
(setq first nil))
|
||||
(if treffer (write-line "" fh))
|
||||
)
|
||||
|
||||
(defun csv:kreisel-weichen-schreiben (label out-dir / pfad erg fh)
|
||||
(setq pfad (strcat out-dir "/" (export:filename "kreisel_weichen_datei")))
|
||||
(if (findfile pfad) (vl-file-delete pfad))
|
||||
(if (car (atoms-family 1 '("KREISEL-BP-ERMITTELN")))
|
||||
(progn
|
||||
(setq erg (kreisel-bp-ermitteln))
|
||||
(if (setq fh (open pfad "w"))
|
||||
(progn
|
||||
(write-line "{" fh)
|
||||
(write-line (strcat " \"toleranz_mm\": " (rtos (kreisel-bp-toleranz) 2 4) ",") fh)
|
||||
(write-line " \"beruehrungen\": [" fh)
|
||||
(csv:kreisel-weichen-json-liste fh (car erg))
|
||||
(write-line " ]," fh)
|
||||
(write-line " \"kollisionen\": [" fh)
|
||||
(csv:kreisel-weichen-json-liste fh (cadr erg))
|
||||
(write-line " ]" fh)
|
||||
(write-line "}" fh)
|
||||
(close fh)
|
||||
(princ (ssg-textf "exp-kreisel-weichen"
|
||||
(list label (itoa (length (car erg))) (itoa (length (cadr erg))) pfad)))
|
||||
(if (cadr erg)
|
||||
(princ (ssg-textf "exp-kreisel-kollision" (list label (itoa (length (cadr erg)))))))
|
||||
pfad
|
||||
)
|
||||
(progn (princ (ssg-textf "exp-file-open-error" (list label pfad))) nil)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; py-name = Python-Skript-Name (z.B. "export_csv.py")
|
||||
;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv")
|
||||
;; include-bbox = T -> Bounding-Box je Block ermitteln und mit exportieren
|
||||
@@ -2745,6 +2888,10 @@
|
||||
(list (itoa *csv-bbox-fail-count*)
|
||||
(cond (*csv-bbox-fail-first*) ("?"))))))
|
||||
|
||||
;; Kreisel-Beruehrpunkte (Weichen) neben die Raw-Datei schreiben -
|
||||
;; die Python-Skripte uebernehmen sie statt selbst zu rechnen.
|
||||
(csv:kreisel-weichen-schreiben label out-dir)
|
||||
|
||||
;; Python-Export-Skript aufrufen (ausgelagert, siehe oben).
|
||||
(csv:python-aufrufen label out-dir out-pfad py-name csv-name)
|
||||
)
|
||||
|
||||
+61
-3
@@ -682,9 +682,67 @@
|
||||
(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))))
|
||||
;; Fussabdruck eines Elements ENTLANG der Fahrtrichtung (Laengsanteil).
|
||||
;;
|
||||
;; Warum das nicht (car (vf-element-masse ...)) ist: vf-element-masse liefert
|
||||
;; KS_EIN->KS_AUS im BLOCK-Koordinatensystem. Ein Element wird aber so gedreht
|
||||
;; eingebaut, dass sein KS_EIN auf der Fahrtrichtung liegt (insert-block-ks-to-ks
|
||||
;; bzw. insert-block-by-ks mit (hz - ein-ang)). Bei einem drehenden Element -
|
||||
;; ES_Element_30 hat ein-ang = 120 Grad - landet dadurch ein TEIL der
|
||||
;; Block-Y-Komponente auf der Fahrtrichtung, und die Block-X-Komponente allein
|
||||
;; unterschaetzt den Laengsverbrauch massiv: beim ES_Element_30_rechts 242.6 mm
|
||||
;; statt tatsaechlich 576.2 mm (realer Bug, Nutzer-Befund 2026-10-09: die Kette
|
||||
;; ragte 333.65 mm ueber den gepickten Endpunkt hinaus).
|
||||
;;
|
||||
;; Die Projektion ist hz-UNABHAENGIG (der Einbauwinkel kuerzt sich heraus), sie
|
||||
;; haengt nur am blockinternen ein-ang:
|
||||
;; laengs = vx*cos(ein-ang) + vy*sin(ein-ang)
|
||||
;; Gleiche Rechnung wie etage-laengs in vf_etage.lsp mit d = -ein-ang, dort fuer
|
||||
;; die Rand-Bilanz des Etage-Typs.
|
||||
;;
|
||||
;; Rueckgabe: Laenge in mm, oder nil wenn der Block/sein KS fehlt (Aufrufer
|
||||
;; faellt dann auf das alte Verhalten zurueck).
|
||||
(defun vf-element-laengs (blockname / i rad)
|
||||
(setq i (vf-element-ks-info blockname))
|
||||
(if (null i)
|
||||
nil
|
||||
(progn
|
||||
(setq rad (* (car i) (/ pi 180.0)))
|
||||
(+ (* (caddr i) (cos rad)) (* (cadddr i) (sin rad))))))
|
||||
|
||||
;; Gegenstueck zu vf-element-laengs: Versatz QUER zur Fahrtrichtung.
|
||||
;; quer = -vx*sin(ein-ang) + vy*cos(ein-ang)
|
||||
;; Ebenfalls hz-unabhaengig. Beim ES_Element_30 sind das rund -52.5 mm - der
|
||||
;; seitliche Versatz, mit dem das ES-KS_AUS neben der Fahrtachse landet. Er
|
||||
;; wird NICHT ausgeglichen (der Hauptstrang bleibt gerade, Nutzer-Entscheidung
|
||||
;; 2026-09-15), aber er muss BEKANNT sein: die Bahn des ES-KS_AUS verlaeuft um
|
||||
;; genau diesen Betrag seitlich versetzt, und nur mit ihm trifft die
|
||||
;; Schnittpunkt-Rechnung gegen eine Ziellinie die richtige Stelle.
|
||||
(defun vf-element-quer (blockname / i rad)
|
||||
(setq i (vf-element-ks-info blockname))
|
||||
(if (null i)
|
||||
nil
|
||||
(progn
|
||||
(setq rad (* (car i) (/ pi 180.0)))
|
||||
(+ (* (- (caddr i)) (sin rad)) (* (cadddr i) (cos rad))))))
|
||||
|
||||
;; Setzt ein-dx/ein-dy/ein-dz (BLOCK-Koordinaten, fuer Quer-/Hoehenrechnungen)
|
||||
;; und zusaetzlich ein-laengs: den Fussabdruck ENTLANG der Fahrtrichtung. Wer
|
||||
;; eine Laenge von deltaL reserviert, nimmt ein-laengs - NICHT (abs ein-dx),
|
||||
;; siehe vf-element-laengs.
|
||||
(defun vf-set-es-masse (winkel seite / m blk)
|
||||
(setq blk (strcat "ES_Element_" winkel "_" seite))
|
||||
(setq m (vf-element-masse blk))
|
||||
(if m (setq ein-dx (car m) ein-dy (cadr m) ein-dz (caddr m)))
|
||||
;; Fallback auf das alte (zu kleine) Mass, wenn das KS nicht lesbar ist -
|
||||
;; lieber etwas zu kurz reservieren als gar nicht bauen koennen.
|
||||
(setq ein-laengs (vf-element-laengs blk))
|
||||
(if (and (null ein-laengs) m) (setq ein-laengs (abs (car m))))
|
||||
;; ein-quer: seitlicher Versatz des ES-KS_AUS zur Fahrtachse (siehe
|
||||
;; vf-element-quer). Wird fuer die Ziellinien-Schnittpunktrechnung gebraucht.
|
||||
(setq ein-quer (vf-element-quer blk))
|
||||
(if (null ein-quer) (setq ein-quer 0.0))
|
||||
ein-laengs)
|
||||
|
||||
|
||||
;; ============================================================
|
||||
|
||||
+465
-27
@@ -116,6 +116,36 @@
|
||||
(defun vfl-as-winkel () (if (boundp '*vfl-as-winkel*) *vfl-as-winkel* "90"))
|
||||
(defun vfl-es-winkel () (if (boundp '*vfl-es-winkel*) *vfl-es-winkel* "90"))
|
||||
|
||||
;; Laengs-Fussabdruck des ES-Elements, den ein Segment VORAB von deltaL
|
||||
;; reservieren muss, damit die Kette am gepickten Punkt endet statt
|
||||
;; darueber hinauszuragen.
|
||||
;;
|
||||
;; ein-laengs setzt vf-set-es-masse (vf_core.lsp) aus dem KS des Blocks; es ist
|
||||
;; der Anteil ENTLANG der Fahrtrichtung. Frueher stand hier (abs ein-dx) - die
|
||||
;; Block-X-Komponente - und die unterschaetzt ein DREHENDES ES-Element massiv:
|
||||
;; beim ES_Element_30_rechts 242.6 mm statt 576.2 mm, die Kette ragte dadurch
|
||||
;; 333.65 mm zu weit (Nutzer-Befund 2026-10-09). Siehe vf-element-laengs.
|
||||
;;
|
||||
;; Der seitliche Versatz (ein-dy) wird hier BEWUSST nicht ausgeglichen - der
|
||||
;; Hauptstrang bleibt gerade (Nutzer-Entscheidung 2026-09-15, bestaetigt
|
||||
;; 2026-10-09; vgl. vfl-richtung-mit-drehendem-fussabdruck).
|
||||
;;
|
||||
;; WICHTIG - Kopplung an die Null-Konvention: an rund zwei Dutzend Stellen
|
||||
;; wird ein-dx bewusst auf 0 gesetzt (und spaeter restauriert), um einen
|
||||
;; DOPPELTEN Abzug zu verhindern, wenn das ES-Element an anderer Stelle schon
|
||||
;; beruecksichtigt ist. ein-laengs wird dort NICHT mitgefuehrt. Darum gilt
|
||||
;; ein genulltes ein-dx hier als "nichts reservieren" und gewinnt vor
|
||||
;; ein-laengs - sonst wuerde dieser Fix genau den Doppelabzug einbauen, den
|
||||
;; jene Stellen vermeiden.
|
||||
;;
|
||||
;; Rueckfallkette, damit eine fehlende Messung nie einen (- deltaL nil)-Absturz
|
||||
;; ausloest: genulltes ein-dx -> ein-laengs -> (abs ein-dx) -> 0.0.
|
||||
(defun vfl-es-laengs ()
|
||||
(cond ((and (numberp ein-dx) (< (abs ein-dx) 0.001)) 0.0)
|
||||
((numberp ein-laengs) (abs ein-laengs))
|
||||
((numberp ein-dx) (abs ein-dx))
|
||||
(t 0.0)))
|
||||
|
||||
;; ============================================================
|
||||
;; TEIL 0a-2: MELDUNGEN (statt alert im Bau-Pfad)
|
||||
;; ============================================================
|
||||
@@ -332,6 +362,21 @@
|
||||
(list (cdr e)))
|
||||
nil))
|
||||
|
||||
;; Art des naechsten Replay-Eintrags, OHNE ihn zu verbrauchen ("DL", "STR",
|
||||
;; "REAL", ...) bzw. nil bei leerer Queue. STEP-Marker werden dabei wie in
|
||||
;; vfl-replay-pop uebersprungen.
|
||||
;;
|
||||
;; Gebraucht fuer Abwaertskompatibilitaet: wird an einer Stelle eine NEUE
|
||||
;; Frage eingefuehrt, fehlt deren Antwort im Journal aelterer Ketten. Statt
|
||||
;; blind zu fragen (und dadurch den naechsten Wert als Antwort zu
|
||||
;; verschlucken - ein Queue-Desync), kann der Aufrufer hier nachsehen, was
|
||||
;; tatsaechlich als Naechstes ansteht, und die Frage bei einem Altbestand
|
||||
;; ueberspringen.
|
||||
(defun vfl-replay-peek-art ( / q)
|
||||
(setq q *vfl-replay-queue*)
|
||||
(while (and q (= (car (car q)) "STEP")) (setq q (cdr q)))
|
||||
(if q (car (car q)) nil))
|
||||
|
||||
;; ============================================================
|
||||
;; TEIL 0b-1b: GLIED-SCHEMA (single source of truth)
|
||||
;; ============================================================
|
||||
@@ -671,6 +716,69 @@
|
||||
(setq r (vl-catch-all-apply 'vflw-wahl-impl (list prompt optionen default-idx)))
|
||||
(if (vl-catch-all-error-p r) "" r))
|
||||
|
||||
;; RADIO-BUTTON-Variante des generischen Wahl-Dialogs.
|
||||
;;
|
||||
;; vflw-wahl zeigt eine Listbox; fuer kurze Entscheidungen (2-3 Optionen) sind
|
||||
;; Radio-Buttons uebersichtlicher - man sieht alle Moeglichkeiten auf einen
|
||||
;; Blick statt sie in einer Liste zu suchen (Nutzer-Wunsch 2026-10-14).
|
||||
;;
|
||||
;; Technik wie bei den Standard-/Etage-Radio-Dialogen (vf_standard.lsp,
|
||||
;; Kommentar dort): Radio-Labels lassen sich nach new_dialog NICHT per
|
||||
;; set_tile aendern - darum wird die DCL bei JEDEM Aufruf frisch geschrieben
|
||||
;; (ssg-dcl-temp-schreiben) und die Labels direkt mit dem schon von ssg-text
|
||||
;; aufgeloesten String befuellt. Das funktioniert dadurch in jeder Sprache,
|
||||
;; ohne Sprachweiche. Wiederverwendet werden vfs-dcl-radio-gruppe-zeilen und
|
||||
;; vfs-dcl-radio-idx-cond (vf_standard wird vor vf_linienzug geladen).
|
||||
;;
|
||||
;; Rueckgabe IDENTISCH zu vflw-wahl: Index als String ("1", "2", ...) bzw. ""
|
||||
;; bei Abbruch/Zurueck (dann ist *vfl-wizard-abbruch-grund* gesetzt). Dadurch
|
||||
;; ist sie ein direkter Ersatz und die Journal-Semantik bleibt unberuehrt.
|
||||
(defun vflw-radio-wahl-impl (prompt optionen default-idx /
|
||||
zeilen dcl-pfad dat idx ergebnis n sauber)
|
||||
(setq n (length optionen))
|
||||
;; Die Optionstexte kommen als Konsolenzeilen ("\n 1 - Ziellinie ..."):
|
||||
;; vflw-clean schneidet Umbruch, fuehrende Leerzeichen und die Nummerierung
|
||||
;; weg - im Dialog nummerieren die Radio-Buttons selbst. Genau wie in
|
||||
;; vflw-wahl-impl, dort beim Fuellen der Listbox.
|
||||
(setq sauber (mapcar '(lambda (o) (vflw-clean o)) optionen))
|
||||
(setq zeilen
|
||||
(append
|
||||
(list "vflw_radio_dyn : dialog {"
|
||||
(strcat " label = \"" (ssg-dcl-label-escape (vflw-clean prompt)) "\";"))
|
||||
(vfs-dcl-radio-gruppe-zeilen "vflwopt" "" sauber)
|
||||
(list " ok_cancel;" "}")))
|
||||
(setq dcl-pfad (ssg-dcl-temp-schreiben "vflwradio" zeilen))
|
||||
(if (null dcl-pfad)
|
||||
'FEHLER
|
||||
(progn
|
||||
(setq dat (load_dialog dcl-pfad))
|
||||
(if (not (new_dialog "vflw_radio_dyn" dat))
|
||||
(progn
|
||||
(if (and dat (>= dat 0)) (unload_dialog dat))
|
||||
(vl-file-delete dcl-pfad)
|
||||
'FEHLER)
|
||||
(progn
|
||||
;; Vorauswahl setzen (default-idx ist 1-basiert, Tiles sind 0-basiert)
|
||||
(set_tile (strcat "vflwopt_" (itoa (max 0 (1- (if default-idx default-idx 1))))) "1")
|
||||
(action_tile "accept"
|
||||
(strcat "(setq idx " (vfs-dcl-radio-idx-cond "vflwopt" n) ") (done_dialog 1)"))
|
||||
(action_tile "cancel" "(done_dialog 0)")
|
||||
(setq ergebnis (start_dialog))
|
||||
(unload_dialog dat)
|
||||
(vl-file-delete dcl-pfad)
|
||||
(if (= ergebnis 1)
|
||||
(itoa (1+ (if idx idx 0)))
|
||||
(progn (setq *vfl-wizard-abbruch-grund* "abbrechen") "")))))))
|
||||
|
||||
;; Dispatcher: bevorzugt Radio, faellt bei technischem Fehler (Temp-Datei
|
||||
;; nicht schreibbar/ladbar) auf die Listbox-Variante zurueck - genau wie die
|
||||
;; -radio/-popup-Paare in vf_standard.lsp.
|
||||
(defun vflw-radio-wahl (prompt optionen default-idx / r)
|
||||
(setq r (vl-catch-all-apply 'vflw-radio-wahl-impl (list prompt optionen default-idx)))
|
||||
(if (or (vl-catch-all-error-p r) (eq r 'FEHLER))
|
||||
(vflw-wahl prompt optionen default-idx)
|
||||
r))
|
||||
|
||||
;; ============================================================
|
||||
;; TEIL 0b-4: GRUPPEN-DIALOGE - GEMEINSAME PUNKTWAHL-BAUSTEINE
|
||||
;; ============================================================
|
||||
@@ -1865,10 +1973,16 @@
|
||||
(defun vfl-in-string (prompt)
|
||||
(vfl-in-value-p "STR"
|
||||
(function (lambda (v) (eq (type v) 'STR)))
|
||||
(function (lambda ()
|
||||
(function (lambda ( / txt)
|
||||
(if (and (vfl-wizard-mode-p) *vflw-menu-optionen*)
|
||||
(vflw-wahl (if *vflw-menu-frage* (ssg-text *vflw-menu-frage*) prompt)
|
||||
*vflw-menu-optionen* *vflw-menu-default*)
|
||||
(progn
|
||||
(setq txt (if *vflw-menu-frage* (ssg-text *vflw-menu-frage*) prompt))
|
||||
;; *vflw-menu-radio* setzt vfl-menu-radio fuer kurze Entscheidungen,
|
||||
;; bei denen Radio-Buttons uebersichtlicher sind als eine Listbox.
|
||||
;; Alle uebrigen Fragen bleiben unveraendert bei vflw-wahl.
|
||||
(if (and (boundp '*vflw-menu-radio*) *vflw-menu-radio*)
|
||||
(vflw-radio-wahl txt *vflw-menu-optionen* *vflw-menu-default*)
|
||||
(vflw-wahl txt *vflw-menu-optionen* *vflw-menu-default*)))
|
||||
(getstring prompt))))))
|
||||
|
||||
(defun vfl-in-real (prompt)
|
||||
@@ -1902,6 +2016,20 @@
|
||||
(setq *vflw-menu-default* default-idx)
|
||||
(setq *vflw-menu-frage* frage-key)
|
||||
(vfl-in-string prompt))
|
||||
;; Wie vfl-menu, zeigt die Auswahl im Wizard-Modus aber als RADIO-BUTTONS
|
||||
;; statt als Listbox - fuer kurze Entscheidungen (2-3 Optionen) uebersichtlicher
|
||||
;; (Nutzer-Wunsch 2026-10-14). Journal-Semantik identisch zu vfl-menu: es wird
|
||||
;; derselbe "STR"-Eintrag mit dem Auswahl-Index geschrieben. Das Flag ist
|
||||
;; lokal gebunden und gilt daher nur fuer DIESEN einen Aufruf.
|
||||
(defun vfl-menu-radio (prompt option-labels default-idx frage-key /
|
||||
*vflw-menu-optionen* *vflw-menu-default* *vflw-menu-frage*
|
||||
*vflw-menu-radio*)
|
||||
(setq *vflw-menu-optionen* option-labels)
|
||||
(setq *vflw-menu-default* default-idx)
|
||||
(setq *vflw-menu-frage* frage-key)
|
||||
(setq *vflw-menu-radio* T)
|
||||
(vfl-in-string prompt))
|
||||
|
||||
(defun vfl-menu-int (prompt option-labels default-idx frage-key /
|
||||
*vflw-menu-optionen* *vflw-menu-default* *vflw-menu-frage*)
|
||||
(setq *vflw-menu-optionen* option-labels)
|
||||
@@ -3206,23 +3334,68 @@
|
||||
(make-frame-from-dir endpunkt (hz-winkel->xu hz winkel))
|
||||
)
|
||||
|
||||
;; Ansicht auf Grundriss (Draufsicht, Welt-UCS) zoomen: nach jedem
|
||||
;; Kettenglied mit Geometrieerzeugung wird die neu entstandene Strecke sonst
|
||||
;; leicht aus dem sichtbaren Bereich oder in eine schraege 3D-Ansicht
|
||||
;; hinauslaufen - der naechste Punkt (vfl-neue-linie-messen, siehe unten)
|
||||
;; waere dann schwer/ungenau anzuwaehlen. _PLAN "_World" aendert nur die
|
||||
;; ANSICHT, nicht das aktuell aktive BKS. Rein kosmetisch (per
|
||||
;; vl-catch-all-apply abgesichert) - ein Fehler hier darf den eigentlichen
|
||||
;; Kettenbau nie unterbrechen.
|
||||
(defun vfl-view-refresh ( / )
|
||||
;; Headless nur Kosten: die Ansicht sieht niemand, _PLAN/_ZOOM laufen aber
|
||||
;; vor JEDER Laengeneingabe (vfl-neue-linie-messen) - im Batch reine Zeit.
|
||||
;; Blickrichtung ist Draufsicht (Welt-Z)? Dann ist ein _PLAN ueberfluessig.
|
||||
(defun vfl-draufsicht-p ( / vd)
|
||||
(setq vd (vl-catch-all-apply 'getvar (list "VIEWDIR")))
|
||||
(if (or (vl-catch-all-error-p vd) (null vd))
|
||||
t ; unbekannt -> nicht eingreifen
|
||||
(and (< (abs (car vd)) 1e-6)
|
||||
(< (abs (cadr vd)) 1e-6)
|
||||
(> (caddr vd) 0.0))))
|
||||
|
||||
;; Liegt pt komfortabel im sichtbaren Ausschnitt? VIEWCTR/VIEWSIZE geben Mitte
|
||||
;; und HOEHE des Viewports, SCREENSIZE das Seitenverhaeltnis. Der Faktor 0.85
|
||||
;; laesst einen Rand: ein Punkt direkt an der Fensterkante gilt als "fast
|
||||
;; draussen", damit auch die noch zu bauende Strecke Platz hat.
|
||||
;; Im Zweifel (Variable nicht lesbar) T -> lieber nichts tun als springen.
|
||||
(defun vfl-punkt-sichtbar-p (pt / ctr size scr aspect)
|
||||
(setq ctr (vl-catch-all-apply 'getvar (list "VIEWCTR"))
|
||||
size (vl-catch-all-apply 'getvar (list "VIEWSIZE"))
|
||||
scr (vl-catch-all-apply 'getvar (list "SCREENSIZE")))
|
||||
(if (or (vl-catch-all-error-p ctr) (vl-catch-all-error-p size)
|
||||
(vl-catch-all-error-p scr) (null ctr) (null size) (null pt)
|
||||
(not (numberp size)) (<= size 0.0))
|
||||
t
|
||||
(progn
|
||||
(setq aspect (if (and scr (numberp (car scr)) (numberp (cadr scr))
|
||||
(> (cadr scr) 0))
|
||||
(/ (float (car scr)) (float (cadr scr)))
|
||||
1.0))
|
||||
(and (< (abs (- (car pt) (car ctr))) (* 0.85 0.5 size aspect))
|
||||
(< (abs (- (cadr pt) (cadr ctr))) (* 0.85 0.5 size))))))
|
||||
|
||||
;; Ansicht fuer die naechste Punktwahl vorbereiten - so WENIG wie moeglich.
|
||||
;;
|
||||
;; Frueher lief hier vor JEDER Laengeneingabe hart (command "_.PLAN" "_World")
|
||||
;; + (command "_.ZOOM" "_Extents"). Bei einer grossen Anlage zoomt Extents auf
|
||||
;; die GESAMTE Zeichnung: die Ansicht sprang nach jedem Klick weg, und der
|
||||
;; Nutzer musste sich jedes Mal neu zurechtfinden (Nutzer-Wunsch 2026-10-09:
|
||||
;; "beim Setzen eines Segments an dieser Position bleiben").
|
||||
;;
|
||||
;; Jetzt wird nur eingegriffen, wenn es noetig ist:
|
||||
;; - _PLAN nur, wenn die Ansicht gar keine Draufsicht ist (in einer schraegen
|
||||
;; 3D-Ansicht ist ein XY-Pick mehrdeutig - das war der eigentliche Zweck).
|
||||
;; - verschoben wird nur, wenn pt aus dem sichtbaren Ausschnitt laeuft, und
|
||||
;; dann per _ZOOM _Center: der Ausschnitt folgt dem Baupunkt, die
|
||||
;; ZOOMSTUFE bleibt erhalten (anders als _Extents).
|
||||
;; Ist der Punkt sichtbar und die Ansicht eine Draufsicht, passiert GAR NICHTS.
|
||||
;;
|
||||
;; Rein kosmetisch und per vl-catch-all-apply abgesichert - ein Fehler hier
|
||||
;; darf den Kettenbau nie unterbrechen.
|
||||
(defun vfl-view-refresh (pt / size)
|
||||
;; Headless nur Kosten: die Ansicht sieht niemand.
|
||||
(if (not (vfl-headless-p))
|
||||
(vl-catch-all-apply
|
||||
(function (lambda ()
|
||||
(command "_.PLAN" "_World")
|
||||
(command "_.ZOOM" "_Extents")))
|
||||
nil))
|
||||
(progn
|
||||
(if (not (vfl-draufsicht-p))
|
||||
(vl-catch-all-apply 'command (list "_.PLAN" "_World")))
|
||||
(if (and pt (not (vfl-punkt-sichtbar-p pt)))
|
||||
(progn
|
||||
(setq size (vl-catch-all-apply 'getvar (list "VIEWSIZE")))
|
||||
(if (or (vl-catch-all-error-p size) (not (numberp size)))
|
||||
(vl-catch-all-apply 'command (list "_.ZOOM" "_Extents"))
|
||||
;; Mitte auf den Baupunkt, Hoehe unveraendert = gleiche Zoomstufe.
|
||||
(vl-catch-all-apply 'command
|
||||
(list "_.ZOOM" "_Center" (list (car pt) (cadr pt)) size)))))))
|
||||
(princ))
|
||||
|
||||
;; --- Hilfsfunktionen fuer die VF-Einheit ---
|
||||
@@ -3257,6 +3430,65 @@
|
||||
pt
|
||||
)
|
||||
|
||||
;; Punkt-Abfrage MIT Achsenzwang: der Cursor wird auf die Anschlussachse
|
||||
;; gezwungen, statt frei in jede Richtung zu laufen.
|
||||
;;
|
||||
;; Warum: das KS_AUS des zuletzt gebauten Teils legt die Richtung des KS_EIN
|
||||
;; des naechsten Teils bereits fest - frei ist nur noch die LAENGE. Bisher
|
||||
;; durfte der Nutzer irgendwohin klicken und vfl-in-abstand projizierte den
|
||||
;; Punkt STILL auf die Fahrtrichtung: das Ergebnis entsprach nicht dem Klick,
|
||||
;; und der Fehler fiel erst an der fertigen Kette auf (Nutzer-Befund
|
||||
;; 2026-10-09, sichtbarer Knick zwischen zwei Segmenten).
|
||||
;;
|
||||
;; Technik: SNAPANG (Rasterwinkel) temporaer auf hz drehen und ORTHOMODE
|
||||
;; einschalten - ORTHO richtet sich nach SNAPANG, der Cursor rastet dadurch
|
||||
;; auf der Anschlussachse (und 90 Grad quer dazu) ein.
|
||||
;;
|
||||
;; NICHT ueber einen BKS-Wechsel: der erste Versuch drehte das BKS per
|
||||
;; (command "_.UCS" "_3point" ...) - BricsCAD kennt diese Optionsbezeichnung
|
||||
;; nicht, der Aufruf scheiterte, und weil er in vl-catch-all-apply steckte,
|
||||
;; scheiterte er STILL: der Cursor blieb frei und nur die ORTHO-Rastung auf
|
||||
;; die WELT-Achsen war aktiv (Nutzer-Befund 2026-10-09: "kann immer noch jede
|
||||
;; Richtung waehlen"). SNAPANG braucht kein Kommando, keine Options-Syntax,
|
||||
;; kein _Previous und laesst das BKS voellig unberuehrt - getpoint liefert
|
||||
;; weiterhin dieselben Koordinaten wie bisher, es ist also KEINE
|
||||
;; Koordinaten-Transformation noetig.
|
||||
;;
|
||||
;; Erzwungen wird NUR die horizontale Richtung (Nutzer-Entscheidung
|
||||
;; 2026-10-09); die Hoehe bleibt frei, weil GF-/VF-Segmente ihre Neigung
|
||||
;; selbst festlegen. SNAPANG wirkt ausschliesslich in der XY-Ebene - genau
|
||||
;; das.
|
||||
;;
|
||||
;; Ohne base oder hz (erstes Kettensegment: Richtung noch frei) faellt die
|
||||
;; Funktion unveraendert auf vfl-getpoint zurueck - ebenso ohne GUI bzw.
|
||||
;; headless: dort gibt es keinen Cursor zu fuehren.
|
||||
;; Rueckgabe: Punkt wie vfl-getpoint (gleiches Bezugssystem) oder nil.
|
||||
(defun vfl-getpoint-achse (base prompt hz / alt-snapang alt-ortho pt)
|
||||
(if (or (null base) (null hz) (not (ssg-gui-p)) (vfl-headless-p))
|
||||
(vfl-getpoint base prompt)
|
||||
(progn
|
||||
(setq alt-snapang (vl-catch-all-apply 'getvar (list "SNAPANG")))
|
||||
(setq alt-ortho (vl-catch-all-apply 'getvar (list "ORTHOMODE")))
|
||||
(if (vl-catch-all-error-p alt-snapang) (setq alt-snapang nil))
|
||||
(if (vl-catch-all-error-p alt-ortho) (setq alt-ortho nil))
|
||||
;; SNAPANG erwartet RADIANT.
|
||||
(vl-catch-all-apply 'setvar (list "SNAPANG" (* (float hz) (/ pi 180.0))))
|
||||
(vl-catch-all-apply 'setvar (list "ORTHOMODE" 1))
|
||||
;; Gecatcht, damit SNAPANG/ORTHOMODE auch bei ESC oder Fehler sicher
|
||||
;; zurueckgesetzt werden - ein verdrehter Rasterwinkel wuerde sonst in
|
||||
;; der Zeichnung des Nutzers stehen bleiben. Ein Fehler wird zu nil,
|
||||
;; also exakt dem bisherigen Abbruch-Rueckgabewert von vfl-getpoint;
|
||||
;; der Aufrufer laeuft damit in seinen vorhandenen nil-Zweig.
|
||||
(setq pt (vl-catch-all-apply 'vfl-getpoint (list base prompt)))
|
||||
(if alt-snapang (vl-catch-all-apply 'setvar (list "SNAPANG" alt-snapang)))
|
||||
(if alt-ortho (vl-catch-all-apply 'setvar (list "ORTHOMODE" alt-ortho))
|
||||
(vl-catch-all-apply 'setvar (list "ORTHOMODE" 0)))
|
||||
(if (vl-catch-all-error-p pt) (setq pt nil))
|
||||
pt
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; Vorschlagswert fuer die GF-Zielhoehe: die aktuelle Kettenhoehe (ist-hoehe)
|
||||
;; UNVERAENDERT als Default anzubieten fuehrt in eine Falle - ein GF-Segment
|
||||
;; MUSS fallen (0 Grad Gefaelle gibt es nicht), ein direkt uebernommener
|
||||
@@ -3325,7 +3557,12 @@
|
||||
;; picken (Gummiband). Der Punkt lebt nur hier lokal, er wird nie
|
||||
;; journalisiert.
|
||||
(setq popped (vflw-pending-pop))
|
||||
(setq p2 (if popped (car popped) (vfl-getpoint p-akt prompt)))
|
||||
;; Achsenzwang, sobald die Fahrtrichtung vom Vorgaenger feststeht
|
||||
;; (hz-vorgabe): der Cursor rastet auf der Anschlussachse ein, statt
|
||||
;; frei zu laufen und spaeter still projiziert zu werden. Beim ERSTEN
|
||||
;; Segment (hz-vorgabe=nil) definiert der Klick die Richtung noch selbst
|
||||
;; - dort faellt vfl-getpoint-achse von sich aus auf vfl-getpoint zurueck.
|
||||
(setq p2 (if popped (car popped) (vfl-getpoint-achse p-akt prompt hz-vorgabe)))
|
||||
;; *vfl-letzter-pick-z*: die Z-Hoehe DIESES Picks, fuer eine unmittelbar
|
||||
;; nachfolgende Zielhoehe-Frage (siehe vfl-hoehe-endpunkt-abfragen) als
|
||||
;; Vorschlag verfuegbar machen - vorher bei JEDEM Endpunkt-Pick
|
||||
@@ -3360,6 +3597,101 @@
|
||||
(vfl-journal-record "REAL" hz-aktuell))
|
||||
(if deltaL (cons deltaL hz-aktuell) nil))
|
||||
|
||||
;; Laenge aus einer ZIELLINIE statt aus einem freien Klick.
|
||||
;;
|
||||
;; Steht die Fahrtrichtung fest (hz), ist die Laenge der letzten Strecke nicht
|
||||
;; mehr frei: die Bahn des ES-KS_AUS - um quer seitlich versetzt - trifft die
|
||||
;; angeklickte Linie in genau einem Punkt. Ein Klick kann diesen Punkt nur
|
||||
;; annaehern (bei weit herausgezoomter Ansicht sind das schnell etliche mm),
|
||||
;; die Rechnung trifft ihn exakt (Nutzer-Vorgabe 2026-10-09: "nur EIN Winkel,
|
||||
;; dann den Schnittpunkt auf der Linie anklicken, Z kann ignoriert werden").
|
||||
;;
|
||||
;; Z wird bewusst ignoriert - rein planare X/Y-Rechnung; die Hoehe kommt
|
||||
;; weiterhin aus dem Gefaellewinkel.
|
||||
;;
|
||||
;; JOURNAL: schreibt denselben "DL"-Eintrag wie vfl-in-abstand und liest beim
|
||||
;; Replay aus derselben Queue. Dadurch ist KEIN neues Journal-Format noetig -
|
||||
;; Doppelklick-Editieren, Replay und 2D/3D-Konvertierung sehen nur eine
|
||||
;; (genauere) Laenge und muessen von Ziellinien nichts wissen. Die Bindung an
|
||||
;; die Linie geht damit allerdings verloren: wird die Linie spaeter
|
||||
;; verschoben, folgt die Kette NICHT (dafuer muesste die Linie selbst
|
||||
;; journalisiert werden, wie Modus 2 es mit "OBJS" tut).
|
||||
;;
|
||||
;; Rueckgabe: Gesamtlaenge bis zum Schnittpunkt (der Aufrufer zieht davon wie
|
||||
;; gewohnt Separator-/ES-Fussabdruck ab) oder nil bei Abbruch / wenn keine
|
||||
;; brauchbare Linie getroffen wurde.
|
||||
(defun vfl-in-abstand-ziellinie (p-akt hz quer / popped pts L fertig p2 weg)
|
||||
(if *vfl-wizard-abbruch-grund*
|
||||
(progn
|
||||
(dbgmsg (strcat "ABBRUCH-TRIGGER (Ziellinie): " *vfl-wizard-abbruch-grund* " -> (exit)"))
|
||||
(exit)))
|
||||
;; Eigene Replay-Logik (ein Eintrag pro Aufruf) -> eigener Headless-Riegel:
|
||||
;; leere Queue ist ein Desync, kein "Nutzer soll waehlen".
|
||||
(if (and (null *vfl-replay-queue*) (vfl-headless-p))
|
||||
(vfl-headless-abbruch "DL"))
|
||||
;; --- Wahl des Eingabewegs (zwei gleichwertige Optionen) ---
|
||||
;; ABWAERTSKOMPATIBILITAET: Ketten, die VOR der Einfuehrung dieser Frage
|
||||
;; gebaut wurden, haben an dieser Stelle kein "STR" im Journal, sondern
|
||||
;; direkt den "DL"-Wert. Wuerde hier blind gefragt, verschluckte die
|
||||
;; Menue-Abfrage diesen Laengenwert als Antwort -> Queue-Desync und eine
|
||||
;; falsch nachgebaute Kette. Darum beim Replay erst nachsehen, was
|
||||
;; tatsaechlich ansteht: steht dort ein "DL", ist es ein Altbestand und die
|
||||
;; Frage entfaellt (dessen Laenge entstand durch freie Punktwahl).
|
||||
(if (and *vfl-replay-queue* (= (vfl-replay-peek-art) "DL"))
|
||||
(setq weg "2")
|
||||
(progn
|
||||
(princ (ssg-text "vfl-endpunkt-modus-header"))
|
||||
(princ (ssg-text "vfl-endpunkt-modus-opt-linie"))
|
||||
(princ (ssg-text "vfl-endpunkt-modus-opt-punkt"))
|
||||
(setq weg (vfl-menu-radio (ssg-text "prompt-wahl-1-2")
|
||||
(list (ssg-text "vfl-endpunkt-modus-opt-linie")
|
||||
(ssg-text "vfl-endpunkt-modus-opt-punkt")) 1
|
||||
"vfl-endpunkt-modus-header"))))
|
||||
(if *vfl-replay-queue*
|
||||
(progn
|
||||
(setq popped (vfl-replay-pop))
|
||||
(setq L (if popped (car popped) nil)))
|
||||
;; Live: wiederholen, bis eine brauchbare Eingabe vorliegt. Beide Wege
|
||||
;; liefern EINE Laenge und schreiben daher denselben einzelnen
|
||||
;; "DL"-Journaleintrag - der Replay sieht keinen Unterschied und muss von
|
||||
;; Ziellinien nichts wissen. Abbruch (ESC) liefert nil; der Aufrufer
|
||||
;; behandelt das wie jeden anderen Eingabe-Abbruch.
|
||||
(progn
|
||||
(setq fertig nil)
|
||||
(while (not fertig)
|
||||
(if (= weg "2")
|
||||
;; --- Option 2: freie Punktwahl (wie bisher) ---
|
||||
;; Punkt picken und auf die feststehende Fahrtrichtung projizieren.
|
||||
;; Der Achsenzwang (vfl-getpoint-achse) gilt dabei weiter, der
|
||||
;; Cursor rastet also auf der Anschlussachse ein. Noetig, wenn gar
|
||||
;; keine Ziellinie existiert (Nutzer-Wunsch 2026-10-12).
|
||||
(progn
|
||||
(setq p2 (vfl-getpoint-achse p-akt
|
||||
(ssg-text "vfl-prompt-endpunkt-fahrtrichtung") hz))
|
||||
(if (null p2)
|
||||
(setq L nil fertig t)
|
||||
(progn
|
||||
(setq L (vfl-projiziere-distanz p-akt p2 hz))
|
||||
(if (< L 1.0)
|
||||
(vfl-meldung (ssg-text "vfl-ziellinie-hinter-start"))
|
||||
(setq fertig t)))))
|
||||
;; --- Option 1: Ziellinie aus zwei Punkten, Schnittpunkt gerechnet ---
|
||||
(progn
|
||||
(setq pts (vfl-ziellinie-zwei-punkte))
|
||||
(cond
|
||||
;; ESC / Punkte identisch
|
||||
((null pts) (setq L nil fertig t))
|
||||
(t
|
||||
(setq L (vfl-laenge-bis-ziellinie p-akt hz quer (car pts) (cadr pts)))
|
||||
(cond
|
||||
;; parallel zur Fahrtrichtung -> kein eindeutiger Schnittpunkt
|
||||
((null L) (vfl-meldung (ssg-text "vfl-ziellinie-parallel")))
|
||||
;; Schnittpunkt liegt hinter dem Start -> unbrauchbar
|
||||
((< L 1.0) (vfl-meldung (ssg-text "vfl-ziellinie-hinter-start")))
|
||||
(t (setq fertig t))))))))))
|
||||
(vfl-journal-record (if L "DL" "NIL") L)
|
||||
L)
|
||||
|
||||
;; Rueckgabe: (deltaL hz) oder nil bei Abbruch. Foerderer-Maximallaenge 25 m:
|
||||
;; bei Ueberschreitung wird die Eingabe abgelehnt und der Endpunkt erneut
|
||||
;; abgefragt.
|
||||
@@ -3390,7 +3722,9 @@
|
||||
(if p2 (vfl-hz-snappen-absolut (vfl-planar-hz p-akt p2)) nil)))))
|
||||
|
||||
(defun vfl-neue-linie-messen (p-akt hz-vorgabe / erg deltaL hz-aktuell ergebnis fertig)
|
||||
(vfl-view-refresh)
|
||||
;; Ansicht nur nachfuehren, wenn der Baupunkt aus dem Bild laeuft - sonst
|
||||
;; bleibt der Ausschnitt stehen (siehe vfl-view-refresh).
|
||||
(vfl-view-refresh p-akt)
|
||||
(setq fertig nil)
|
||||
(while (not fertig)
|
||||
(setq erg (vfl-in-abstand p-akt hz-vorgabe
|
||||
@@ -3422,6 +3756,92 @@
|
||||
;; die Drehteller-Rotation, die insert-block-mixed-to-ks beim echten Einfuegen
|
||||
;; anwendet, und liefern empirisch bestaetigt einen falschen Wert (~210mm
|
||||
;; Schaetzung vs. ~420mm tatsaechlich noetiger Versatz bei AS_Element_30).
|
||||
;; ============================================================
|
||||
;; ZIELLINIE: X/Y-Lage des Kettenendes aus einer Linie ableiten
|
||||
;; ============================================================
|
||||
;; Steht die Fahrtrichtung fest (Frame des Vorgaengers) und soll die Kette mit
|
||||
;; einem ES-Element auf einer vorgegebenen Linie enden, ist die Laenge NICHT
|
||||
;; mehr frei: das ES-KS_AUS laeuft auf einer Geraden PARALLEL zur
|
||||
;; Fahrtrichtung (um den ES-Querversatz seitlich versetzt), und die trifft die
|
||||
;; Ziellinie in genau einem Punkt.
|
||||
;;
|
||||
;; WICHTIG - das ist bewusst eine REIN PLANARE (X/Y-)Rechnung, kein
|
||||
;; 3D-Schnittpunkt (Nutzer-Praezisierung 2026-10-09): die HOEHE ergibt sich
|
||||
;; weiterhin aus dem Gefaellewinkel (3 Grad), die Ziellinie bestimmt
|
||||
;; ausschliesslich die X/Y-Lage. Die Z-Koordinaten der Linie werden daher
|
||||
;; ignoriert.
|
||||
;;
|
||||
;; 2D-Kreuzprodukt (Determinante) zweier Richtungen - 0 heisst parallel.
|
||||
(defun vfl-kreuz2 (ax ay bx by) (- (* ax by) (* ay bx)))
|
||||
|
||||
;; Horizontale Laenge L vom Startpunkt bis zu dem Punkt, an dem die um
|
||||
;; quer seitlich versetzte Bahn in Richtung hz die Ziellinie (durch pa und pb)
|
||||
;; schneidet.
|
||||
;; p-start : Startpunkt der Bahn (X/Y zaehlen)
|
||||
;; hz : Fahrtrichtung in Grad
|
||||
;; quer : seitlicher Versatz der Bahn (ES-Querversatz; 0 = Achse selbst)
|
||||
;; pa/pb : zwei Punkte der Ziellinie
|
||||
;; Rueckgabe: L (kann negativ sein, der Aufrufer prueft das) oder nil, wenn
|
||||
;; die Ziellinie parallel zur Fahrtrichtung liegt (kein eindeutiger Treffer).
|
||||
(defun vfl-laenge-bis-ziellinie (p-start hz quer pa pb /
|
||||
rad ux uy nx ny vx vy wx wy nenner)
|
||||
(setq rad (* (float hz) (/ pi 180.0)))
|
||||
(setq ux (cos rad) uy (sin rad)) ; Fahrtrichtung
|
||||
(setq nx (- uy) ny ux) ; quer dazu
|
||||
(setq vx (- (car pb) (car pa)) ; Richtung der Ziellinie
|
||||
vy (- (cadr pb) (cadr pa)))
|
||||
(setq nenner (vfl-kreuz2 ux uy vx vy))
|
||||
(if (< (abs nenner) 1e-9)
|
||||
nil ; parallel -> kein Schnittpunkt
|
||||
(progn
|
||||
;; w = pa - (p-start + quer*n) (Startpunkt der versetzten Bahn)
|
||||
(setq wx (- (car pa) (+ (car p-start) (* quer nx)))
|
||||
wy (- (cadr pa) (+ (cadr p-start) (* quer ny))))
|
||||
(/ (vfl-kreuz2 wx wy vx vy) nenner))))
|
||||
|
||||
;; Ziellinie auswaehlen und als zwei Punkte zurueckgeben.
|
||||
;;
|
||||
;; entsel deckt BEIDE vom Nutzer gewuenschten Eingabewege in EINER Interaktion
|
||||
;; ab (Nutzer-Wunsch 2026-10-09 "kann man nicht beide anbieten?"): es liefert
|
||||
;; gleichzeitig das getroffene Objekt UND den Pickpunkt. Wird eine Linie
|
||||
;; getroffen, wird exakt mit ihr gerechnet; wird nichts getroffen, liefert die
|
||||
;; Funktion nil und der Aufrufer bleibt beim bisherigen Punkt-Verhalten.
|
||||
;;
|
||||
;; Statt den Objekttyp zu unterscheiden, wird die TANGENTE am Pickpunkt
|
||||
;; genommen (vlax-curve-*): fuer eine LINE ist das exakt die Linie selbst, fuer
|
||||
;; eine Polylinie genau das angeklickte Segment - ohne Segment-Suche von Hand.
|
||||
;; Rueckgabe: (pa pb) oder nil.
|
||||
;; Ziellinie durch ZWEI selbst gesetzte Punkte definieren.
|
||||
;;
|
||||
;; Allgemeiner als das Anklicken eines vorhandenen Objekts (Nutzer-Idee
|
||||
;; 2026-10-12): es muss gar keine Linie gezeichnet sein. Existiert doch eine,
|
||||
;; faengt man mit Objektfang zwei Punkte darauf und hat sie exakt erfasst -
|
||||
;; dieser Weg deckt den Objekt-Fall also mit ab.
|
||||
;;
|
||||
;; WICHTIG - hier KEIN Achsenzwang (vfl-getpoint statt vfl-getpoint-achse):
|
||||
;; die Ziellinie darf jede Richtung haben. Wuerde der Cursor auf die
|
||||
;; Fahrtrichtung gezwungen, koennte man nur Linien PARALLEL dazu definieren -
|
||||
;; und genau die haben keinen Schnittpunkt.
|
||||
;;
|
||||
;; Die beiden Punkte werden NICHT journalisiert: ins Journal geht allein die
|
||||
;; daraus berechnete Laenge (ein "DL", siehe vfl-in-abstand-ziellinie). Darum
|
||||
;; der rohe vfl-getpoint und nicht vfl-in-point.
|
||||
;; Rueckgabe: (p1 p2) oder nil bei Abbruch.
|
||||
(defun vfl-ziellinie-zwei-punkte ( / p1 p2)
|
||||
(setq p1 (vfl-getpoint nil (ssg-text "vfl-prompt-ziellinie-p1")))
|
||||
(if (null p1)
|
||||
nil
|
||||
(progn
|
||||
;; Gummiband ab p1, damit die entstehende Ziellinie sichtbar ist.
|
||||
(setq p2 (vfl-getpoint p1 (ssg-text "vfl-prompt-ziellinie-p2")))
|
||||
(if (or (null p2)
|
||||
(and (< (abs (- (car p2) (car p1))) 1e-9)
|
||||
(< (abs (- (cadr p2) (cadr p1))) 1e-9)))
|
||||
nil ; abgebrochen oder Punkte identisch
|
||||
(list p1 p2)))))
|
||||
|
||||
;; Planare Projektionsdistanz von neu-basis zu ziel-punkt entlang hz - siehe
|
||||
;; unten (unveraendert).
|
||||
(defun vfl-projiziere-distanz (neu-basis ziel-punkt hz / rad)
|
||||
(setq rad (* (float hz) (/ pi 180.0)))
|
||||
(+ (* (- (car ziel-punkt) (car neu-basis)) (cos rad))
|
||||
@@ -7006,9 +7426,11 @@
|
||||
)
|
||||
)
|
||||
;; ES-Fussabdruck VORAB reservieren, siehe Kommentar bei
|
||||
;; gf-kettenende-antwort oben.
|
||||
;; gf-kettenende-antwort oben. ein-laengs = Anteil ENTLANG
|
||||
;; der Fahrtrichtung (vf-element-laengs); (abs ein-dx) waere
|
||||
;; die Block-X-Komponente und damit viel zu klein.
|
||||
(if (= gf-kettenende-antwort "1")
|
||||
(setq deltaL (- deltaL (abs ein-dx))))
|
||||
(setq deltaL (- deltaL (vfl-es-laengs))))
|
||||
(if (< deltaL 1.0)
|
||||
(princ (ssg-text "vfl-fehler-linie-zu-kurz"))
|
||||
(progn
|
||||
@@ -7117,10 +7539,26 @@
|
||||
)
|
||||
(t
|
||||
;; --- Zweig A: Endpunkt waehlen (heutiges Verhalten + Separator) ---
|
||||
(setq linie-mess (vfl-neue-linie-messen p-aktuell
|
||||
(if frame (car (frame->hz-winkel frame)) nil)))
|
||||
;; Steht die Fahrtrichtung bereits fest (frame) UND endet die Kette
|
||||
;; mit einem ES-Element, ist die Laenge nicht mehr frei: dann wird
|
||||
;; eine ZIELLINIE angeklickt und der Schnittpunkt exakt gerechnet
|
||||
;; (Nutzer-Vorgabe 2026-10-09 - ein Klick trifft den Schnittpunkt
|
||||
;; nie genau). Trifft der Klick keine Linie, faellt der Ablauf auf
|
||||
;; die gewohnte Punktwahl zurueck, damit kein Weg verbaut wird.
|
||||
(setq hz-neu (if frame (car (frame->hz-winkel frame)) nil))
|
||||
(if (and hz-neu (= gf-kettenende-antwort "1"))
|
||||
;; Richtung steht fest UND die Kette endet mit ES -> Ziellinie.
|
||||
;; Rueckgabe ist die Gesamtlaenge bis zum Schnittpunkt; der
|
||||
;; Separator-/ES-Fussabdruck wird weiter unten wie gewohnt
|
||||
;; abgezogen.
|
||||
(progn
|
||||
(setq deltaL (vfl-in-abstand-ziellinie p-aktuell hz-neu
|
||||
(if (numberp ein-quer) ein-quer 0.0)))
|
||||
(if (null deltaL) (progn (princ (ssg-text "vfl-abgebrochen")) (exit))))
|
||||
(progn
|
||||
(setq linie-mess (vfl-neue-linie-messen p-aktuell hz-neu))
|
||||
(if (null linie-mess) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
|
||||
(setq deltaL (car linie-mess) hz-neu (cadr linie-mess))
|
||||
(setq deltaL (car linie-mess) hz-neu (cadr linie-mess))))
|
||||
(if (< deltaL 1.0)
|
||||
(princ (ssg-text "vfl-fehler-linie-zu-kurz"))
|
||||
(progn
|
||||
@@ -7188,7 +7626,7 @@
|
||||
;; anders als die optionalen vor/nach-Separatoren oben
|
||||
;; NICHT reserviert wurde).
|
||||
(if (= gf-kettenende-antwort "1")
|
||||
(setq deltaL (- deltaL (* *vfk-separator-laenge* (cos rad3)) (abs ein-dx))))
|
||||
(setq deltaL (- deltaL (* *vfk-separator-laenge* (cos rad3)) (vfl-es-laengs))))
|
||||
(if (< deltaL 1.0)
|
||||
(progn (princ (ssg-text "vfl-fehler-linie-zu-kurz")) (setq gf-ok nil)))
|
||||
(if gf-ok
|
||||
|
||||
@@ -152,6 +152,12 @@ kreisel_pinbereich = pinbereich, 3
|
||||
# Beschriftungstext des Kreisels (Laenge/Bezeichnung).
|
||||
kreisel_beschriftung = S_KRS_BESCHR, 7
|
||||
|
||||
# Marker der Kreisel-Beruehrpunkte (Weichen) und -Kollisionen, gezeichnet
|
||||
# vom Befehl KREISEL_WEICHEN (Lisp/KreiselInsert.lsp, kreisel-bp-*). Reine
|
||||
# Hilfsgeometrie, jeder Aufruf ersetzt die Marker des vorigen Laufs.
|
||||
kreisel_weiche = ZZ_Kreisel_Weichen, 4
|
||||
kreisel_kollision = ZZ_Kreisel_Kollision, 1
|
||||
|
||||
# Beschriftungstext der Vario-Foerderanlage (Hoehen-/Sektionslabel).
|
||||
vf_beschriftung = VF_Beschriftung, 7
|
||||
|
||||
|
||||
+18
-7
@@ -64,13 +64,16 @@
|
||||
# Code (bedingte Umbenennung bzw. Umrechnung mm->m).
|
||||
|
||||
[eckrad]
|
||||
# Dieselben Sivas-Merkmalnummern wie bei Kreisel (siehe oben), aus
|
||||
# Merkmalsstamm_in_Sivas.xlsx: 3992 "Geruest fuer Einzelmodul", 3993
|
||||
# "Geruestoption fuer Einzelmodul". "ist_eins" liefert ein ECHTES bool
|
||||
# (True/False), NICHT "Ja"/"Nein" wie ja_nein bei Kreisel/Gefaellestrecke -
|
||||
# json.dumps schreibt dann true/false ohne Anfuehrungszeichen (bisheriges
|
||||
# Verhalten von build_eckrad_details).
|
||||
3992= GERUEST_EINZELMODUL : 0 | ist_eins
|
||||
# Am Kreisel ausgerichtet, aber nur Merkmale, fuer die das Eckrad-Objekt ein
|
||||
# Attribut hat (*eckrad-attrib-defs*); Merkmalnummern aus
|
||||
# Merkmalsstamm_in_Sivas.xlsx. Hoehe: beim Eckrad 2950 "Anlagenhoehe in
|
||||
# Meter" (NICHT 2952 "Kreiselhoehe" wie beim Kreisel), Wert aus HOEHE (mm),
|
||||
# da das Eckrad kein MONTAGEHOEHE_m traegt. 3381 "Eckrad - zu
|
||||
# fertigende Menge" steht wie 3511 beim Kreisel fest auf 1 (reale Stueckzahl
|
||||
# in der Anzahl-Spalte). Separatoren (3382/3383) hat das Eckrad nicht.
|
||||
2950= HOEHE : 0 | mm_zu_m
|
||||
3381= : 1
|
||||
3992= GERUEST_EINZELMODUL : 0 | ja_nein
|
||||
3993= GERUEST_TYP : Schoenenberger Geruest | geruest_typ_anzeige
|
||||
|
||||
[geruest]
|
||||
@@ -104,3 +107,11 @@ Farbe= FARBE
|
||||
# ASCII-Attributwert (GERUEST_TYP) -> Sivas-Klartext mit Umlauten/Sonder-
|
||||
# zeichen (Attribute selbst bleiben ASCII, siehe CLAUDE.md-Konvention).
|
||||
Schoenenberger Geruest= Schönenberger-Gerüst
|
||||
|
||||
[weichen]
|
||||
# ILS 2.0 Weiche (6216): eine Summenzeile, Werte aus den Kreisel-Beruehr-
|
||||
# punkten des LISP-Exports (kreisel_weichen.json). Ein Beruehrpunkt ist eine
|
||||
# Weichenkombination = 1x aktiv + 1x passiv, darum lesen beide Merkmale
|
||||
# dieselbe Zahl ANZAHL_WEICHEN (= Anzahl Beruehrpunkte, siehe export_sivas.py).
|
||||
3371= ANZAHL_WEICHEN : 0
|
||||
3372= ANZAHL_WEICHEN : 0
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
"color_pin": 3,
|
||||
"toleranz_kollinear": 0.01,
|
||||
"toleranz_winkel": 1.0,
|
||||
"toleranz_min_distanz": 0.001
|
||||
"toleranz_min_distanz": 0.001,
|
||||
"toleranz_beruehrung": 1.0,
|
||||
"toleranz_hoehe": 1.0,
|
||||
"weiche_marker_radius": 50.0
|
||||
},
|
||||
"eckrad": {
|
||||
"default_hoehe": 2000.0
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,54 @@
|
||||
// ============================================================
|
||||
// eckrad_edit.dcl - Eckrad Parameter einfuegen/bearbeiten
|
||||
// Wie kreisel_edit.dcl, aber ohne Abstand und Typ (ein Eckrad
|
||||
// hat keine Laenge und keine Kreiselart). Die Drehung ist ein
|
||||
// freier Winkel (aus dem Richtungsklick), daher edit_box statt
|
||||
// der festen Kreisel-Ausrichtungen.
|
||||
// ============================================================
|
||||
|
||||
eckrad_edit : dialog {
|
||||
label = "Eckrad";
|
||||
|
||||
: column {
|
||||
|
||||
: edit_box {
|
||||
key = "name";
|
||||
label = "Bezeichnung:";
|
||||
edit_width = 30;
|
||||
}
|
||||
|
||||
: row {
|
||||
: edit_box {
|
||||
key = "hoehe";
|
||||
label = "Hoehe (mm):";
|
||||
edit_width = 10;
|
||||
}
|
||||
: edit_box {
|
||||
key = "drehung";
|
||||
label = "Drehung (Grad):";
|
||||
edit_width = 10;
|
||||
}
|
||||
}
|
||||
|
||||
: popup_list {
|
||||
key = "drehrichtung";
|
||||
label = "Motordrehrichtung:";
|
||||
width = 30;
|
||||
}
|
||||
|
||||
: toggle {
|
||||
key = "geruest_einzelmodul";
|
||||
label = "Geruest fuer Einzelmodul";
|
||||
}
|
||||
|
||||
: popup_list {
|
||||
key = "geruest_typ";
|
||||
label = "Geruestoption:";
|
||||
width = 30;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ok_cancel;
|
||||
errtile;
|
||||
}
|
||||
+21
-1
@@ -99,6 +99,13 @@
|
||||
"vfl-es-setzen-frage": "\n\nES-Element setzen?",
|
||||
"vfl-es-nein-zielpunkt": "\n 2 - Nein (Kette endet direkt am Zielpunkt)",
|
||||
"vfl-prompt-endpunkt-fahrtrichtung": "\n\nEndpunkt entlang Fahrtrichtung waehlen (bestimmt die Laenge): ",
|
||||
"vfl-endpunkt-modus-header": "\n\nWie soll der Endpunkt bestimmt werden?",
|
||||
"vfl-endpunkt-modus-opt-linie": "\n 1 - Ziellinie zeichnen (2 Punkte) - der Schnittpunkt bestimmt die Laenge",
|
||||
"vfl-prompt-ziellinie-p1": "\n\nZiellinie - 1. Punkt (beliebige Richtung, Z wird ignoriert): ",
|
||||
"vfl-prompt-ziellinie-p2": "\n Ziellinie - 2. Punkt: ",
|
||||
"vfl-endpunkt-modus-opt-punkt": "\n 2 - Punkt waehlen - entlang der festen Fahrtrichtung",
|
||||
"vfl-ziellinie-parallel": "\n Die gewaehlte Linie liegt parallel zur Fahrtrichtung - es gibt keinen eindeutigen Schnittpunkt.",
|
||||
"vfl-ziellinie-hinter-start": "\n Der Schnittpunkt liegt hinter dem Startpunkt der Strecke - bitte eine andere Linie waehlen.",
|
||||
"vfl-prompt-endpunkt-frei": "\n\nEndpunkt der Linie (XY, beliebige Richtung): ",
|
||||
"vfl-fehler-laenge-max": "\n>>> FEHLER: Laenge %1 m ueberschreitet die Foerderer-Maximallaenge von 25 m - bitte anderen Endpunkt waehlen.",
|
||||
"vfl-glied-gf-bogen": "GF-Bogen (horizontale Kurve)",
|
||||
@@ -469,6 +476,11 @@
|
||||
"kreisel-quick-fertig": "\nSchnell Einfuegen fertig.",
|
||||
"kreisel-start-connect": "Kreisel Smart Connect",
|
||||
"kreisel-start-eckrad": "Eckrad einfuegen",
|
||||
"kreisel-start-eckrad-edit": "Eckrad bearbeiten",
|
||||
"kreisel-prompt-eckrad-auswaehlen": "\nEckrad-Block auswaehlen: ",
|
||||
"kreisel-emsg-kein-eckrad-block": "Kein Eckrad-Block! (Blockname ECKRAD_* erwartet)",
|
||||
"kreisel-eckrad-ungueltige-zahl": "Hoehe und Drehung muessen Zahlen sein.",
|
||||
"kreisel-eckrad-edit-aktualisiert": "\nEckrad aktualisiert: Name=%1 Hoehe=%2 Rotation=%3",
|
||||
"kreisel-start-edit": "Kreisel bearbeiten",
|
||||
"kreisel-start-insert": "Kreisel Modul einfuegen",
|
||||
"kreisel-start-quick": "Kreisel Schnell Einfuegen",
|
||||
@@ -674,6 +686,7 @@
|
||||
"plq-geloescht": "\n[DELETE_PLANQUADRATE] Planquadrat-Raster entfernt (%1 Einfuegung(en)).",
|
||||
"plq-nichts-zu-loeschen": "\n[DELETE_PLANQUADRATE] Kein Planquadrat-Raster in der Zeichnung vorhanden.",
|
||||
"exp-check-ids": "\n[%1] Pruefe und vergebe IDs...",
|
||||
"exp-hoehen-aktualisiert": "\nHoehen-Attribute von %1 verschobenen Bloecken aus der Z-Koordinate neu gesetzt.",
|
||||
"exp-sep-proxy-fehler": "\n[EXPORT] WARNUNG: Kopie von '%1' aus Block '%2' fehlgeschlagen (%3) - dieses Element fehlt im Export.",
|
||||
"exp-sep-zuordnung": "\n[%1] verpackte Separator-Kopie(n): ZUORDNUNG aus der ID des Wrapper-Blocks gesetzt.",
|
||||
"exp-sep-zuordnung-ohne-id": "\n[%1] verpackte Separator-Kopie(n) ohne Wrapper-ID - ZUORDNUNG wird geometrisch bestimmt.",
|
||||
@@ -1102,6 +1115,13 @@
|
||||
"sl-erk-fahrstrecke": "Omniflo Fahrstrecke",
|
||||
"sl-erk-ap60": "Omniflo Gerade AP60",
|
||||
"sl-erk-ap110": "Omniflo Gerade AP110",
|
||||
"sl-erk-apg110": "Omniflo Gerade APG110"
|
||||
"sl-erk-apg110": "Omniflo Gerade APG110",
|
||||
"kreisel-bp-start": "Kreisel-Weichenpunkte",
|
||||
"kreisel-bp-summe": "\n=== Kreisel-Weichenpunkte: %1 Beruehrung(en), %2 Kollision(en) (Toleranz %3 mm) ===",
|
||||
"kreisel-bp-zeile": "\n Weiche %1: %2 <-> %3 [%4] bei X=%5 Y=%6 Z=%7",
|
||||
"kreisel-bp-kollision": "\n KOLLISION %1: %2 <-> %3 [%4] bei X=%5 Y=%6 Z=%7",
|
||||
"kreisel-bp-marker": "\n%1 Marker gezeichnet (Layer %2).",
|
||||
"exp-kreisel-weichen": "\n[%1] Kreisel-Weichenpunkte: %2 Beruehrung(en), %3 Kollision(en) -> %4",
|
||||
"exp-kreisel-kollision": "\n[%1] WARNUNG: %2 Kreisel-Kollision(en) - Befehl KREISEL_WEICHEN zeigt sie an."
|
||||
}
|
||||
}
|
||||
|
||||
+21
-1
@@ -99,6 +99,13 @@
|
||||
"vfl-es-setzen-frage": "\n\nPlace ES element?",
|
||||
"vfl-es-nein-zielpunkt": "\n 2 - No (chain ends directly at the target point)",
|
||||
"vfl-prompt-endpunkt-fahrtrichtung": "\n\nSelect end point along travel direction (defines the length): ",
|
||||
"vfl-endpunkt-modus-header": "\n\nHow should the end point be determined?",
|
||||
"vfl-endpunkt-modus-opt-linie": "\n 1 - Draw target line (2 points) - the intersection defines the length",
|
||||
"vfl-prompt-ziellinie-p1": "\n\nTarget line - 1st point (any direction, Z is ignored): ",
|
||||
"vfl-prompt-ziellinie-p2": "\n Target line - 2nd point: ",
|
||||
"vfl-endpunkt-modus-opt-punkt": "\n 2 - Select point - along the fixed travel direction",
|
||||
"vfl-ziellinie-parallel": "\n The selected line is parallel to the travel direction - there is no unique intersection.",
|
||||
"vfl-ziellinie-hinter-start": "\n The intersection lies behind the start of the run - please select a different line.",
|
||||
"vfl-prompt-endpunkt-frei": "\n\nEnd point of the line (XY, any direction): ",
|
||||
"vfl-fehler-laenge-max": "\n>>> ERROR: Length %1 m exceeds the conveyor maximum length of 25 m - please pick another end point.",
|
||||
"vfl-glied-gf-bogen": "GF arc (horizontal curve)",
|
||||
@@ -469,6 +476,11 @@
|
||||
"kreisel-quick-fertig": "\nQuick insert done.",
|
||||
"kreisel-start-connect": "Kreisel Smart Connect",
|
||||
"kreisel-start-eckrad": "Insert Eckrad",
|
||||
"kreisel-start-eckrad-edit": "Edit Eckrad",
|
||||
"kreisel-prompt-eckrad-auswaehlen": "\nSelect Eckrad block: ",
|
||||
"kreisel-emsg-kein-eckrad-block": "Not an Eckrad block! (block name ECKRAD_* expected)",
|
||||
"kreisel-eckrad-ungueltige-zahl": "Height and rotation must be numbers.",
|
||||
"kreisel-eckrad-edit-aktualisiert": "\nEckrad updated: Name=%1 Height=%2 Rotation=%3",
|
||||
"kreisel-start-edit": "Edit Kreisel",
|
||||
"kreisel-start-insert": "Insert Kreisel module",
|
||||
"kreisel-start-quick": "Kreisel quick insert",
|
||||
@@ -674,6 +686,7 @@
|
||||
"plq-geloescht": "\n[DELETE_PLANQUADRATE] Planquadrat grid removed (%1 insertion(s)).",
|
||||
"plq-nichts-zu-loeschen": "\n[DELETE_PLANQUADRATE] No Planquadrat grid found in the drawing.",
|
||||
"exp-check-ids": "\n[%1] Checking and assigning IDs...",
|
||||
"exp-hoehen-aktualisiert": "\nHeight attributes of %1 moved blocks reset from their Z coordinate.",
|
||||
"exp-sep-proxy-fehler": "\n[EXPORT] WARNING: copying '%1' out of block '%2' failed (%3) - this element is missing from the export.",
|
||||
"exp-sep-zuordnung": "\n[%1] packed separator copy/copies: ZUORDNUNG set from the wrapper block ID.",
|
||||
"exp-sep-zuordnung-ohne-id": "\n[%1] packed separator copy/copies without a wrapper ID - ZUORDNUNG determined geometrically.",
|
||||
@@ -1102,6 +1115,13 @@
|
||||
"sl-erk-fahrstrecke": "Omniflo track",
|
||||
"sl-erk-ap60": "Omniflo straight AP60",
|
||||
"sl-erk-ap110": "Omniflo straight AP110",
|
||||
"sl-erk-apg110": "Omniflo straight APG110"
|
||||
"sl-erk-apg110": "Omniflo straight APG110",
|
||||
"kreisel-bp-start": "Kreisel switch points",
|
||||
"kreisel-bp-summe": "\n=== Kreisel switch points: %1 contact(s), %2 collision(s) (tolerance %3 mm) ===",
|
||||
"kreisel-bp-zeile": "\n Switch %1: %2 <-> %3 [%4] at X=%5 Y=%6 Z=%7",
|
||||
"kreisel-bp-kollision": "\n COLLISION %1: %2 <-> %3 [%4] at X=%5 Y=%6 Z=%7",
|
||||
"kreisel-bp-marker": "\n%1 markers drawn (layer %2).",
|
||||
"exp-kreisel-weichen": "\n[%1] Kreisel switch points: %2 contact(s), %3 collision(s) -> %4",
|
||||
"exp-kreisel-kollision": "\n[%1] WARNING: %2 Kreisel collision(s) - command KREISEL_WEICHEN shows them."
|
||||
}
|
||||
}
|
||||
|
||||
+18
-2
@@ -40,6 +40,8 @@ from export_neighbors import (
|
||||
compute_sensor_zuordnung,
|
||||
compute_scanner_nearest_separator,
|
||||
compute_kreisel_touch_switches,
|
||||
lisp_touch_switches,
|
||||
load_lisp_kreisel_weichen,
|
||||
compute_sep_kette,
|
||||
compute_kreisel_umlauf,
|
||||
SENSOR_TEILEARTEN,
|
||||
@@ -952,7 +954,9 @@ def pruefe_ases_in_beiden_containern(items, dbg=None):
|
||||
return auffaellig
|
||||
|
||||
|
||||
def process_blocks(blocks, lookup, dbg=None):
|
||||
def process_blocks(blocks, lookup, dbg=None, lisp_weichen=None):
|
||||
# lisp_weichen = Inhalt von kreisel_weichen.json (load_lisp_kreisel_weichen)
|
||||
# oder None - dann werden die Kreisel-Beruehrpunkte hier selbst gerechnet.
|
||||
# x_origin_mm/y_origin_mm einmalig fuer diesen Exportlauf aufloesen (siehe
|
||||
# export_planquadrat.resolve_origins): automatisch aus den Bloecken, falls
|
||||
# nicht explizit in [Planquadrate] konfiguriert.
|
||||
@@ -1535,6 +1539,12 @@ def process_blocks(blocks, lookup, dbg=None):
|
||||
# compute_kreisel_umlauf (damit die Weiche als Verzweigungspunkt in
|
||||
# BEIDEN beruehrenden Kreisel-Umlaeufen auftaucht) als auch in
|
||||
# build_kreisel_weiche_items (das die tatsaechlichen Zeilen erzeugt).
|
||||
# Quelle ist der LISP-Export (kreisel_weichen.json: Kreisel UND Eckrad,
|
||||
# nur gleiche Hoehe, Kollisionen getrennt); fehlt die Datei, rechnet
|
||||
# compute_kreisel_touch_switches wie bisher (nur Kreisel, reine XY-Pruefung).
|
||||
if lisp_weichen is not None:
|
||||
touch_switches = lisp_touch_switches(lisp_weichen, element_box_mm=ELEMENT_BOX_MM, dbg=dbg)
|
||||
else:
|
||||
touch_switches = compute_kreisel_touch_switches(
|
||||
items, NEIGHBOR_TOLERANCE_MM, durchmesser_mm=KREISEL_DURCHMESSER_MM,
|
||||
element_box_mm=ELEMENT_BOX_MM, dbg=dbg)
|
||||
@@ -1693,7 +1703,13 @@ def main():
|
||||
dbg_file = None
|
||||
dbg = None
|
||||
|
||||
items = process_blocks(blocks, lookup, dbg=dbg)
|
||||
lisp_weichen = load_lisp_kreisel_weichen(raw_json_path)
|
||||
if lisp_weichen is not None:
|
||||
print(f"[export_csv] Kreisel-Weichen aus LISP: "
|
||||
f"{len(lisp_weichen['beruehrungen'])} Beruehrung(en), "
|
||||
f"{len(lisp_weichen['kollisionen'])} Kollision(en).")
|
||||
|
||||
items = process_blocks(blocks, lookup, dbg=dbg, lisp_weichen=lisp_weichen)
|
||||
|
||||
if dbg_file:
|
||||
dbg_file.close()
|
||||
|
||||
@@ -47,6 +47,7 @@ MIN_PARTNER) - Ergebnis ist die CSV-Spalte "Fehler" in export_csv.py.
|
||||
Nur von export_csv.py genutzt (EXPORTCSV), nicht von export_sivas.py.
|
||||
"""
|
||||
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
|
||||
@@ -1091,6 +1092,84 @@ def compute_kreisel_touch_switches(items, tolerance_mm, durchmesser_mm=800.0,
|
||||
return switches
|
||||
|
||||
|
||||
# Dateiname der von LISP (csv:kreisel-weichen-schreiben, export.lsp) neben
|
||||
# export_raw.json geschriebenen Kreisel-Beruehrpunkte.
|
||||
LISP_KREISEL_WEICHEN_DATEI = "kreisel_weichen.json"
|
||||
|
||||
|
||||
def load_lisp_kreisel_weichen(raw_json_path):
|
||||
"""Liest kreisel_weichen.json aus dem Ordner von raw_json_path.
|
||||
|
||||
Die Datei schreibt der LISP-Export (KreiselInsert.lsp kreisel-bp-*):
|
||||
Kreisel UND Eckraeder, nur gleiche Hoehe, mehrere Punkte je Paar moeglich,
|
||||
Ueberschneidungen getrennt als "kollisionen". Rueckgabe: dict mit
|
||||
"beruehrungen"/"kollisionen" oder None, wenn die Datei fehlt/unlesbar ist
|
||||
(dann rechnet der Aufrufer selbst - compute_kreisel_touch_switches)."""
|
||||
pfad = os.path.join(os.path.dirname(os.path.abspath(raw_json_path)),
|
||||
LISP_KREISEL_WEICHEN_DATEI)
|
||||
if not os.path.exists(pfad):
|
||||
return None
|
||||
try:
|
||||
with open(pfad, encoding="utf-8", errors="replace") as f:
|
||||
daten = json.load(f)
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
if not isinstance(daten, dict):
|
||||
return None
|
||||
daten.setdefault("beruehrungen", [])
|
||||
daten.setdefault("kollisionen", [])
|
||||
return daten
|
||||
|
||||
|
||||
def lisp_touch_switches(lisp_daten, element_box_mm=None, dbg=None):
|
||||
"""Baut aus den LISP-Beruehrpunkten (load_lisp_kreisel_weichen) dieselben
|
||||
Switch-dicts wie compute_kreisel_touch_switches - damit laufen
|
||||
compute_kreisel_umlauf und build_kreisel_weiche_items unveraendert.
|
||||
|
||||
Partner-ID = ID-Attribut (= teileid), ohne ID der Handle. Mehrere Punkte
|
||||
eines Paares bekommen eindeutige Schluessel (WEICHE#a#b, WEICHE#a#b#2, ...).
|
||||
Die Box wird an achse_rot_grad (Radiale des beruehrenden Rades) ausgerichtet.
|
||||
Kollisionen werden NICHT zu Weichen, nur ins Debuglog geschrieben."""
|
||||
if element_box_mm is None:
|
||||
element_box_mm = load_element_box_mm()
|
||||
box_l, box_b, box_h = element_box_mm
|
||||
switches = []
|
||||
keys = set()
|
||||
if dbg:
|
||||
dbg("")
|
||||
dbg("=== Kreisel-Beruehrpunkte -> ILS-Weichen (aus LISP kreisel_weichen.json) ===")
|
||||
for b in lisp_daten.get("beruehrungen", []):
|
||||
id_a = b.get("a") or b.get("a_handle", "")
|
||||
id_b = b.get("b") or b.get("b_handle", "")
|
||||
key = f"WEICHE#{id_a}#{id_b}"
|
||||
n = 1
|
||||
while key in keys:
|
||||
n += 1
|
||||
key = f"WEICHE#{id_a}#{id_b}#{n}"
|
||||
keys.add(key)
|
||||
dx, dy, dz = _oriented_box_aabb(math.radians(float(b.get("achse_rot_grad", 0.0))),
|
||||
box_l, box_b, box_h)
|
||||
sw = {
|
||||
"key": key,
|
||||
"x": float(b.get("x", 0.0)), "y": float(b.get("y", 0.0)), "z": float(b.get("z", 0.0)),
|
||||
"dx": dx, "dy": dy, "dz": dz,
|
||||
"nachbarn": [id_a, id_b],
|
||||
"achse_von": b.get("achse_von", "") or id_a,
|
||||
"beruehrung": b.get("art", ""),
|
||||
}
|
||||
switches.append(sw)
|
||||
if dbg:
|
||||
dbg(f" {id_a} <-> {id_b}: {sw['beruehrung']} ({b.get('detail', '')}) "
|
||||
f"bei ({sw['x']:.0f}, {sw['y']:.0f}, {sw['z']:.0f})")
|
||||
if dbg:
|
||||
for k in lisp_daten.get("kollisionen", []):
|
||||
dbg(f" KOLLISION {k.get('a', '')} <-> {k.get('b', '')}: {k.get('art', '')} "
|
||||
f"({k.get('detail', '')}) bei ({float(k.get('x', 0)):.0f}, "
|
||||
f"{float(k.get('y', 0)):.0f})")
|
||||
dbg(f"Erzeugte ILS-Weichen: {len(switches)}")
|
||||
return switches
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Kreisel <-> Strecken-Enden: verfeinerte Kollisionsbox fuer compute_neighbor_
|
||||
# ids (die "Nachbarn"-Spalte einer GF/VF-Strecke soll nur an ihrem echten AS-/
|
||||
|
||||
+55
-17
@@ -63,6 +63,7 @@ from export_blockpatterns import (load_patterns, load_export_cfg, cfg_path_from_
|
||||
matches_any, csv_quote, safe_float, safe_int)
|
||||
from merkmalstamm import lade_merkmalstamm, bezeichnung_fuer
|
||||
import sivas_mapping
|
||||
from export_neighbors import load_lisp_kreisel_weichen
|
||||
|
||||
BLOCKPATTERNS = load_patterns()
|
||||
|
||||
@@ -113,17 +114,17 @@ def ja_nein(flag_1_0):
|
||||
return "Ja" if flag_1_0 == "1" else "Nein"
|
||||
|
||||
|
||||
def ist_eins(flag_1_0):
|
||||
"""Wandelt ein "0"/"1"-Attribut in ein echtes bool um (True/False im
|
||||
JSON, NICHT "Ja"/"Nein" wie ja_nein - siehe build_eckrad_details)."""
|
||||
return flag_1_0 == "1"
|
||||
def mm_zu_m(wert_mm):
|
||||
"""Wandelt ein mm-Attribut (z.B. Eckrad-HOEHE) in Meter um, gerundet wie
|
||||
3512 beim Kreisel ("2000" -> "2.0")."""
|
||||
return str(round(safe_float(wert_mm, 0.0) / 1000.0, 3))
|
||||
|
||||
|
||||
# --- Transform-Namen, wie sie in cfg/sivas.cfg hinter "|" stehen koennen ---
|
||||
TRANSFORMS = {
|
||||
"ja_nein": ja_nein,
|
||||
"geruest_typ_anzeige": geruest_typ_anzeige,
|
||||
"ist_eins": ist_eins,
|
||||
"mm_zu_m": mm_zu_m,
|
||||
}
|
||||
|
||||
|
||||
@@ -157,13 +158,15 @@ def build_gefaellestrecke_details(block, anzahl=1):
|
||||
|
||||
def kreisel_signature(block):
|
||||
"""
|
||||
Schluessel zur Gruppierung gleichartiger Kreisel: Kreiselart, Abstand,
|
||||
Anzahl der Separatoren, Anzahl der Scanner, Anzahl der Rampen sowie
|
||||
Geruest fuer Einzelmodul/Geruestoption entscheiden, ob Kreisel in
|
||||
derselben Sivas-Zeile summiert werden.
|
||||
Schluessel zur Gruppierung gleichartiger Kreisel: Montagehoehe (Merkmal
|
||||
2952), Kreiselart, Abstand, Anzahl der Separatoren, Anzahl der Scanner,
|
||||
Anzahl der Rampen sowie Geruest fuer Einzelmodul/Geruestoption
|
||||
entscheiden, ob Kreisel in derselben Sivas-Zeile summiert werden.
|
||||
Die Hoehe wird numerisch verglichen ("4.820" == "4.82").
|
||||
"""
|
||||
attribs = block.get("attribs", {})
|
||||
return (
|
||||
round(safe_float(attribs.get("MONTAGEHOEHE_m", "0"), 0.0), 3),
|
||||
attribs.get("KREISELART", "STANDARD"),
|
||||
attribs.get("ABSTAND", "2300"),
|
||||
attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")),
|
||||
@@ -176,12 +179,13 @@ def kreisel_signature(block):
|
||||
|
||||
def eckrad_signature(block):
|
||||
"""
|
||||
Schluessel zur Gruppierung gleichartiger Eckraeder: Geruest fuer
|
||||
Einzelmodul/Geruestoption entscheiden, ob Eckraeder in derselben
|
||||
Sivas-Zeile summiert werden.
|
||||
Schluessel zur Gruppierung gleichartiger Eckraeder: Hoehe (Merkmal 2950)
|
||||
und Geruest fuer Einzelmodul/Geruestoption entscheiden, ob Eckraeder in
|
||||
derselben Sivas-Zeile summiert werden.
|
||||
"""
|
||||
attribs = block.get("attribs", {})
|
||||
return (
|
||||
mm_zu_m(attribs.get("HOEHE", "0")),
|
||||
attribs.get("GERUEST_EINZELMODUL", "0"),
|
||||
attribs.get("GERUEST_TYP", "Schoenenberger Geruest"),
|
||||
)
|
||||
@@ -294,6 +298,7 @@ def build_variofoerderer_details(block):
|
||||
SIVAS_MAPPING_KREISEL = sivas_mapping.load_section_mapping("kreisel")
|
||||
SIVAS_MAPPING_ECKRAD = sivas_mapping.load_section_mapping("eckrad")
|
||||
SIVAS_MAPPING_GERUEST = sivas_mapping.load_section_mapping("geruest")
|
||||
SIVAS_MAPPING_WEICHEN = sivas_mapping.load_section_mapping("weichen")
|
||||
|
||||
|
||||
def build_kreisel_details(block):
|
||||
@@ -315,8 +320,8 @@ def build_kreisel_details(block):
|
||||
|
||||
|
||||
def build_eckrad_details(block):
|
||||
"""Merkmale-Dict fuer ein ILS Eckrad. Vollstaendig aus cfg/sivas.cfg
|
||||
[eckrad] (keine berechneten Werte)."""
|
||||
"""Merkmale-Dict fuer eine Gruppe gleichartiger ILS Eckraeder.
|
||||
Vollstaendig aus cfg/sivas.cfg [eckrad] (2950 per Transform mm_zu_m)."""
|
||||
attribs = block.get("attribs", {})
|
||||
return sivas_mapping.anwenden(SIVAS_MAPPING_ECKRAD, attribs, TRANSFORMS)
|
||||
|
||||
@@ -411,7 +416,9 @@ ILS_SIVASNR = {
|
||||
}
|
||||
|
||||
|
||||
def process_blocks(blocks):
|
||||
def process_blocks(blocks, anzahl_weichen_ils=0):
|
||||
# anzahl_weichen_ils = Anzahl Kreisel-Beruehrpunkte aus dem LISP-Export
|
||||
# (kreisel_weichen.json, siehe main) -> Merkmal 4403 der Automation-Zeile.
|
||||
"""
|
||||
Verarbeitet alle Bloecke.
|
||||
|
||||
@@ -445,7 +452,7 @@ def process_blocks(blocks):
|
||||
"sep_kreisel_ohne_pin": 0,
|
||||
"anzahl_verbindungskreisel": 0,
|
||||
"laengen_kreisel": 0.0,
|
||||
"anzahl_weichen_ils": 0,
|
||||
"anzahl_weichen_ils": anzahl_weichen_ils,
|
||||
"anzahl_eckraeder": 0,
|
||||
"anzahl_gefaellestrecken": 0,
|
||||
"anzahl_variofoerderer": 0,
|
||||
@@ -823,6 +830,30 @@ def process_blocks(blocks):
|
||||
})
|
||||
nr += 1
|
||||
|
||||
# ILS 2.0 Weiche (6216): EINE Summenzeile fuer alle Kreisel-Beruehrpunkte
|
||||
# (kreisel_weichen.json des LISP-Exports, Anzahl = anzahl_weichen_ils).
|
||||
# Ein Beruehrpunkt = eine Weichenkombination aus 1x aktiv (3371) + 1x
|
||||
# passiv (3372); die Zuordnung Merkmal -> Wert steht in sivas.cfg
|
||||
# [weichen], beide lesen ANZAHL_WEICHEN. Anzahl-Spalte 1 wie bei der
|
||||
# Automation-Zeile - die Gesamtanzahlen stecken in den Merkmalen.
|
||||
if counters["anzahl_weichen_ils"] > 0:
|
||||
weiche_sivasnr, weiche_teileart = ILS_SIVASNR["ils_weiche"]
|
||||
items.append({
|
||||
"nr": nr,
|
||||
"teileart": weiche_teileart,
|
||||
"sivasnr": weiche_sivasnr,
|
||||
"sivasnr_quoted": True,
|
||||
"bezeichnung": "",
|
||||
"dispgruppe": 20,
|
||||
"anzahl": 1,
|
||||
"laenge_mm": "",
|
||||
"details": sivas_mapping.anwenden(
|
||||
SIVAS_MAPPING_WEICHEN,
|
||||
{"ANZAHL_WEICHEN": str(counters["anzahl_weichen_ils"])},
|
||||
TRANSFORMS),
|
||||
})
|
||||
nr += 1
|
||||
|
||||
# ILS Automation Summierungszeile (nur wenn ILS-Elemente vorhanden)
|
||||
has_ils = (counters["anzahl_kreisel"] > 0
|
||||
or counters["anzahl_eckraeder"] > 0
|
||||
@@ -983,7 +1014,14 @@ def main():
|
||||
blocks = load_json(raw_json_path)
|
||||
print(f"[export_sivas] {len(blocks)} Bloecke geladen.")
|
||||
|
||||
items, _ = process_blocks(blocks)
|
||||
# Kreisel-Beruehrpunkte (= ILS-Weichen) zaehlt der LISP-Export
|
||||
# (kreisel_weichen.json neben der Raw-Datei); fehlt sie, bleibt 4403 = 0.
|
||||
lisp_weichen = load_lisp_kreisel_weichen(raw_json_path)
|
||||
anzahl_weichen = len(lisp_weichen["beruehrungen"]) if lisp_weichen else 0
|
||||
if lisp_weichen is not None:
|
||||
print(f"[export_sivas] Kreisel-Weichen aus LISP: {anzahl_weichen}")
|
||||
|
||||
items, _ = process_blocks(blocks, anzahl_weichen_ils=anzahl_weichen)
|
||||
|
||||
header = "Nr;TeileArt;SivasNummer;Bezeichnung;Dispogruppe;Anzahl;Laenge[mm];IDs;details"
|
||||
with open(output_csv, "w", encoding="utf-8") as f:
|
||||
|
||||
@@ -109,9 +109,7 @@ def anwenden(section_mapping, attribs, transforms):
|
||||
Rueckgabe: dict merkmal -> wert. Ohne Transform wird der Rohwert zu str()
|
||||
gewandelt (bisheriges Verhalten der build_*_details-Funktionen: alles
|
||||
Merkmalnummer-Werte sind Strings). Ein Transform liefert seinen
|
||||
Rueckgabewert UNVERAENDERT (z.B. build_eckrad_details erwartet fuer
|
||||
"Geruest fuer Einzelmodul" ein echtes bool, kein "True"/"False"-String -
|
||||
json.dumps schreibt dann true/false statt eines Strings).
|
||||
Rueckgabewert UNVERAENDERT (er ist selbst fuer den Typ verantwortlich).
|
||||
"""
|
||||
ergebnis = {}
|
||||
for merkmal, (attribut, alt_attribut, default, transform) in section_mapping.items():
|
||||
|
||||
+499
-462
File diff suppressed because it is too large
Load Diff
+5
-1
@@ -7,7 +7,10 @@
|
||||
[Einfuegen]^C^C(ssg-ensure "KreiselInsert") KreiselInsert
|
||||
[Verbinden]^C^C(ssg-ensure "KreiselInsert") KreiselConnect
|
||||
[Bearbeiten]^C^C(ssg-ensure "KreiselInsert") KreiselEdit
|
||||
[<-Eckrad]^C^C(ssg-ensure "KreiselInsert") ILS_Eckrad
|
||||
[Eckrad]^C^C(ssg-ensure "KreiselInsert") ILS_Eckrad
|
||||
[Eckrad bearbeiten]^C^C(ssg-ensure "KreiselInsert") EckradEdit
|
||||
[--]
|
||||
[<-Weichenpunkte ermitteln]^C^C(ssg-ensure "KreiselInsert") KREISEL_WEICHEN
|
||||
[->Zusatzmodul]
|
||||
[BTMT-Beladung]^C^C(ssg-ensure "SSG_LIB_Commands") ILS_BTMT_Beladung
|
||||
[BTMT-Entladung]^C^C(ssg-ensure "SSG_LIB_Commands") ILS_BTMT_Entladung
|
||||
@@ -197,6 +200,7 @@
|
||||
***POP501
|
||||
[SSG_LIB Edit Context]
|
||||
[Kreisel bearbeiten](ssg-ensure "KreiselInsert") KreiselEdit
|
||||
[Eckrad bearbeiten](ssg-ensure "KreiselInsert") EckradEdit
|
||||
[Omniflo Element bearbeiten](ssg-ensure "OmniModulInsert") OMNI_EDIT
|
||||
[Gefaellestrecke bearbeiten](ssg-ensure "Gefaellestrecke") GEFAELLESTRECKE_EDIT
|
||||
[VarioFoerderer bearbeiten](ssg-ensure "VarioFoerderer") VARIOFOERDERER_EDIT
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_export_sivas_eckrad.py - Sivas-Merkmale des ILS Eckrads (6210),
|
||||
am Kreisel ausgerichtet (cfg/sivas.cfg [eckrad]).
|
||||
Laeuft komplett ohne BricsCAD (reine Python-Logik).
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
|
||||
|
||||
import export_sivas # noqa: E402
|
||||
|
||||
|
||||
def _eckrad(nr, hoehe="2000", einzelmodul="1", typ="Schoenenberger Geruest"):
|
||||
return {"block_name": f"ECKRAD_{nr}", "x": 0.0, "y": 0.0, "z": float(hoehe),
|
||||
"rotation": 0.0,
|
||||
"attribs": {"ID": f"000{nr}", "Bezeichnung": f"Eckrad {nr}",
|
||||
"NUMMER": str(nr), "HOEHE": hoehe,
|
||||
"GERUEST_EINZELMODUL": einzelmodul, "GERUEST_TYP": typ}}
|
||||
|
||||
|
||||
def test_eckrad_details_wie_kreisel():
|
||||
details = export_sivas.build_eckrad_details(_eckrad(1))
|
||||
assert details == {"2950": "2.0", "3381": "1", "3992": "Ja",
|
||||
"3993": export_sivas.geruest_typ_anzeige("Schoenenberger Geruest")}
|
||||
|
||||
|
||||
def test_eckrad_ohne_geruest_ist_nein():
|
||||
details = export_sivas.build_eckrad_details(_eckrad(1, einzelmodul="0"))
|
||||
assert details["3992"] == "Nein"
|
||||
|
||||
|
||||
def test_eckrad_hoehe_in_metern():
|
||||
assert export_sivas.build_eckrad_details(_eckrad(1, hoehe="4820"))["2950"] == "4.82"
|
||||
|
||||
|
||||
def _kreisel(nr, montagehoehe):
|
||||
return {"block_name": f"KREISEL_{nr}", "x": 0.0, "y": 0.0, "z": 0.0,
|
||||
"rotation": 0.0,
|
||||
"attribs": {"ID": f"000{nr}", "NUMMER": str(nr), "KREISELART": "STANDARD",
|
||||
"ABSTAND": "2300", "MONTAGEHOEHE_m": montagehoehe}}
|
||||
|
||||
|
||||
def test_kreisel_verschiedene_hoehen_getrennte_zeilen():
|
||||
assert (export_sivas.kreisel_signature(_kreisel(1, "4.820"))
|
||||
!= export_sivas.kreisel_signature(_kreisel(2, "2.000")))
|
||||
# numerischer Vergleich: gleiche Hoehe, andere Schreibweise -> eine Zeile
|
||||
assert (export_sivas.kreisel_signature(_kreisel(1, "4.820"))
|
||||
== export_sivas.kreisel_signature(_kreisel(2, "4.82")))
|
||||
|
||||
|
||||
def test_eckrad_verschiedene_hoehen_getrennte_zeilen():
|
||||
assert (export_sivas.eckrad_signature(_eckrad(1, hoehe="2000"))
|
||||
!= export_sivas.eckrad_signature(_eckrad(2, hoehe="2500")))
|
||||
assert (export_sivas.eckrad_signature(_eckrad(1, hoehe="2000"))
|
||||
== export_sivas.eckrad_signature(_eckrad(2, hoehe="2000")))
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_kreisel_weichen_json.py - Uebernahme der LISP-Kreisel-Beruehrpunkte.
|
||||
|
||||
Der LISP-Export (export.lsp csv:kreisel-weichen-schreiben, Kern
|
||||
KreiselInsert.lsp kreisel-bp-*) schreibt kreisel_weichen.json neben
|
||||
export_raw.json. export_csv.py/export_sivas.py lesen sie statt selbst zu
|
||||
rechnen; fehlt sie, bleibt es beim alten Python-Rueckfall.
|
||||
Laeuft ohne BricsCAD.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
|
||||
|
||||
from export_neighbors import ( # noqa: E402
|
||||
lisp_touch_switches,
|
||||
load_lisp_kreisel_weichen,
|
||||
)
|
||||
import export_sivas # noqa: E402
|
||||
|
||||
BOX = (250.0, 100.0, 30.0)
|
||||
|
||||
BEISPIEL = {
|
||||
"toleranz_mm": 1.0,
|
||||
"beruehrungen": [
|
||||
# Rad an Laufprofil, Radiale senkrecht (90 Grad)
|
||||
{"a": "0001", "b": "0002", "a_handle": "2A", "b_handle": "2B",
|
||||
"a_typ": "KREISEL", "b_typ": "KREISEL", "x": 22503.49, "y": -2821.76,
|
||||
"z": 5000.0, "art": "Kreis/Tangente", "detail": "AN8/Profil",
|
||||
"achse_von": "0002", "achse_rot_grad": 270.0},
|
||||
# Zweiter Punkt desselben Paares -> eigener Schluessel
|
||||
{"a": "0001", "b": "0002", "a_handle": "2A", "b_handle": "2B",
|
||||
"a_typ": "KREISEL", "b_typ": "KREISEL", "x": 23000.0, "y": -2821.76,
|
||||
"z": 5000.0, "art": "Kreis/Kreis", "detail": "SP8/AN8",
|
||||
"achse_von": "0001", "achse_rot_grad": 0.0},
|
||||
# Eckrad ohne ID -> Handle
|
||||
{"a": "", "b": "0003", "a_handle": "3F", "b_handle": "3C",
|
||||
"a_typ": "ECKRAD", "b_typ": "KREISEL", "x": 1.0, "y": 2.0, "z": 0.0,
|
||||
"art": "Kreis/Kreis", "detail": "ECKRAD/AN8",
|
||||
"achse_von": "", "achse_rot_grad": 0.0},
|
||||
],
|
||||
"kollisionen": [
|
||||
{"a": "0004", "b": "0005", "x": 0.0, "y": 0.0, "z": 0.0,
|
||||
"art": "Ueberschneidung", "detail": "Achsabstand 500.0",
|
||||
"achse_von": "0004", "achse_rot_grad": 0.0},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _schreibe(tmp_path, daten):
|
||||
raw = tmp_path / "export_raw.json"
|
||||
raw.write_text("[]", encoding="utf-8")
|
||||
(tmp_path / "kreisel_weichen.json").write_text(json.dumps(daten), encoding="utf-8")
|
||||
return str(raw)
|
||||
|
||||
|
||||
def test_datei_fehlt_liefert_none(tmp_path):
|
||||
raw = tmp_path / "export_raw.json"
|
||||
raw.write_text("[]", encoding="utf-8")
|
||||
assert load_lisp_kreisel_weichen(str(raw)) is None
|
||||
|
||||
|
||||
def test_kaputte_datei_liefert_none(tmp_path):
|
||||
raw = tmp_path / "export_raw.json"
|
||||
raw.write_text("[]", encoding="utf-8")
|
||||
(tmp_path / "kreisel_weichen.json").write_text("{kaputt", encoding="utf-8")
|
||||
assert load_lisp_kreisel_weichen(str(raw)) is None
|
||||
|
||||
|
||||
def test_switches_aus_lisp(tmp_path):
|
||||
daten = load_lisp_kreisel_weichen(_schreibe(tmp_path, BEISPIEL))
|
||||
sw = lisp_touch_switches(daten, element_box_mm=BOX)
|
||||
# Kollisionen werden keine Weichen
|
||||
assert len(sw) == 3
|
||||
assert [s["key"] for s in sw] == ["WEICHE#0001#0002", "WEICHE#0001#0002#2",
|
||||
"WEICHE#3F#0003"]
|
||||
assert sw[0]["nachbarn"] == ["0001", "0002"]
|
||||
assert sw[0]["beruehrung"] == "Kreis/Tangente"
|
||||
assert sw[0]["achse_von"] == "0002"
|
||||
assert (sw[0]["x"], sw[0]["y"], sw[0]["z"]) == (22503.49, -2821.76, 5000.0)
|
||||
# Radiale 270 Grad: Laengsseite (250) quer zur Radialen -> entlang X
|
||||
assert abs(sw[0]["dx"] - 250.0) < 1e-6 and abs(sw[0]["dy"] - 100.0) < 1e-6
|
||||
# Radiale 0 Grad: Laengsseite entlang Y
|
||||
assert abs(sw[1]["dx"] - 100.0) < 1e-6 and abs(sw[1]["dy"] - 250.0) < 1e-6
|
||||
# Ohne achse_von -> erster Partner
|
||||
assert sw[2]["achse_von"] == "3F"
|
||||
|
||||
|
||||
def test_sivas_4403_zaehlt_beruehrungen():
|
||||
_, counters = export_sivas.process_blocks([], anzahl_weichen_ils=10)
|
||||
assert counters["anzahl_weichen_ils"] == 10
|
||||
_, counters = export_sivas.process_blocks([])
|
||||
assert counters["anzahl_weichen_ils"] == 0
|
||||
|
||||
|
||||
def test_sivas_6216_summenzeile():
|
||||
"""Eine Zeile 6216, je Beruehrpunkt 1x aktiv (3371) + 1x passiv (3372)."""
|
||||
items, _ = export_sivas.process_blocks([], anzahl_weichen_ils=10)
|
||||
weichen = [i for i in items if i["sivasnr"] == "6216"]
|
||||
assert len(weichen) == 1
|
||||
w = weichen[0]
|
||||
assert w["teileart"] == "ILS 2.0 Weiche"
|
||||
assert w["anzahl"] == 1
|
||||
assert w["details"] == {"3371": "10", "3372": "10"}
|
||||
# CSV-Zeile laesst sich formatieren
|
||||
assert ';"6216";' in export_sivas.format_csv_line(w)
|
||||
|
||||
|
||||
def test_sivas_6216_ohne_weichen_keine_zeile():
|
||||
items, _ = export_sivas.process_blocks([])
|
||||
assert not [i for i in items if i["sivasnr"] == "6216"]
|
||||
+249
-3
@@ -360,6 +360,95 @@
|
||||
)
|
||||
|
||||
|
||||
;; ------------------------------------------------------------
|
||||
;; TESTS: vf_linienzug.lsp - ES-Fussabdruck entlang der Fahrtrichtung
|
||||
;; ------------------------------------------------------------
|
||||
;; vfl-es-laengs liefert die Laenge, die ein Segment fuer das ES-Element
|
||||
;; reservieren muss. Getestet wird die Rueckfallkette: ohne sie wuerde ein
|
||||
;; fehlender Messwert zu (- deltaL nil) und damit zum Absturz fuehren.
|
||||
(defun tu-tests-vfl-es ( / _s-laengs _s-dx)
|
||||
(princ "\n[vf_linienzug ES]")
|
||||
;; globale Masse sichern - sie gehoeren dem laufenden Bau-Kontext
|
||||
(setq _s-laengs ein-laengs _s-dx ein-dx)
|
||||
|
||||
;; Normalfall: ein-laengs ist gesetzt und wird genommen.
|
||||
;; 576.2 = ES_Element_30_rechts entlang der Fahrtrichtung; (abs ein-dx)
|
||||
;; waere 242.6 - genau die 333.65 mm, um die die Kette frueher zu weit ragte.
|
||||
(setq ein-laengs 576.2 ein-dx -242.6)
|
||||
(tu-eqf "vfl-es-laengs/nimmt-laengs-nicht-dx" 576.2 (vfl-es-laengs))
|
||||
|
||||
;; Vorzeichen: reserviert wird immer ein Betrag
|
||||
(setq ein-laengs -576.2)
|
||||
(tu-eqf "vfl-es-laengs/betrag" 576.2 (vfl-es-laengs))
|
||||
|
||||
;; Genulltes ein-dx gewinnt: die Null-Konvention der Bau-Pfade bedeutet
|
||||
;; "ES anderswo schon beruecksichtigt - hier nichts reservieren". Ohne diese
|
||||
;; Kopplung wuerde der Fix einen doppelten Abzug einbauen.
|
||||
(setq ein-laengs 576.2 ein-dx 0.0)
|
||||
(tu-eqf "vfl-es-laengs/genulltes-dx-gewinnt" 0.0 (vfl-es-laengs))
|
||||
|
||||
;; KS nicht lesbar -> Rueckfall auf das alte (zu kleine) Blockmass
|
||||
(setq ein-laengs nil ein-dx -242.6)
|
||||
(tu-eqf "vfl-es-laengs/fallback-auf-dx" 242.6 (vfl-es-laengs))
|
||||
|
||||
;; gar nichts gemessen -> 0.0, niemals nil (Absturzschutz)
|
||||
(setq ein-dx nil)
|
||||
(tu-eqf "vfl-es-laengs/fallback-null" 0.0 (vfl-es-laengs))
|
||||
|
||||
(setq ein-laengs _s-laengs ein-dx _s-dx)
|
||||
)
|
||||
|
||||
|
||||
;; ------------------------------------------------------------
|
||||
;; TESTS: vf_linienzug.lsp - Laenge aus einer Ziellinie (X/Y)
|
||||
;; ------------------------------------------------------------
|
||||
;; Steht die Fahrtrichtung fest, ist die Laenge nicht mehr frei: die (ggf.
|
||||
;; seitlich versetzte) Bahn trifft die Ziellinie in genau einem Punkt.
|
||||
;; REIN PLANAR - die Hoehe kommt weiterhin aus dem Gefaellewinkel.
|
||||
(defun tu-tests-vfl-ziellinie ( / _p0 _l _ks)
|
||||
(princ "\n[vf_linienzug Ziellinie]")
|
||||
|
||||
(tu-eqf "vfl-kreuz2/einheitsachsen" 1.0 (vfl-kreuz2 1.0 0.0 0.0 1.0))
|
||||
(tu-eqf "vfl-kreuz2/parallel" 0.0 (vfl-kreuz2 2.0 4.0 1.0 2.0))
|
||||
|
||||
(setq _p0 '(0.0 0.0 0.0))
|
||||
|
||||
;; Fahrtrichtung 0 Grad, senkrechte Ziellinie bei x=1000 -> L=1000
|
||||
(tu-eqf "vfl-laenge-bis-ziellinie/senkrecht"
|
||||
1000.0 (vfl-laenge-bis-ziellinie _p0 0.0 0.0 '(1000.0 -500.0 0.0) '(1000.0 500.0 0.0)))
|
||||
|
||||
;; Querversatz verschiebt die BAHN seitlich, nicht laengs: bei einer zur
|
||||
;; Fahrtrichtung senkrechten Ziellinie bleibt L deshalb gleich.
|
||||
(tu-eqf "vfl-laenge-bis-ziellinie/querversatz-ohne-wirkung"
|
||||
1000.0 (vfl-laenge-bis-ziellinie _p0 0.0 52.5 '(1000.0 -500.0 0.0) '(1000.0 500.0 0.0)))
|
||||
|
||||
;; 45-Grad-Ziellinie durch (1000,0): die um +100 versetzte Bahn trifft sie
|
||||
;; 100 frueher (y=100 wird bei x=1100 erreicht -> ohne Versatz 1000+100).
|
||||
(tu-eqf "vfl-laenge-bis-ziellinie/schraege-linie"
|
||||
1100.0 (vfl-laenge-bis-ziellinie _p0 0.0 100.0 '(1000.0 0.0 0.0) '(2000.0 1000.0 0.0)))
|
||||
|
||||
;; Ziellinie PARALLEL zur Fahrtrichtung -> kein eindeutiger Treffer -> nil
|
||||
(tu-nil "vfl-laenge-bis-ziellinie/parallel"
|
||||
(vfl-laenge-bis-ziellinie _p0 0.0 0.0 '(0.0 500.0 0.0) '(1000.0 500.0 0.0)))
|
||||
|
||||
;; Z der Ziellinie ist irrelevant (rein planare Rechnung, Nutzer-Vorgabe):
|
||||
;; dieselbe Linie auf anderer Hoehe liefert dasselbe L.
|
||||
(tu-eqf "vfl-laenge-bis-ziellinie/z-ignoriert"
|
||||
1000.0 (vfl-laenge-bis-ziellinie _p0 0.0 0.0 '(1000.0 -500.0 7777.0) '(1000.0 500.0 -99.0)))
|
||||
|
||||
;; Realfall aus dem Nutzer-Log: nach dem 30-Grad-Bogen steht die Kette bei
|
||||
;; (901.21,-2728.97) mit hz=-120; das ES-KS_AUS laeuft 52.51 mm versetzt und
|
||||
;; landete bei (231.42,-3994.11). Eine Ziellinie durch diesen Punkt quer zur
|
||||
;; Fahrtrichtung muss genau die damals gebaute Gesamtlaenge liefern.
|
||||
(setq _ks '(231.42 -3994.11 0.0))
|
||||
(setq _l (vfl-laenge-bis-ziellinie '(901.21 -2728.97 0.0) -120.0 52.51
|
||||
_ks (list (+ (car _ks) (cos (/ pi 6.0))) ; 30 Grad = quer zu -120
|
||||
(+ (cadr _ks) (sin (/ pi 6.0))) 0.0)))
|
||||
(tu-true "vfl-laenge-bis-ziellinie/realfall-plausibel"
|
||||
(and _l (> _l 1430.0) (< _l 1431.0)))
|
||||
)
|
||||
|
||||
|
||||
;; ------------------------------------------------------------
|
||||
;; TESTS: export.lsp - CSV/KOS-Kodierung
|
||||
;; ------------------------------------------------------------
|
||||
@@ -617,6 +706,46 @@
|
||||
)
|
||||
|
||||
|
||||
;; ------------------------------------------------------------
|
||||
;; TESTS: export.lsp - Hoehen-Attribute aus Z neu berechnen
|
||||
;; ------------------------------------------------------------
|
||||
(defun tu-tests-hoehen ( / _w)
|
||||
(princ "\n[export/hoehen]")
|
||||
|
||||
;; Nicht verschoben -> nil (Block bleibt beim Bau-Wert)
|
||||
(tu-nil "csv:hoehen-werte/kreisel-unveraendert"
|
||||
(csv:hoehen-werte "KREISEL" 2000.0 '(("HOEHE" . "2000") ("MONTAGEHOEHE_m" . "1.800"))))
|
||||
(tu-nil "csv:hoehen-werte/vf-unveraendert"
|
||||
(csv:hoehen-werte "VF" 2000.0 '(("HOEHE_VON_mm" . "2000") ("HOEHE_BIS_mm" . "1876"))))
|
||||
|
||||
;; Kreisel / Eckrad verschoben
|
||||
(setq _w (csv:hoehen-werte "KREISEL" 2500.0 '(("HOEHE" . "2000"))))
|
||||
(tu-eq "csv:hoehen-werte/kreisel-hoehe" "2500" (cdr (assoc "HOEHE" _w)))
|
||||
(tu-eqf "csv:hoehen-werte/kreisel-montage" 2.5 (atof (cdr (assoc "MONTAGEHOEHE_m" _w))))
|
||||
(setq _w (csv:hoehen-werte "ECKRAD" 1500.0 '(("HOEHE" . "2000"))))
|
||||
(tu-eq "csv:hoehen-werte/eckrad-hoehe" "1500" (cdr (assoc "HOEHE" _w)))
|
||||
(tu-nil "csv:hoehen-werte/eckrad-ohne-montage" (assoc "MONTAGEHOEHE_m" _w))
|
||||
|
||||
;; GF abwaerts: Differenz 124 bleibt, MONTAGE = VON/1000
|
||||
(setq _w (csv:hoehen-werte "GF" 2500.0
|
||||
'(("HOEHE_VON_mm" . "2000") ("HOEHE_BIS_mm" . "1876") ("DELTA_H_mm" . "123"))))
|
||||
(tu-eq "csv:hoehen-werte/gf-von" "2500" (cdr (assoc "HOEHE_VON_mm" _w)))
|
||||
(tu-eq "csv:hoehen-werte/gf-bis" "2376" (cdr (assoc "HOEHE_BIS_mm" _w)))
|
||||
(tu-eqf "csv:hoehen-werte/gf-montage" 2.5 (atof (cdr (assoc "MONTAGEHOEHE_m" _w))))
|
||||
|
||||
;; VF steigend ("Auf"): BIS liegt ueber VON, DELTA_H_mm ohne Vorzeichen
|
||||
(setq _w (csv:hoehen-werte "VF" 1000.0
|
||||
'(("HOEHE_VON_mm" . "2000") ("HOEHE_BIS_mm" . "2400") ("DELTA_H_mm" . "400"))))
|
||||
(tu-eq "csv:hoehen-werte/vf-auf-von" "1000" (cdr (assoc "HOEHE_VON_mm" _w)))
|
||||
(tu-eq "csv:hoehen-werte/vf-auf-bis" "1400" (cdr (assoc "HOEHE_BIS_mm" _w)))
|
||||
(tu-eqf "csv:hoehen-werte/vf-auf-montage" 1.2 (atof (cdr (assoc "MONTAGEHOEHE_m" _w))))
|
||||
|
||||
;; VF ohne VON/BIS -> Rueckfall z - DELTA_H_mm
|
||||
(setq _w (csv:hoehen-werte "VF" 3000.0 '(("DELTA_H_mm" . "500"))))
|
||||
(tu-eq "csv:hoehen-werte/vf-rueckfall-bis" "2500" (cdr (assoc "HOEHE_BIS_mm" _w)))
|
||||
)
|
||||
|
||||
|
||||
;; ------------------------------------------------------------
|
||||
;; TESTS: Gefaellestrecke.lsp - Geometrie-/Listen-Helfer
|
||||
;; ------------------------------------------------------------
|
||||
@@ -671,7 +800,7 @@
|
||||
;; ------------------------------------------------------------
|
||||
;; TESTS: KreiselInsert.lsp - Geometrie-Helfer
|
||||
;; ------------------------------------------------------------
|
||||
(defun tu-tests-kreisel ()
|
||||
(defun tu-tests-kreisel ( / _e)
|
||||
(princ "\n[KreiselInsert]")
|
||||
|
||||
;; Projektion eines Punktes auf eine Gerade
|
||||
@@ -689,6 +818,123 @@
|
||||
(tu-eqf "kreisel-idx-to-rotation/2" 90.0 (kreisel-idx-to-rotation 2))
|
||||
(tu-eq "kreisel-rotation-to-idx/90" 2 (kreisel-rotation-to-idx 90.0))
|
||||
(tu-eq "kreisel-rotation-to-idx/180" 1 (kreisel-rotation-to-idx 180.0))
|
||||
|
||||
;; Beschriftung mit/ohne eigenen Namen
|
||||
(tu-eq "kreisel-label-text/name"
|
||||
"KREISEL 3: Puffer STANDARD_2300" (kreisel-label-text 3 "Puffer" "STANDARD" 2300.0))
|
||||
(tu-eq "kreisel-label-text/vorgabe-name"
|
||||
"KREISEL 3: PIN_4000" (kreisel-label-text 3 "Kreisel3" "PIN" 4000.0))
|
||||
(tu-eq "kreisel-label-text/leer"
|
||||
"KREISEL 3: PIN_4000" (kreisel-label-text 3 "" "PIN" 4000.0))
|
||||
|
||||
;; Eckrad-Dialog: Eingaben -> (attribs . rotation)
|
||||
(setq _e (eckrad-dialog-werte '(("NUMMER" . "3") ("HOEHE" . "0") ("DREHRICHTUNG" . "UZS"))
|
||||
"Eck3" "2500" "-90" "1" "0" "1"))
|
||||
(tu-eqf "eckrad-dialog-werte/rotation-normiert" 270.0 (cdr _e))
|
||||
(tu-eq "eckrad-dialog-werte/hoehe" "2500" (cdr (assoc "HOEHE" (car _e))))
|
||||
(tu-eq "eckrad-dialog-werte/nummer-bleibt" "3" (cdr (assoc "NUMMER" (car _e))))
|
||||
(tu-eq "eckrad-dialog-werte/name" "Eck3" (cdr (assoc "Bezeichnung" (car _e))))
|
||||
(tu-eq "eckrad-dialog-werte/guz" "GUZ" (cdr (assoc "DREHRICHTUNG" (car _e))))
|
||||
(tu-eq "eckrad-dialog-werte/einzelmodul" "0" (cdr (assoc "GERUEST_EINZELMODUL" (car _e))))
|
||||
(tu-eq "eckrad-dialog-werte/geruest-typ" (nth 1 *ssg-geruest-optionen*)
|
||||
(cdr (assoc "GERUEST_TYP" (car _e))))
|
||||
(tu-nil "eckrad-dialog-werte/hoehe-ungueltig"
|
||||
(eckrad-dialog-werte nil "" "abc" "0" "0" "1" "2"))
|
||||
(tu-nil "eckrad-dialog-werte/drehung-leer"
|
||||
(eckrad-dialog-werte nil "" "2000" "" "0" "1" "2"))
|
||||
)
|
||||
|
||||
|
||||
;; ------------------------------------------------------------
|
||||
;; TESTS: KreiselInsert.lsp - Beruehrpunkte/Weichen (kreisel-bp-*)
|
||||
;; ------------------------------------------------------------
|
||||
;; Element-Record kurz: (typ x y z rot abstand id)
|
||||
(defun tu-bp-el (typ x y z rot abstand id)
|
||||
(kreisel-bp-element typ (list x y) z rot abstand 400.0 id id id)
|
||||
)
|
||||
|
||||
(defun tu-bp-arten (treffer)
|
||||
(mapcar '(lambda (tr) (cdr (assoc "art" tr))) treffer)
|
||||
)
|
||||
|
||||
(defun tu-tests-kreisel-bp ( / _els _erg _ber _kol)
|
||||
(princ "\n[KreiselInsert/Beruehrpunkte]")
|
||||
|
||||
;; Hilfs-Kerne
|
||||
(tu-eqf "kreisel-bp-abstand/attribut" 7715.0 (kreisel-bp-abstand "7715" "KREISEL_1_STANDARD_7715"))
|
||||
(tu-eqf "kreisel-bp-abstand/blockname" 3469.0 (kreisel-bp-abstand nil "KREISEL_3_STANDARD_3469"))
|
||||
(tu-eqf "kreisel-bp-naechste-punkte/kreuz"
|
||||
'((5.0 0.0) (5.0 0.0))
|
||||
(kreisel-bp-naechste-punkte '(0.0 0.0) '(10.0 0.0) '(5.0 -5.0) '(5.0 5.0)))
|
||||
|
||||
;; Die 11 Einfuegungen aus tests/testdata/schnittspunkte_kreisel.dxf
|
||||
(setq _els
|
||||
(list
|
||||
(tu-bp-el "ECKRAD" 22405.42028816571 -4021.762548616969 5000.0 270.0 0.0 "E1")
|
||||
(tu-bp-el "KREISEL" 20971.14247926632 -3221.762548616969 5000.0 0.0 7715.0 "K1")
|
||||
(tu-bp-el "KREISEL" 22503.49142858727 -2821.762548616969 5000.0 90.0 9020.0 "K2")
|
||||
(tu-bp-el "KREISEL" 27253.08134883916 -3621.762548616969 5000.0 270.0 3469.0 "K3")
|
||||
(tu-bp-el "KREISEL" 22503.49142858727 6998.075803812235 5000.0 90.0 4746.0 "K4")
|
||||
(tu-bp-el "KREISEL" 22405.42028816571 -4421.762548616969 5000.0 270.0 6147.0 "K5")
|
||||
(tu-bp-el "KREISEL" 26853.08134883916 -6582.442351476258 5000.0 180.0 3248.0 "K6")
|
||||
(tu-bp-el "KREISEL" 32541.84792939903 -5752.219206460449 2000.0 270.0 9200.0 "K7")
|
||||
(tu-bp-el "KREISEL" 32941.84792939903 -7662.266486416901 2000.0 0.0 3637.0 "K8a")
|
||||
(tu-bp-el "KREISEL" 32941.84792939903 -11185.64734962363 2000.0 0.0 3637.0 "K8b")
|
||||
(tu-bp-el "ECKRAD" 20571.14247926632 -3221.762548616969 5000.0 270.0 0.0 "E1b")))
|
||||
(setq _erg (kreisel-bp-berechnen _els 1.0 1.0)
|
||||
_ber (car _erg)
|
||||
_kol (cadr _erg))
|
||||
(tu-eq "kreisel-bp/dxf-beruehrungen" 10 (length _ber))
|
||||
(tu-eq "kreisel-bp/dxf-kollisionen" 0 (length _kol))
|
||||
(tu-eq "kreisel-bp/dxf-kreis-kreis" 3
|
||||
(length (vl-remove-if-not '(lambda (a) (= a "Kreis/Kreis")) (tu-bp-arten _ber))))
|
||||
(tu-eq "kreisel-bp/dxf-paare"
|
||||
'(("E1" "K1") ("E1" "K5") ("K1" "K2") ("K1" "K3") ("K1" "E1b") ("K2" "K4")
|
||||
("K3" "K6") ("K5" "K6") ("K7" "K8a") ("K7" "K8b"))
|
||||
(mapcar '(lambda (tr) (list (cdr (assoc "id" (cdr (assoc "a" tr))))
|
||||
(cdr (assoc "id" (cdr (assoc "b" tr))))))
|
||||
_ber))
|
||||
;; K2 (AN8) steht auf dem oberen Laufprofil von K1
|
||||
(tu-eqf "kreisel-bp/dxf-punkt-k1-k2"
|
||||
(list 22503.49142858727 -2821.762548616969)
|
||||
(cdr (assoc "p" (nth 2 _ber))))
|
||||
(tu-eq "kreisel-bp/dxf-achse-von-k1-k2" "K2" (cdr (assoc "achse_von" (nth 2 _ber))))
|
||||
|
||||
;; Unterschiedliche Hoehe -> kein Kontakt
|
||||
(tu-eq "kreisel-bp/hoehe"
|
||||
'(nil nil)
|
||||
(kreisel-bp-berechnen (list (tu-bp-el "ECKRAD" 0.0 0.0 0.0 0.0 0.0 "A")
|
||||
(tu-bp-el "ECKRAD" 800.0 0.0 500.0 0.0 0.0 "B"))
|
||||
1.0 1.0))
|
||||
;; Zwei Eckraeder Rad an Rad -> eine Weiche in der Mitte
|
||||
(setq _ber (car (kreisel-bp-berechnen (list (tu-bp-el "ECKRAD" 0.0 0.0 0.0 0.0 0.0 "A")
|
||||
(tu-bp-el "ECKRAD" 800.0 0.0 0.0 0.0 0.0 "B"))
|
||||
1.0 1.0)))
|
||||
(tu-eq "kreisel-bp/eckraeder-art" '("Kreis/Kreis") (tu-bp-arten _ber))
|
||||
(tu-eqf "kreisel-bp/eckraeder-punkt" '(400.0 0.0) (cdr (assoc "p" (car _ber))))
|
||||
;; Spalt groesser als Toleranz -> nichts
|
||||
(tu-eq "kreisel-bp/spalt"
|
||||
'(nil nil)
|
||||
(kreisel-bp-berechnen (list (tu-bp-el "ECKRAD" 0.0 0.0 0.0 0.0 0.0 "A")
|
||||
(tu-bp-el "ECKRAD" 805.0 0.0 0.0 0.0 0.0 "B"))
|
||||
1.0 1.0))
|
||||
;; Laufprofile kreuzen sich -> Kollision, keine Weiche
|
||||
(setq _erg (kreisel-bp-berechnen (list (tu-bp-el "KREISEL" 0.0 0.0 0.0 0.0 2000.0 "A")
|
||||
(tu-bp-el "KREISEL" 1000.0 -1500.0 0.0 90.0 2000.0 "B"))
|
||||
1.0 1.0))
|
||||
(tu-nil "kreisel-bp/kreuzung-keine-weiche" (car _erg))
|
||||
(tu-eq "kreisel-bp/kreuzung-kollision" '("Ueberschneidung") (tu-bp-arten (cadr _erg)))
|
||||
;; Kreisel Seite an Seite (Profile aufeinander) -> Kollision, keine Weiche
|
||||
(setq _erg (kreisel-bp-berechnen (list (tu-bp-el "KREISEL" 0.0 0.0 0.0 0.0 2000.0 "A")
|
||||
(tu-bp-el "KREISEL" 500.0 800.0 0.0 0.0 2000.0 "B"))
|
||||
1.0 1.0))
|
||||
(tu-nil "kreisel-bp/parallel-keine-weiche" (car _erg))
|
||||
(tu-eq "kreisel-bp/parallel-kollision" '("Tangente/Tangente") (tu-bp-arten (cadr _erg)))
|
||||
;; Eckrad an der Flanke eines Kreiselrades -> Weiche (freie Seite)
|
||||
(setq _ber (car (kreisel-bp-berechnen (list (tu-bp-el "KREISEL" 0.0 0.0 0.0 0.0 2000.0 "A")
|
||||
(tu-bp-el "ECKRAD" 400.0 800.0 0.0 0.0 0.0 "B"))
|
||||
1.0 1.0)))
|
||||
(tu-eq "kreisel-bp/flanke-eckrad" 1 (length _ber))
|
||||
)
|
||||
|
||||
|
||||
@@ -1981,8 +2227,8 @@
|
||||
(princ "\n================================================================")
|
||||
|
||||
(foreach fn '("tu-tests-core" "tu-tests-lang" "tu-tests-id" "tu-tests-vfcore"
|
||||
"tu-tests-etage"
|
||||
"tu-tests-export" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel"
|
||||
"tu-tests-etage" "tu-tests-vfl-es" "tu-tests-vfl-ziellinie"
|
||||
"tu-tests-export" "tu-tests-hoehen" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel" "tu-tests-kreisel-bp"
|
||||
"tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest"
|
||||
"tu-tests-sepscan" "tu-tests-vfl-segs" "tu-tests-vfl-abbruch"
|
||||
"tu-tests-werkzeuge")
|
||||
|
||||
Reference in New Issue
Block a user