52264f1531
csv:block-to-json ergaenzt fuer jeden Block einen Insertpoint-String (Position+Rotation als Z-Quaternion, csv:kos-encode) sowie K1-K4 als reine Positions-Strings (csv:trans-encode, 0.1 mm). KS_EIN/KS_AUS werden auf K1/K2 gemappt, falls keine echten K1/K2 vorhanden sind. Neue CSV- Spalten Insertpoint;K1;K2;K3;K4 in export_csv.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
675 lines
25 KiB
Common Lisp
675 lines
25 KiB
Common Lisp
;; ============================================================
|
|
;; export.lsp - Export-Funktionen fuer INSERT-Bloecke
|
|
;;
|
|
;; Enthaelt:
|
|
;; - JSON-Sammler (csv:collect-export-blocks, csv:run-export)
|
|
;; - Python-basierter Export: EXPORTSIVAS, EXPORTCSV
|
|
;;
|
|
;; Alle Blockname-Muster dieses Moduls sind zentral in cfg/export.cfg
|
|
;; (INI-Style, Abschnitt [blockpattern]) deklariert und werden beim
|
|
;; Laden des Moduls einmalig eingelesen (export:load-cfg).
|
|
;; Datei-/Skriptnamen sind fest im Code hinterlegt (*export-filenames*).
|
|
;; ============================================================
|
|
|
|
(vl-load-com)
|
|
|
|
;; --- Export-Konfiguration aus cfg/export.cfg laden ---
|
|
;; Nutzt den zentralen INI-Parser aus ssg_core.lsp (ssg-load-ini / ssg-ini-get /
|
|
;; ssg-ini-list), damit fuer alle Module derselbe .cfg-Standard gilt.
|
|
(defun export:load-cfg ( / cfg-pfad)
|
|
(setq cfg-pfad (strcat (getenv "DXFM_CFG") "/export.cfg"))
|
|
(setq *export-cfg* (ssg-load-ini cfg-pfad))
|
|
(if (null *export-cfg*)
|
|
(princ (ssg-textf "exp-cfg-not-found" (list cfg-pfad)))
|
|
)
|
|
(princ)
|
|
)
|
|
|
|
;; --- Einzelwert aus *export-cfg* lesen, mit Fallback ---
|
|
;; section = "blockpattern"
|
|
(defun export:cfg (section key default)
|
|
(ssg-ini-get *export-cfg* section key default)
|
|
)
|
|
|
|
;; --- Muster aus [blockpattern] als normalisierter wcmatch-String ---
|
|
;; key = Config-Key, z.B. "pattern_kreisel"
|
|
;; default-str = Fallback-Wert als kommagetrennter String, falls Config fehlt
|
|
;; Rueckgabe: kommagetrennter String ohne Leerzeichen, z.B. "KR_*,KREISEL_*"
|
|
(defun export:pattern (key default-str)
|
|
(ssg-ini-list *export-cfg* "blockpattern" key default-str)
|
|
)
|
|
|
|
;; Beim Laden des Moduls einmalig einlesen (Lazy-Loading via ssg-ensure "export")
|
|
(export:load-cfg)
|
|
|
|
;; --- Test-Override fuer den Ausgabeordner (nur Lisp-Sitzungsspeicher) ---
|
|
;; Wird ausschliesslich von TEST_EXPORT_ALL (tests/test_export_all.lsp)
|
|
;; waehrend des Testlaufs auf DXFM_TESTOUT gesetzt und danach garantiert
|
|
;; wieder auf nil zurueckgesetzt. Bewusst KEIN (setenv "DXFM_RESULTS" ...):
|
|
;; (setenv ...) schreibt in BricsCAD dauerhaft in die Profil-Registry und
|
|
;; ueberlebt damit jeden Neustart.
|
|
(setq *export-test-override* nil)
|
|
|
|
|
|
;; --- Feste Datei-/Skriptnamen dieses Moduls (keine Config, direkt im Code) ---
|
|
(setq *export-filenames*
|
|
(list
|
|
(cons "raw_json_datei" "export_raw.json")
|
|
(cons "sivas_script" "export_sivas.py")
|
|
(cons "sivas_csv" "export_sivas.csv")
|
|
(cons "csv_script" "export_csv.py")
|
|
(cons "csv_csv" "export.csv")
|
|
)
|
|
)
|
|
|
|
;; --- Dateinamen aus *export-filenames* lesen ---
|
|
(defun export:filename (key)
|
|
(cdr (assoc key *export-filenames*))
|
|
)
|
|
|
|
|
|
;; --- String fuer JSON escapen (Anfuehrungszeichen/Backslash) ---
|
|
;; Konvertiert nicht-String-Werte (z.B. T, nil, Zahlen) defensiv in String.
|
|
(defun csv:json-escape (s)
|
|
(if (not (= (type s) 'STR))
|
|
(setq s (if s (vl-princ-to-string s) ""))
|
|
)
|
|
(setq s (vl-string-subst "\\\\" "\\" s))
|
|
(vl-string-subst "\\\"" "\"" s)
|
|
)
|
|
|
|
|
|
;; --- Safearray/Variant-Rueckgabe von vla-getboundingbox in Liste wandeln ---
|
|
;; BricsCAD liefert ein Safearray, AutoCAD ein Variant mit Safearray.
|
|
(defun csv:pt->list (v)
|
|
(cond
|
|
((= (type v) 'variant) (vlax-safearray->list (vlax-variant-value v)))
|
|
((= (type v) 'safearray) (vlax-safearray->list v))
|
|
((listp v) v)
|
|
(t nil)
|
|
)
|
|
)
|
|
|
|
;; ============================================================
|
|
;; KOS-KOMPRIMIERUNG (Position + Rotation als 24-Zeichen Base64-String)
|
|
;; ============================================================
|
|
;; Kodiert Position (x,y,z in mm) und Rotation (als Quaternion qx,qy,qz,qw)
|
|
;; verlustarm (24 Bit Fixed-Point je Wert) in einen 24-Zeichen Base64-String.
|
|
;; Ported aus einer interaktiven Referenz-Routine (c:ExportKOS), hier als
|
|
;; reine Funktion fuer den automatischen Export nutzbar. Alle Elemente in
|
|
;; diesem Projekt werden ausschliesslich um die Z-Achse gedreht (siehe
|
|
;; DREHUNG-Konvention weiter unten), daher genuegt eine Halbwinkel-Quaternion
|
|
;; fuer eine reine Z-Rotation (csv:z-angle-to-quat) -- keine vollstaendige
|
|
;; Rotationsmatrix/Quaternion-Extraktion noetig.
|
|
(setq *csv-b64-chars* "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
|
|
|
|
;; --- Wert auf [min-val, max-val] begrenzen ---
|
|
(defun csv:clamp (val min-val max-val)
|
|
(max min-val (min val max-val))
|
|
)
|
|
|
|
;; --- Rotationswinkel (Grad, reine Z-Drehung) -> Quaternion (qx qy qz qw) ---
|
|
(defun csv:z-angle-to-quat (deg / rad)
|
|
(setq rad (/ (* deg pi) 180.0))
|
|
(list 0.0 0.0 (sin (/ rad 2.0)) (cos (/ rad 2.0)))
|
|
)
|
|
|
|
;; --- Liste vorzeichenbehafteter 24-Bit-Integer -> Base64 (je 4 Zeichen) ---
|
|
;; Gemeinsamer Kodierer fuer csv:kos-encode und csv:trans-encode.
|
|
(defun csv:b64-encode-ints (int-list / b64-str val)
|
|
(setq b64-str "")
|
|
(foreach val int-list
|
|
(if (< val 0) (setq val (+ val 16777216))) ; Zweierkomplement (24 Bit)
|
|
(setq b64-str (strcat b64-str
|
|
(substr *csv-b64-chars* (1+ (lsh val -18)) 1)
|
|
(substr *csv-b64-chars* (1+ (logand (lsh val -12) 63)) 1)
|
|
(substr *csv-b64-chars* (1+ (logand (lsh val -6) 63)) 1)
|
|
(substr *csv-b64-chars* (1+ (logand val 63)) 1)
|
|
))
|
|
)
|
|
b64-str
|
|
)
|
|
|
|
;; --- Position (mm) + Quaternion -> 24-Zeichen Base64-String ---
|
|
;; Fixed-Point: x,y,z (Faktor 1000 = mm) und qx,qy,qz werden je auf 24 Bit
|
|
;; gerundet (6 Werte * 4 Zeichen = 24 Zeichen). qw wird nicht mitkodiert,
|
|
;; sondern nur zur Vorzeichen-Normierung verwendet (q und -q sind dieselbe
|
|
;; Rotation). Fuer den Insertpoint (volle Lage), siehe csv:trans-encode fuer
|
|
;; reine Positionen (K1-K4).
|
|
(defun csv:kos-encode (x y z qx qy qz qw)
|
|
(if (< qw 0.0)
|
|
(setq qx (- qx) qy (- qy) qz (- qz))
|
|
)
|
|
(csv:b64-encode-ints (list
|
|
(fix (csv:clamp (* x 1000.0) -8388607 8388607))
|
|
(fix (csv:clamp (* y 1000.0) -8388607 8388607))
|
|
(fix (csv:clamp (* z 1000.0) -8388607 8388607))
|
|
(fix (csv:clamp (* qx 8388607.0) -8388607 8388607))
|
|
(fix (csv:clamp (* qy 8388607.0) -8388607 8388607))
|
|
(fix (csv:clamp (* qz 8388607.0) -8388607 8388607))
|
|
))
|
|
)
|
|
|
|
;; --- Position (nur x,y,z) -> 12-Zeichen Base64-String ---
|
|
;; Fixed-Point Faktor 10.0 (0.1 mm Genauigkeit, Bereich +-838 m), 3 Werte *
|
|
;; 4 Zeichen = 12 Zeichen. Fuer die Koordinatensysteme K1-K4 (nur Position,
|
|
;; keine Rotation). Ported aus der Referenz-Routine c:ExportTrans.
|
|
(defun csv:trans-encode (x y z)
|
|
(csv:b64-encode-ints (list
|
|
(fix (csv:clamp (* x 10.0) -8388607 8388607))
|
|
(fix (csv:clamp (* y 10.0) -8388607 8388607))
|
|
(fix (csv:clamp (* z 10.0) -8388607 8388607))
|
|
))
|
|
)
|
|
|
|
;; --- Lokalen Punkt (relativ zum Block-Ursprung) unter Beruecksichtigung der
|
|
;; Eltern-Rotation (reine Z-Drehung um den Einfuegepunkt) in Weltkoordinaten
|
|
;; umrechnen. Rueckgabe: (x y z) ---
|
|
(defun csv:local-to-world (parent-pt parent-rot-rad local-pt / c s lx ly lz pz)
|
|
(setq c (cos parent-rot-rad) s (sin parent-rot-rad))
|
|
(setq lx (car local-pt) ly (cadr local-pt))
|
|
(setq lz (if (caddr local-pt) (caddr local-pt) 0.0))
|
|
(setq pz (if (caddr parent-pt) (caddr parent-pt) 0.0))
|
|
(list
|
|
(+ (car parent-pt) (- (* lx c) (* ly s)))
|
|
(+ (cadr parent-pt) (+ (* lx s) (* ly c)))
|
|
(+ pz lz)
|
|
)
|
|
)
|
|
|
|
;; --- K1-K4-Koordinatensysteme eines Blocks als Positions-Strings lesen ---
|
|
;; ename = Entity-Name des platzierten INSERT (Omniflo Bogen/Weiche/Gerade).
|
|
;; pt/rot-rad = bereits ermittelte Welt-Position/-Rotation (radiant) des
|
|
;; Eltern-INSERT (siehe csv:block-to-json), keine erneute Abfrage.
|
|
;; Sucht in der Block-DEFINITION (wie omni:get-block-hoehe) nach direkten
|
|
;; Sub-INSERTs "K1".."K4" und rechnet deren lokale Position ueber die
|
|
;; Eltern-Rotation (reine Z-Drehung) in Weltkoordinaten um. Es wird nur die
|
|
;; POSITION kodiert (12-Zeichen-String, csv:trans-encode) - keine Rotation.
|
|
;; Zusaetzlich werden Strecken-Koordinatensysteme KS_EIN->K1 und KS_AUS->K2
|
|
;; (inkl. der KSYS_-Varianten, siehe ks-normalize-name in vf_core.lsp) gemappt,
|
|
;; sofern kein echter K1/K2 am Block vorhanden ist (echte K-Bloecke haben
|
|
;; Vorrang: sie werden vorne angehaengt, csv:k-kos liest per assoc den ersten).
|
|
;; Rueckgabe: Assoc-Liste (("K1" . "<pos-string>") ("K2" . ...) ...), nur
|
|
;; fuer tatsaechlich vorhandene K-Bloecke (nicht jedes Element hat alle 4).
|
|
(defun csv:get-k-kos-strings (ename pt rot-rad / ed bname blk-tbl sub-ent sub-ed
|
|
sub-bname sub-pt world-pt target is-real result)
|
|
(setq ed (entget ename))
|
|
(setq bname (cdr (assoc 2 ed)))
|
|
(setq blk-tbl (tblsearch "BLOCK" bname))
|
|
(setq result nil)
|
|
(if blk-tbl
|
|
(progn
|
|
(setq sub-ent (entnext (cdr (assoc -2 blk-tbl))))
|
|
(while sub-ent
|
|
(setq sub-ed (entget sub-ent))
|
|
(if (= (cdr (assoc 0 sub-ed)) "INSERT")
|
|
(progn
|
|
(setq sub-bname (strcase (cdr (assoc 2 sub-ed))))
|
|
;; Ziel-Slot bestimmen: echte K1-K4 direkt, KS_EIN/KS_AUS gemappt.
|
|
(setq is-real (wcmatch sub-bname "K1,K2,K3,K4"))
|
|
(setq target
|
|
(cond
|
|
(is-real sub-bname)
|
|
((or (= sub-bname "KS_EIN") (= sub-bname "KSYS_EIN")) "K1")
|
|
((or (= sub-bname "KS_AUS") (= sub-bname "KSYS_AUS")) "K2")
|
|
(t nil)))
|
|
;; Echte K-Bloecke immer aufnehmen (vorne = Vorrang bei assoc);
|
|
;; KS-abgeleitete nur, wenn der Slot noch nicht belegt ist.
|
|
(if (and target (or is-real (not (assoc target result))))
|
|
(progn
|
|
(setq sub-pt (cdr (assoc 10 sub-ed)))
|
|
(setq world-pt (csv:local-to-world pt rot-rad sub-pt))
|
|
(setq result (cons
|
|
(cons target
|
|
(csv:trans-encode
|
|
(car world-pt) (cadr world-pt) (caddr world-pt)))
|
|
result))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(setq sub-ent (entnext sub-ent))
|
|
)
|
|
)
|
|
)
|
|
result
|
|
)
|
|
|
|
;; --- Wert aus csv:get-k-kos-strings nach Name lesen, "" falls nicht vorhanden ---
|
|
(defun csv:k-kos (k-list name / found)
|
|
(setq found (assoc name k-list))
|
|
(if found (cdr found) "")
|
|
)
|
|
|
|
|
|
;; --- Mindesthoehe der Bounding-Box (Z-Ausdehnung), aus cfg/export.cfg ---
|
|
;; [Boundingbox] bb_minimum_height_mm, Default 1 (mm) falls nicht gesetzt.
|
|
;; Verhindert dz=0 bei flachen 2D-Bloecken (Draufsicht ohne Z-Ausdehnung).
|
|
(defun csv:bb-minimum-height-mm ()
|
|
(atof (export:cfg "Boundingbox" "bb_minimum_height_mm" "1"))
|
|
)
|
|
|
|
;; --- Bounding-Box (WCS, achsparallel) eines INSERT-Blocks ermitteln ---
|
|
;; Erzwingt vorher ein vla-update (Regen), da vla-getboundingbox sonst auf
|
|
;; nicht-regenerierten oder auf eingefrorenen Layern liegenden Bloecken scheitert.
|
|
;; Rueckgabe: Liste (cx cy cz dx dy dz) -- Mittelpunkt + Ausdehnung, oder nil bei Fehler.
|
|
;; dz wird auf csv:bb-minimum-height-mm angehoben, falls kleiner (siehe dort).
|
|
(defun csv:get-bbox (ename / obj res minpt maxpt dz min-hoehe)
|
|
(setq obj (vlax-ename->vla-object ename))
|
|
(vl-catch-all-apply 'vla-update (list obj))
|
|
(setq res
|
|
(vl-catch-all-apply
|
|
'(lambda ( / ll ur)
|
|
(vla-getboundingbox obj 'll 'ur)
|
|
(list (csv:pt->list ll) (csv:pt->list ur)))))
|
|
(if (vl-catch-all-error-p res)
|
|
nil
|
|
(progn
|
|
(setq minpt (car res))
|
|
(setq maxpt (cadr res))
|
|
(setq dz (- (caddr maxpt) (caddr minpt)))
|
|
(setq min-hoehe (csv:bb-minimum-height-mm))
|
|
(if (< dz min-hoehe) (setq dz min-hoehe))
|
|
(list
|
|
(/ (+ (car minpt) (car maxpt)) 2.0)
|
|
(/ (+ (cadr minpt) (cadr maxpt)) 2.0)
|
|
(/ (+ (caddr minpt) (caddr maxpt)) 2.0)
|
|
(- (car maxpt) (car minpt))
|
|
(- (cadr maxpt) (cadr minpt))
|
|
dz
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
;; --- Attribute eines INSERT-Blocks als JSON-Objekt lesen ---
|
|
;; ename = Entity-Name des INSERT
|
|
;; Rueckgabe: String wie {"TAG1":"Wert1","TAG2":"Wert2"}
|
|
(defun csv:read-attribs (ename / obj ed typ tag wert parts)
|
|
(setq obj (entnext ename))
|
|
(setq parts nil)
|
|
(while obj
|
|
(setq ed (entget obj))
|
|
(setq typ (cdr (assoc 0 ed)))
|
|
(if (equal typ "SEQEND") (setq obj nil)
|
|
(progn
|
|
(if (equal typ "ATTRIB")
|
|
(setq parts (cons (strcat "\"" (csv:json-escape (cdr (assoc 2 ed)))
|
|
"\":\"" (csv:json-escape (cdr (assoc 1 ed))) "\"")
|
|
parts))
|
|
)
|
|
(setq obj (entnext obj))
|
|
)
|
|
)
|
|
)
|
|
(if parts
|
|
(progn
|
|
(setq parts (reverse parts))
|
|
(strcat "{" (car parts)
|
|
(apply 'strcat (mapcar '(lambda (p) (strcat "," p)) (cdr parts)))
|
|
"}")
|
|
)
|
|
"{}"
|
|
)
|
|
)
|
|
|
|
|
|
;; --- Einen INSERT-Block als JSON-Objekt schreiben ---
|
|
;; include-bbox = T -> zusaetzlich Bounding-Box (Mitte + Ausdehnung) ermitteln
|
|
;; und als "bbox"-Objekt anhaengen (nur fuer EXPORTCSV, nicht fuer EXPORTSIVAS).
|
|
;; "insertpoint" (KOS-String von Position+Rotation des Blocks selbst) und
|
|
;; "k1".."k4" (KOS-Strings der Koordinatensysteme im Block, siehe
|
|
;; csv:get-k-kos-strings) werden immer geschrieben, unabhaengig von
|
|
;; include-bbox -- Kodierung ueber csv:kos-encode (siehe dort).
|
|
(defun csv:block-to-json (ename include-bbox / ed blk-name layer pt rotation handle attribs bbox bbox-json
|
|
insert-quat insert-kos k-list)
|
|
(setq ed (entget ename))
|
|
(setq blk-name (cdr (assoc 2 ed)))
|
|
(setq layer (cdr (assoc 8 ed)))
|
|
(setq pt (cdr (assoc 10 ed)))
|
|
(setq rotation (cdr (assoc 50 ed)))
|
|
(setq handle (cdr (assoc 5 ed)))
|
|
(if (null rotation) (setq rotation 0.0))
|
|
(setq attribs (csv:read-attribs ename))
|
|
(setq insert-quat (csv:z-angle-to-quat (* (/ rotation pi) 180.0)))
|
|
(setq insert-kos (csv:kos-encode
|
|
(car pt) (cadr pt) (if (caddr pt) (caddr pt) 0.0)
|
|
(nth 0 insert-quat) (nth 1 insert-quat) (nth 2 insert-quat) (nth 3 insert-quat)))
|
|
(setq k-list (csv:get-k-kos-strings ename pt rotation))
|
|
(setq bbox-json "")
|
|
(if include-bbox
|
|
(progn
|
|
(setq bbox (csv:get-bbox ename))
|
|
(if bbox
|
|
(setq bbox-json
|
|
(strcat
|
|
",\"bbox\":{"
|
|
"\"cx\":" (rtos (nth 0 bbox) 2 4)
|
|
",\"cy\":" (rtos (nth 1 bbox) 2 4)
|
|
",\"cz\":" (rtos (nth 2 bbox) 2 4)
|
|
",\"dx\":" (rtos (nth 3 bbox) 2 4)
|
|
",\"dy\":" (rtos (nth 4 bbox) 2 4)
|
|
",\"dz\":" (rtos (nth 5 bbox) 2 4)
|
|
"}"
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(strcat
|
|
" {\"block_name\":\"" (csv:json-escape blk-name) "\""
|
|
",\"layer\":\"" (csv:json-escape layer) "\""
|
|
",\"handle\":\"" (csv:json-escape handle) "\""
|
|
",\"x\":" (rtos (car pt) 2 4)
|
|
",\"y\":" (rtos (cadr pt) 2 4)
|
|
",\"z\":" (rtos (if (caddr pt) (caddr pt) 0.0) 2 4)
|
|
",\"rotation\":" (rtos (* (/ rotation pi) 180.0) 2 4)
|
|
",\"attribs\":" attribs
|
|
",\"insertpoint\":\"" insert-kos "\""
|
|
",\"k1\":\"" (csv:k-kos k-list "K1") "\""
|
|
",\"k2\":\"" (csv:k-kos k-list "K2") "\""
|
|
",\"k3\":\"" (csv:k-kos k-list "K3") "\""
|
|
",\"k4\":\"" (csv:k-kos k-list "K4") "\""
|
|
bbox-json
|
|
"}"
|
|
)
|
|
)
|
|
|
|
|
|
;; --- Relevante Bloecke sammeln ---
|
|
;; Filtert INSERT-Entities auf exportierbare Bloecke:
|
|
;; KR_* = Kreisel Compound-Bloecke
|
|
;; AP110* = Omniflo Geraden
|
|
;; Omniflo Boegen/Weichen = rein numerische Blocknamen (SivasNr)
|
|
;; Vario* = VarioFoerderer-Bloecke
|
|
;; GF_* = Gefaellestrecke Compound-Bloecke
|
|
;; Gibt Auswahlsatz zurueck oder nil.
|
|
(defun csv:collect-export-blocks ( / ss-all ss-out i ename ed bname)
|
|
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
|
|
(if (null ss-all) (setq ss-out nil)
|
|
(progn
|
|
(setq ss-out (ssadd))
|
|
(setq i 0)
|
|
(while (setq ename (ssname ss-all i))
|
|
(setq ed (entget ename))
|
|
(setq bname (cdr (assoc 2 ed)))
|
|
(if (or
|
|
(wcmatch bname (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*"))
|
|
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*"))
|
|
(wcmatch bname (export:pattern "pattern_strecke"
|
|
"Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*"))
|
|
;; Rein numerische Namen = Omniflo SivasNr (Boegen/Weichen)
|
|
;; Rein LISP-intern (Python klassifiziert Omniflo stattdessen
|
|
;; ueber exakten Katalog-Match), daher fest im Code statt in
|
|
;; cfg/export.cfg.
|
|
(wcmatch bname "#*")
|
|
)
|
|
(ssadd ename ss-out)
|
|
)
|
|
(setq i (1+ i))
|
|
)
|
|
(if (= (sslength ss-out) 0) (setq ss-out nil))
|
|
)
|
|
)
|
|
ss-out
|
|
)
|
|
|
|
|
|
;; --- HOEHE/DREHUNG aller Omniflo-Elemente still aktualisieren ---
|
|
;; Wird automatisch vor jedem Export aufgerufen.
|
|
(defun omni:update-all-attribs ( / ss i ename ed bname teileart)
|
|
(setq ss (ssget "X" (list (cons 0 "INSERT"))))
|
|
(if ss
|
|
(progn
|
|
(setq i 0)
|
|
(while (setq ename (ssname ss i))
|
|
(setq ed (entget ename))
|
|
(setq bname (cdr (assoc 2 ed)))
|
|
(setq teileart (csv:get-attrib ename "TEILEART"))
|
|
(if (or teileart
|
|
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*")))
|
|
(progn
|
|
(omni:set-hoehe-attrib ename)
|
|
(omni:set-drehung-attrib ename)
|
|
)
|
|
)
|
|
(setq i (1+ i))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
;; --- Gemeinsame Export-Funktion ---
|
|
;; Sammelt relevante Bloecke, schreibt JSON, ruft Python auf.
|
|
;; label = Anzeigename (z.B. "EXPORTCSV")
|
|
;; 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
|
|
;; (siehe csv:block-to-json); fuer EXPORTSIVAS nil.
|
|
(defun csv:run-export (label py-name csv-name include-bbox / ss i ename fh out-pfad
|
|
py-skript ergebnis-pfad cmd first-block count out-dir)
|
|
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren
|
|
(princ (ssg-textf "exp-check-ids" (list label)))
|
|
(ssg-id-check-all)
|
|
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
|
|
(princ (ssg-textf "exp-update-hoehe-drehung" (list label)))
|
|
(omni:update-all-attribs)
|
|
(princ (ssg-textf "exp-collect-blocks" (list label)))
|
|
|
|
(setq ss (csv:collect-export-blocks))
|
|
(if (null ss)
|
|
(progn
|
|
(princ (ssg-textf "exp-no-blocks-found" (list label)))
|
|
(princ)
|
|
)
|
|
(progn
|
|
(setq count (sslength ss))
|
|
(princ (ssg-textf "exp-blocks-found" (list label count)))
|
|
|
|
;; Ausgabeordner: DXFM_RESULTS, ausser *export-test-override* ist gesetzt
|
|
;; (nur waehrend TEST_EXPORT_ALL, siehe tests/test_export_all.lsp).
|
|
;; WICHTIG: Hier bewusst KEIN (setenv "DXFM_RESULTS" ...) verwenden!
|
|
;; (setenv ...) schreibt in BricsCAD dauerhaft in die Profil-Registry
|
|
;; und ueberlebt damit jeden Neustart - ein einziger Testlauf wuerde
|
|
;; DXFM_RESULTS fuer alle zukuenftigen Sitzungen unwiderruflich auf den
|
|
;; Testordner umbiegen. *export-test-override* ist eine reine
|
|
;; Lisp-Variable (nur Prozess-/Sitzungsspeicher) und daher sicher.
|
|
(setq out-dir (if *export-test-override*
|
|
*export-test-override*
|
|
(getenv "DXFM_RESULTS")))
|
|
(if (not (vl-file-directory-p out-dir)) (vl-mkdir out-dir))
|
|
(princ (ssg-textf "exp-output-dir" (list label out-dir)))
|
|
|
|
;; JSON-Datei zusammenbauen
|
|
(setq out-pfad (strcat out-dir "/"
|
|
(export:filename "raw_json_datei")))
|
|
(setq fh (open out-pfad "w"))
|
|
(if (null fh)
|
|
(progn
|
|
(princ (ssg-textf "exp-file-open-error" (list label out-pfad)))
|
|
(princ)
|
|
)
|
|
(progn
|
|
(write-line "[" fh)
|
|
(setq i 0)
|
|
(setq first-block T)
|
|
(while (setq ename (ssname ss i))
|
|
(if (not first-block)
|
|
(write-line "," fh)
|
|
)
|
|
(write-line (csv:block-to-json ename include-bbox) fh)
|
|
(setq first-block nil)
|
|
(setq i (1+ i))
|
|
)
|
|
(write-line "]" fh)
|
|
(close fh)
|
|
(princ (ssg-textf "exp-json-written" (list label out-pfad)))
|
|
|
|
;; Python-Skript aufrufen
|
|
(setq py-skript (strcat (getenv "DXFM_LIB") "/" py-name))
|
|
(if (findfile py-skript)
|
|
(progn
|
|
(setq ergebnis-pfad (strcat out-dir "/" csv-name))
|
|
(setq cmd (strcat "python \"" py-skript "\""
|
|
" \"" out-pfad "\""
|
|
" \"" (getenv "DXFM_DATA") "\""
|
|
" \"" ergebnis-pfad "\""))
|
|
(princ (ssg-textf "exp-calling-python" (list label)))
|
|
(startapp "cmd" (strcat "/c " cmd))
|
|
(princ (ssg-textf "exp-export-started" (list label ergebnis-pfad)))
|
|
)
|
|
(princ (ssg-textf "exp-python-not-found" (list label py-skript)))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(princ)
|
|
)
|
|
|
|
|
|
;; --- EXPORTSIVAS: Sivas-spezifischer CSV-Export mit Summierungszeilen ---
|
|
(defun c:EXPORTSIVAS ()
|
|
(csv:run-export "EXPORTSIVAS"
|
|
(export:filename "sivas_script")
|
|
(export:filename "sivas_csv")
|
|
nil)
|
|
)
|
|
|
|
;; --- EXPORTCSV: Einfache Item-Liste aller Bloecke (ohne Summierung) ---
|
|
;; Ermittelt zusaetzlich je Block eine Bounding-Box (siehe csv:get-bbox),
|
|
;; die im Python-Skript als Position/Boundingbox-Spalten ausgegeben wird.
|
|
(defun c:EXPORTCSV ()
|
|
(csv:run-export "EXPORTCSV"
|
|
(export:filename "csv_script")
|
|
(export:filename "csv_csv")
|
|
T)
|
|
)
|
|
|
|
|
|
;; ============================================================
|
|
;; OMNIFLO HOEHE/DREHUNG AKTUALISIEREN
|
|
;; ============================================================
|
|
|
|
;; --- Attribut-Wert eines INSERT-Blocks nach Tag lesen ---
|
|
(defun csv:get-attrib (ename tag / obj ed typ atag)
|
|
(setq obj (entnext ename))
|
|
(while obj
|
|
(setq ed (entget obj))
|
|
(setq typ (cdr (assoc 0 ed)))
|
|
(if (equal typ "SEQEND")
|
|
(setq obj nil)
|
|
(progn
|
|
(if (and (equal typ "ATTRIB")
|
|
(= (strcase (cdr (assoc 2 ed))) (strcase tag)))
|
|
(progn (setq obj nil) (setq atag (cdr (assoc 1 ed))))
|
|
(setq obj (entnext obj))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
atag
|
|
)
|
|
|
|
;; --- Hoehe eines Omniflo-Blocks aus der Zeichnung ermitteln ---
|
|
;; Liest z-Koordinate des Einfuegepunkts.
|
|
;; Falls der Block KS_EIN, KS_AUS oder K1-K4 enthaelt,
|
|
;; wird die minimale Z-Koordinate dieser Unterelemente + Block-Z verwendet.
|
|
(defun omni:get-block-hoehe (ename / ed bname pt z-insert blk-tbl sub-ent sub-ed sub-bname sub-pt z-list z-min)
|
|
(setq ed (entget ename))
|
|
(setq bname (cdr (assoc 2 ed)))
|
|
(setq pt (cdr (assoc 10 ed)))
|
|
(setq z-insert (if (caddr pt) (caddr pt) 0.0))
|
|
(setq blk-tbl (tblsearch "BLOCK" bname))
|
|
(if (null blk-tbl)
|
|
z-insert
|
|
(progn
|
|
(setq sub-ent (entnext (cdr (assoc -2 blk-tbl))))
|
|
(setq z-list nil)
|
|
(while sub-ent
|
|
(setq sub-ed (entget sub-ent))
|
|
(if (= (cdr (assoc 0 sub-ed)) "INSERT")
|
|
(progn
|
|
(setq sub-bname (strcase (cdr (assoc 2 sub-ed))))
|
|
(if (wcmatch sub-bname
|
|
(export:pattern "pattern_ks_subblocks" "KS_EIN,KS_AUS,K1,K2,K3,K4"))
|
|
(progn
|
|
(setq sub-pt (cdr (assoc 10 sub-ed)))
|
|
(setq z-list (cons (if (caddr sub-pt) (caddr sub-pt) 0.0) z-list))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(setq sub-ent (entnext sub-ent))
|
|
)
|
|
(if z-list
|
|
(progn
|
|
(setq z-min (car z-list))
|
|
(foreach z (cdr z-list)
|
|
(if (< z z-min) (setq z-min z))
|
|
)
|
|
(+ z-insert z-min)
|
|
)
|
|
z-insert
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
;; --- HOEHE-Attribut eines Omniflo-Blocks setzen ---
|
|
;; Liest Z-Koordinate (ggf. aus KS-Unterblock) und schreibt als HOEHE-Attribut.
|
|
(defun omni:set-hoehe-attrib (ename / hoehe)
|
|
(setq hoehe (omni:get-block-hoehe ename))
|
|
(ssg-attrib-set-on ename (list (cons "HOEHE" (rtos hoehe 2 1))))
|
|
hoehe
|
|
)
|
|
|
|
|
|
;; --- DREHUNG-Attribut eines Omniflo-Blocks setzen ---
|
|
;; Liest CAD-Rotationswinkel (Gruppe 50, Bogenmass) und schreibt als DREHUNG-Attribut.
|
|
(defun omni:set-drehung-attrib (ename / ed rotation deg)
|
|
(setq ed (entget ename))
|
|
(setq rotation (cdr (assoc 50 ed)))
|
|
(if (null rotation) (setq rotation 0.0))
|
|
(setq deg (* (/ rotation pi) 180.0))
|
|
(ssg-attrib-set-on ename (list (cons "DREHUNG" (rtos deg 2 1))))
|
|
deg
|
|
)
|
|
|
|
|
|
;; ============================================================
|
|
;; C:OMNI_UPDATE_ATTRIBS - HOEHE und DREHUNG aller Omniflo-Elemente aktualisieren
|
|
;; Setzt HOEHE aus Z-Koordinate (bzw. min. KS-Sub-Block) und
|
|
;; DREHUNG aus CAD-Rotationswinkel fuer alle Bloecke mit TEILEART-Attribut.
|
|
;; ============================================================
|
|
(defun c:OMNI_UPDATE_ATTRIBS ( / ss i ename ed bname teileart count)
|
|
(setq ss (ssget "X" (list (cons 0 "INSERT"))))
|
|
(if (null ss)
|
|
(princ (ssg-text "exp-omni-no-inserts"))
|
|
(progn
|
|
(setq count 0)
|
|
(setq i 0)
|
|
(while (setq ename (ssname ss i))
|
|
(setq ed (entget ename))
|
|
(setq bname (cdr (assoc 2 ed)))
|
|
;; Nur Omniflo-Elemente (erkennbar an TEILEART-Attribut oder AP110-Blockname)
|
|
(setq teileart (csv:get-attrib ename "TEILEART"))
|
|
(if (or teileart
|
|
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*")))
|
|
(progn
|
|
(omni:set-hoehe-attrib ename)
|
|
(omni:set-drehung-attrib ename)
|
|
(setq count (1+ count))
|
|
)
|
|
)
|
|
(setq i (1+ i))
|
|
)
|
|
(princ (ssg-textf "exp-omni-updated" (list count)))
|
|
)
|
|
)
|
|
(princ)
|
|
)
|