export_csv: VF_n/GF_n-Einzelelemente (Boegen, Streckenelemente, Umlenk-/Motorstation) als Proxy-Zeilen exportieren

Bisher tauchten nur der Wrapper selbst, verpackte Separatoren und AS/ES-
Kettenenden als eigene CSV-Zeilen auf - die uebrigen Kettenbausteine
(Boegen, gerade Foerderer-/Gefaellestrecken-Segmente, Umlenk-/Motorstation)
blieben nur in der Wrapper-Blockdefinition verpackt. csv:element-proxies-
erzeugen (analog zu csv:sep-proxies-erzeugen) fuegt fuer jeden Fund eine
temporaere Kopie ein, K1/K2 kommen aus dem EIGENEN KS_EIN/KS_AUS jedes
Elements (nicht dem Gesamt-AS/ES der Kette) und bleiben leer, wo keines
existiert (z.B. das nie tatsaechlich eingefuegte Vario_Kurve_*).

Zusaetzlicher Fix: csv:get-k-kos-strings ignorierte bisher die Skalierung
des Top-Level-Inserts - fuer Staustrecke_SP_1000_mm (das per Laenge nicht-
uniform skaliert wird) waere K2 damit falsch gewesen, unabhaengig von der
tatsaechlich gebauten Segmentlaenge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-09-24 13:08:35 +02:00
co-authored by Claude Sonnet 5
parent 4f1a0e0b5b
commit f751a90076
8 changed files with 445 additions and 18 deletions
+146 -10
View File
@@ -352,7 +352,17 @@
;; Weltpunkt (realer Bug: K1 landete beim Vorzeichen von X/Y gespiegelt weit
;; ausserhalb der eigenen Boundingbox, sichtbar als fehlendes K-Kreuz in
;; sps_skel/HundM05_bbs.svg trotz gefuelltem K1 in der CSV).
(defun csv:get-k-kos-strings (ename pt parent-R / ed bname blk-tbl sub-ent sub-ed
;; sx/sy/sz: Skalierung des Top-Level-Inserts ename selbst (Gruppencodes
;; 41/42/43), auf sub-pt-real anzuwenden BEVOR in Weltkoordinaten gedreht
;; wird - noetig fuer nicht-uniform skalierte Bloecke wie Staustrecke_SP_
;; 1000_mm (per Laenge auf der X-Achse gestreckt, siehe insert-inclined-
;; scaled-block, Lisp/ssg_ks_insert.lsp:260). Ohne diese Skalierung wuerde
;; K2 dort so berichtet, als waere der Block unskaliert (1:1) eingefuegt -
;; derselbe Fehler, den csv:vfgf-ks-loc fuer AS-/ES-Elemente schon per
;; elem-sx/elem-sy/elem-sz vermeidet (siehe dort). Fuer alle bisherigen
;; Aufrufer (Omniflo/Kreisel/AS-ES-Marker, durchweg uniform mit 1.0 skaliert
;; eingefuegt) ist die Multiplikation ein No-Op - unveraendertes Verhalten.
(defun csv:get-k-kos-strings (ename pt parent-R sx sy sz / ed bname blk-tbl sub-ent sub-ed
sub-bname sub-pt sub-extrusion sub-pt-real world-pt
target is-real result)
(setq ed (entget ename))
@@ -389,6 +399,9 @@
(setq sub-pt (list (car sub-pt) (cadr sub-pt) 0.0)))
(setq sub-extrusion (cond ((cdr (assoc 210 sub-ed))) ('(0.0 0.0 1.0))))
(setq sub-pt-real (mat3-mul-vec3 (mat3-ocs-matrix sub-extrusion) sub-pt))
(setq sub-pt-real (list (* sx (car sub-pt-real))
(* sy (cadr sub-pt-real))
(* sz (caddr sub-pt-real))))
(setq world-pt (csv:local-to-world pt parent-R sub-pt-real))
(setq result (cons
(cons target
@@ -650,7 +663,7 @@
;; 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 extrusion R-world
handle attribs bbox bbox-json
handle attribs bbox bbox-json psx psy psz
insert-quat insert-kos k-list warnung-json warnung-eintrag
zuordnung-fix-json zuordnung-fix-eintrag
strecke-typ-json strecke-typ-eintrag
@@ -683,7 +696,10 @@
(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 R-world))
(setq psx (cond ((cdr (assoc 41 ed))) (1.0)))
(setq psy (cond ((cdr (assoc 42 ed))) (1.0)))
(setq psz (cond ((cdr (assoc 43 ed))) (1.0)))
(setq k-list (csv:get-k-kos-strings ename pt R-world psx psy psz))
;; Omniflo-Geraden fuehren keine echten K-Bloecke - K1/K2 aus der Geometrie
;; (Anfang/Ende ueber Laenge + Rotation) synthetisieren, damit sie im Export
;; wie echte Anschluss-Koordinatensysteme erscheinen und in der Nachbar-
@@ -856,6 +872,24 @@
"Separator_SP_2D,Separator_SP_3D"
)
;; --- Muster der uebrigen verpackten VF_n/GF_n-Bausteine (Boegen, gerade
;; Foerderer-/Gefaellestrecken-Segmente, Umlenk-/Motorstation) ---
;; Wie csv:pattern-nested-separator/-schleus: diese Bausteine stecken als
;; Sub-INSERTs in der Wrapper-Blockdefinition und sind fuer (ssget "X" ...)
;; unsichtbar. EIN kombiniertes Muster fuer alle vier Familien (statt vier
;; getrennter csv:nested-welt-records-Aufrufe) haelt den zusaetzlichen
;; Scan-Aufwand auf EINEN weiteren Durchlauf der ganzen Zeichnung beschraenkt
;; - csv:element-teileart klassifiziert danach anhand des Blocknamens, in
;; welche TeileArt-Familie ein Treffer faellt.
(defun csv:pattern-nested-element ()
(strcat
(export:pattern "pattern_nested_bogen" "Gefaellebogen_*,Vario_Bogen_*,Vario_Kurve_*")
"," (export:pattern "pattern_nested_streckenelement" "Staustrecke_SP_1000_mm*")
"," (export:pattern "pattern_nested_umlenkstation" "Vario_Umlenkstation_*")
"," (export:pattern "pattern_nested_motorstation" "Vario_Motorstation_*")
)
)
;; --- Muster der verpackten AS-/ES-Schleuselemente ---
;; AS_Element_*/ES_Element_* sind die beiden Kettenenden einer VF_n/GF_n-
;; Strecke (Ein-/Ausschleusung zum Kreisel). Sie stecken - wie die Separator-
@@ -881,6 +915,30 @@
)
)
;; --- TeileArt-Familie eines ausgepackten Elements aus seinem Blocknamen ---
;; Reiner Namensklassifikator (parallel zu csv:proxy-typ), file-frei testbar
;; (tests/test_unit.lsp). Vario_Bogen_* deckt sowohl die _TEF_-Variante (VF-
;; Vertikalboegen) als auch die reine Variante ohne _TEF_ (GF-eigene Vertikal-
;; boegen) ab - beide auf denselben TeileArt-Bucket "BOGEN", darum ist -
;; anders als bei vfl-kette-typ (vf_linienzug.lsp) - keine _TEF_-zuerst-
;; Reihenfolge noetig. Vario_Kurve_* ist laut Code-Kommentar (vf_linienzug.lsp)
;; aktuell nirgends im Projekt tatsaechlich eingefuegt, aber zur Vollstaendig-
;; keit mit aufgenommen. Liefert nil fuer alles andere (sollte bei einem
;; ueber csv:pattern-nested-element gefundenen Treffer nie vorkommen -
;; defensive Absicherung, falls Muster und cond je auseinanderlaufen).
(defun csv:element-teileart (bname / bn)
(setq bn (strcase bname))
(cond
((wcmatch bn "GEFAELLEBOGEN_*") "BOGEN")
((wcmatch bn "VARIO_KURVE_*") "BOGEN")
((wcmatch bn "VARIO_BOGEN_*") "BOGEN")
((wcmatch bn "STAUSTRECKE_SP_1000_MM*") "STRECKENELEMENT")
((wcmatch bn "VARIO_UMLENKSTATION_*") "UMLENKSTATION")
((wcmatch bn "VARIO_MOTORSTATION_*") "MOTORSTATION")
(t nil)
)
)
;; --- Muster der Compound-("Wrapper"-)Bloecke, deren Definition ueberhaupt
;; auf verpackte Separator_SP-Symbole durchsucht wird (KR_n/KREISEL_n/
;; ECKRAD_n/VF_n/GF_n) - alle anderen INSERTs (Omniflo, eigenstaendige
@@ -1120,6 +1178,57 @@
(reverse proxy-liste)
)
;; --- Temporaere Kopie je verpacktem uebrigen VF_n/GF_n-Baustein (Bogen/
;; Streckenelement/Umlenkstation/Motorstation) ---
;; Analog zu csv:sep-proxies-erzeugen, aber ohne die dortige "aussen"/XDATA-
;; Zusatzlogik (die existiert nur fuer Separator wegen SSG_SEP_TYP) und ohne
;; ZUORDNUNG-Zuweisung: diese Elemente sind keine einem Carrier zugeordneten
;; Sensoren, sondern die Geometrie des Carriers (VF_n/GF_n) selbst - ARTINR/
;; LAYER kommen direkt aus den ATTDEFs der kopierten Original-Blockdefinition
;; (ssg-attrib-read auf die Kopie), wie bei Separator.
;;
;; proxy-liste-Eintrag: (proxy-ename wrapper-ename teileart-key (x y z)).
;; teileart-key (aus csv:element-teileart) wird NICHT ins JSON geschrieben -
;; Python klassifiziert die TeileArt ohnehin ueber block_name + die vier
;; pattern_nested_*-Keys aus cfg/export.cfg, wie jede andere Familie auch.
(defun csv:element-proxies-erzeugen ( / ms recs rec proxy proxy-liste)
(setq recs (csv:nested-welt-records (csv:pattern-nested-element)))
(if recs
(progn
(setq ms (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object))))
(foreach rec recs
(setq proxy (csv:block-einfuegen-welt ms (nth 2 rec) (nth 3 rec) (nth 4 rec)
(nth 5 rec) (nth 6 rec) (nth 7 rec)
(cdr (assoc 2 (entget (nth 0 rec))))))
(if proxy
(setq proxy-liste
(cons (list proxy (nth 0 rec) (csv:element-teileart (nth 2 rec)) (nth 3 rec))
proxy-liste)))
)
)
)
(reverse proxy-liste)
)
;; --- Temporaere Element-Kopien nach dem Export wieder entfernen ---
;; Eigene, minimale Loeschfunktion statt csv:sep-proxies-loeschen wieder-
;; zuverwenden: *cs-sep-fix-by-handle*/*cs-sep-typ-by-handle*/
;; *csv-sepliste-id-by-handle* bleiben separator-/AS-/ES-spezifisch, diese
;; Elemente haben keine Entsprechung davon. Nutzt denselben Debug-Schalter
;; "csv_leave_proxies" wie Separator, fuer konsistentes Debugging.
(defun csv:element-proxies-loeschen (proxy-liste / behalten)
(setq behalten (and (car (atoms-family 1 '("DBG-SCHALTER-P")))
(dbg-schalter-p "csv_leave_proxies")))
(if behalten
(princ (strcat "\n[EXPORT] csv_leave_proxies=1: "
(itoa (length proxy-liste))
" Element-Proxy-Kopie(n) bleiben in der Zeichnung"
" (Abschalten: (dbg-schalter-off \"csv_leave_proxies\"))"))
(foreach rec proxy-liste (entdel (car rec)))
)
(princ)
)
;; --- ZUORDNUNG der temporaeren Separator-Kopien aus ihrem Wrapper setzen ---
;; MUSS nach ssg-id-check-all laufen: erst dann steht die ID des Wrapper-
;; Blocks (VF_n/GF_n/KREISEL_n) fest - ein frisch gebauter Wrapper kann beim
@@ -1741,6 +1850,21 @@
(wcmatch bname (export:pattern "pattern_eingang" "Eingang*"))
(wcmatch bname (export:pattern "pattern_ausgang" "Ausgang*"))
(wcmatch bname (export:pattern "pattern_geruest" "GR_*"))
;; Uebrige verpackte VF_n/GF_n-Bausteine (Boegen, gerade
;; Foerderer-/Gefaellestrecken-Segmente, Umlenk-/Motorstation) -
;; siehe csv:element-proxies-erzeugen. pattern_strecke oben
;; wuerde die meisten davon schon per Praefix (Vario*/
;; Staustrecke*) miterfassen, NUR Gefaellebogen_* nicht - die
;; vier expliziten Klauseln schliessen diese Luecke und machen
;; die Zugehoerigkeit klar. Kein Duplikate-Risiko: eine ssadd-
;; Selektion kann dieselbe Entity nicht zweimal enthalten, egal
;; wie viele Klauseln hier matchen.
(wcmatch bname (export:pattern "pattern_nested_bogen"
"Gefaellebogen_*,Vario_Bogen_*,Vario_Kurve_*"))
(wcmatch bname (export:pattern "pattern_nested_streckenelement"
"Staustrecke_SP_1000_mm*"))
(wcmatch bname (export:pattern "pattern_nested_umlenkstation" "Vario_Umlenkstation_*"))
(wcmatch bname (export:pattern "pattern_nested_motorstation" "Vario_Motorstation_*"))
;; Rein numerische Namen = Omniflo SivasNr (Boegen/Weichen)
;; Rein LISP-intern (Python klassifiziert Omniflo stattdessen
;; ueber exakten Katalog-Match), daher fest im Code statt in
@@ -1794,7 +1918,7 @@
;; die bbox-Fehlerdiagnose zuruecksetzen. Rueckgabe: die Liste ALLER erzeugten
;; Kopien - Separatoren UND AS/ES-Schleuselemente (der Aufrufer entfernt sie am
;; Ende wieder). label dient nur den Meldungen.
(defun csv:export-vorbereiten (label / sep-proxies asmarks)
(defun csv:export-vorbereiten (label / sep-proxies asmarks element-proxies)
;; Verpackte Separator_SP-Symbole (siehe csv:sep-proxies-erzeugen) VOR der
;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende
;; von csv:run-export wieder entfernt (csv:sep-proxies-loeschen).
@@ -1804,6 +1928,13 @@
;; (ZZ_ASES_MARKER): er macht das Kettenende in der Zeichnung sichtbar und
;; traegt dessen ID ueber Exportlaeufe hinweg (siehe csv:asmark-*).
(setq asmarks (csv:asmark-aktualisieren))
;; Die uebrigen Kettenbausteine (Boegen, gerade Foerderer-/Gefaellestrecken-
;; Segmente, Umlenk-/Motorstation) ebenso wie die Separatoren durch
;; temporaere Kopien ersetzen - siehe csv:element-proxies-erzeugen. MUSS wie
;; sep-proxies VOR ssg-id-check-all stehen, damit die frischen Kopien in
;; dessen Lauf mit nummeriert werden (siehe ssg-id-collect-blocks,
;; ssg_id.lsp).
(setq element-proxies (csv:element-proxies-erzeugen))
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren.
;; Erst hier - also erst nachdem ALLE Kopien aus allen Wrapper-Bloecken
;; stehen - wird ueberhaupt eine ID vergeben. ssg-id-check-all ermittelt
@@ -1862,7 +1993,7 @@
(setq *csv-bbox-fail-first* nil)
;; Nur die TEMPORAEREN Kopien zurueckgeben - die AS/ES-Marker bleiben
;; bewusst in der Zeichnung stehen und duerfen nicht mitgeloescht werden.
sep-proxies)
(list sep-proxies element-proxies))
;; --- Python-Export-Skript aufrufen und Ergebnis auswerten ---------------
;; Aus csv:run-export ausgelagert: das fertige raw-JSON (out-pfad) an das
@@ -1927,11 +2058,14 @@
;; 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
first-block count out-dir sep-proxies)
first-block count out-dir sep-proxies element-proxies
vorbereitung-ergebnis)
;; Zeichnungszustand fuer den Export herrichten (Proxies, IDs, Zuordnung,
;; Attribut-Update, Sep/Scan-Zaehlung, REGENALL, bbox-Reset) - ausgelagert.
;; Rueckgabe: alle erzeugten Kopien (unten wieder zu entfernen).
(setq sep-proxies (csv:export-vorbereiten label))
;; Rueckgabe: (sep-proxies element-proxies), beide unten wieder zu entfernen.
(setq vorbereitung-ergebnis (csv:export-vorbereiten label))
(setq sep-proxies (nth 0 vorbereitung-ergebnis))
(setq element-proxies (nth 1 vorbereitung-ergebnis))
(princ (ssg-textf "exp-collect-blocks" (list label)))
(setq ss (csv:collect-export-blocks))
@@ -1997,10 +2131,12 @@
)
)
)
;; Temporaere Separator-/AS-/ES-Kopien wieder entfernen - sie haben ihren
;; Temporaere Separator-/Element-Kopien wieder entfernen - sie haben ihren
;; Zweck (ID-Vergabe + JSON-Zeile ueber die obigen, unveraenderten
;; Sammel-Funktionen) erfuellt.
;; Sammel-Funktionen) erfuellt. Die AS-/ES-Marker (ZZ_ASES_MARKER) bleiben
;; bewusst stehen (siehe csv:export-vorbereiten).
(csv:sep-proxies-loeschen sep-proxies)
(csv:element-proxies-loeschen element-proxies)
(princ)
)