export: Kettenbausteine ueber KS verketten, Einheiten neu gruppieren

K1/K2 und Boundingbox je Export-Zeile folgen jetzt dem echten
Kettenbaustein statt seinem Stellvertreter:
- Separator (all_elements): KS + bbox des 300-mm-Trennstegs statt des
  Sensor-Symbols; separator_track/group_vf_gf behalten das Symbol
- AS/ES (all_elements, group_vf_gf): KS + bbox des AS-/ES-Elements statt
  des 200x200-Markers ohne K
- gemeinsamer Kern csv:ks-welt-punkte; VF/GF-Wrapper-K1/K2 = K1 des AS
  bzw. K2 des ES

Gruppierter Export neu (sepliste-/XDATA-Algorithmus entfernt):
- Bausteine je Wrapper als Vektorkette K2 -> K1 ordnen (Start am AS)
- Foerdereinheit = Separator vor der Umlenkstation bis Motorstation,
  alles dazwischen Gefaelleeinheit (csv:kette-einheiten, Unit-Tests)
- je Einheit ein temporaerer Proxy mit 3D-Boundingbox, K1/K2, Laenge und
  Anzahl Boegen; wird nach dem Export geloescht (ausser csv_leave_proxies)
- IDs neuer Export-Objekte entlang der Kette AS -> ES aufsteigend

Weiteres:
- ssg-make-layer ohne _LAYER-Befehl fuer bestehende Layer (Konsole)
- einheitliche, zweisprachige Meldungen fuer behaltene Hilfskopien
- bin/show_exportcsv.bat: Export-CSV als Boundingbox-SVG (sps_skel)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-09-25 20:25:29 +02:00
co-authored by Claude Opus 5.5
parent bc481de901
commit 394d476d56
13 changed files with 1017 additions and 1037 deletions
+52 -77
View File
@@ -411,86 +411,61 @@
(tu-eq "csv:element-teileart/unbekannt" nil
(csv:element-teileart "AS_Element_90_links"))
;; --- csv:sepliste-partition: sepliste in Foerdereinheit-/Gefaelleeinheit-
;; Spannen aufteilen (Lauflaengen-Kodierung ueber den effektiven Typ) -----
;; Reine Rechenfunktion auf (typ lfdnr strecke-typ welt-pt)-Tupeln, hier
;; ohne Zeichnung testbar (welt-pt wird von der Partitionierung selbst
;; nicht gebraucht, darum ueberall nil).
;; --- csv:kette-typ: Bausteintyp einer VF_n/GF_n-Kette aus dem Blocknamen --
(tu-eq "csv:kette-typ/as" "AS" (csv:kette-typ "AS_Element_90_links_3D"))
(tu-eq "csv:kette-typ/es" "ES" (csv:kette-typ "ES_Element_30_rechts_2D"))
(tu-eq "csv:kette-typ/sep" "SEP" (csv:kette-typ "Staustrecke_Separator_SP_300_mm_3D"))
(tu-eq "csv:kette-typ/streckenelement" "STRECKENELEMENT"
(csv:kette-typ "Staustrecke_SP_1000_mm_3D"))
(tu-eq "csv:kette-typ/umlenk" "UMLENKSTATION"
(csv:kette-typ "Vario_Umlenkstation_500mm_rechts_3D"))
(tu-nil "csv:kette-typ/symbol-ist-kein-baustein" (csv:kette-typ "Separator_SP_3D"))
;; HundM05 VF_1-Beispiel (echt verifiziert): AS, SEP2=VF, SEP3=GF, SEP4=GF,
;; SEP5=VF, ES -> 3 Einheiten VF,GF,VF. SEP3+SEP4 bracketen GEMEINSAM eine
;; einzige Gefaelleeinheit (nicht zwei separate).
(setq _sep-hundm05
'(("AS" 1 nil nil) ("SEP" 2 "VF" nil) ("SEP" 3 "GF" nil)
("SEP" 4 "GF" nil) ("SEP" 5 "VF" nil) ("ES" 6 nil nil)))
(tu-eq "csv:sepliste-partition/hundm05"
'(("VF" (1 2)) ("GF" (3 4)) ("VF" (5 6)))
(csv:sepliste-partition _sep-hundm05))
;; Minimalfall: nur AS-SEP(VF)-ES, kein Typwechsel -> 1 Foerdereinheit ueber
;; die ganze Kette.
(setq _sep-minimal '(("AS" 1 nil nil) ("SEP" 2 "VF" nil) ("ES" 3 nil nil)))
(tu-eq "csv:sepliste-partition/minimal"
'(("VF" (1 2 3)))
(csv:sepliste-partition _sep-minimal))
;; Einzel-GF-Fall: die Gefaelleeinheit wird nur von EINEM SEP (nicht zwei)
;; UND dem nachfolgenden ES bracketiert.
(setq _sep-einzel-gf
'(("AS" 1 nil nil) ("SEP" 2 "VF" nil) ("SEP" 3 "GF" nil) ("ES" 4 nil nil)))
(tu-eq "csv:sepliste-partition/einzel-gf"
'(("VF" (1 2)) ("GF" (3 4)))
(csv:sepliste-partition _sep-einzel-gf))
;; 3+ gleichtypige SEP-Eintraege hintereinander muessen weiterhin EINE
;; Einheit ergeben (nicht mehrere) - Lauflaengen-Regel, kein Sonderfall.
(setq _sep-dreifach-gf
'(("AS" 1 nil nil) ("SEP" 2 "VF" nil) ("SEP" 3 "GF" nil)
("SEP" 4 "GF" nil) ("SEP" 5 "GF" nil) ("SEP" 6 "VF" nil) ("ES" 7 nil nil)))
(tu-eq "csv:sepliste-partition/dreifach-gf"
'(("VF" (1 2)) ("GF" (3 4 5)) ("VF" (6 7)))
(csv:sepliste-partition _sep-dreifach-gf))
;; Kette beginnend mit GF (AS erbt den Typ des ersten SEP-Eintrags, hier GF
;; statt des sonstigen Default "VF").
(setq _sep-startet-gf
'(("AS" 1 nil nil) ("SEP" 2 "GF" nil) ("ES" 3 nil nil)))
(tu-eq "csv:sepliste-partition/startet-gf"
;; --- csv:kette-einheiten: geordnete Typfolge in VF-/GF-Einheiten schneiden
;; VF = Separator vor der Umlenkstation ... Motorstation; alles dazwischen
;; GF; AS/ES gehoeren zu keiner Einheit. Reine Rechenfunktion.
;; HundM05 VF_5 (Bausteinfolge aus der Testzeichnung, per KS-Kette geordnet):
;; GF(1) VF(2..10) GF(11 12) VF(13..25) GF(26 27) - passt zu L_GF_m
;; "0.727,0.201,0.318" des Wrappers (drei Gefaelleabschnitte).
(setq _kette-hundm05
'("AS" "STRECKENELEMENT" "SEP" "UMLENKSTATION" "BOGEN" "STRECKENELEMENT"
"BOGEN" "STRECKENELEMENT" "BOGEN" "BOGEN" "MOTORSTATION" "SEP"
"STRECKENELEMENT" "SEP" "UMLENKSTATION" "BOGEN" "STRECKENELEMENT" "BOGEN"
"BOGEN" "BOGEN" "STRECKENELEMENT" "BOGEN" "BOGEN" "STRECKENELEMENT" "BOGEN"
"MOTORSTATION" "STRECKENELEMENT" "SEP" "ES"))
(tu-eq "csv:kette-einheiten/hundm05"
'(("GF" (1)) ("VF" (2 3 4 5 6 7 8 9 10)) ("GF" (11 12))
("VF" (13 14 15 16 17 18 19 20 21 22 23 24 25)) ("GF" (26 27)))
(csv:kette-einheiten _kette-hundm05))
;; Reine Gefaellestrecke ohne Stationen -> eine einzige GF.
(tu-eq "csv:kette-einheiten/nur-gf"
'(("GF" (1 2 3)))
(csv:sepliste-partition _sep-startet-gf))
(csv:kette-einheiten '("AS" "STRECKENELEMENT" "BOGEN" "SEP" "ES")))
;; Kette beginnt direkt mit dem Foerderer (Separator gleich hinter dem AS),
;; endet mit der Motorstation vor dem ES -> nur eine VF, keine leeren GF.
(tu-eq "csv:kette-einheiten/nur-vf"
'(("VF" (1 2 3 4)))
(csv:kette-einheiten '("AS" "SEP" "UMLENKSTATION" "STRECKENELEMENT" "MOTORSTATION" "ES")))
;; Umlenkstation ohne Separator davor beginnt die VF selbst; der Separator,
;; der NICHT vor einer Umlenkstation steht, bleibt im Gefaelle.
(tu-eq "csv:kette-einheiten/umlenk-ohne-sep"
'(("GF" (1 2)) ("VF" (3 4 5)))
(csv:kette-einheiten '("AS" "SEP" "BOGEN" "UMLENKSTATION" "STRECKENELEMENT" "MOTORSTATION" "ES")))
;; --- csv:dot2: 2D-Skalarprodukt ---
(tu-eqf "csv:dot2/achsen" 0.0 (csv:dot2 '(1.0 0.0) '(0.0 1.0)))
(tu-eqf "csv:dot2/parallel" 6.0 (csv:dot2 '(2.0 0.0) '(3.0 0.0)))
;; --- csv:element-unit-zuordnung: Elemente per Kettenrichtungs-Projektion
;; den Einheiten-Spannen zuordnen und INNERHALB jeder Spanne nach der
;; Projektion sortieren -----------------------------------------------
;; Gerade Kette AS(0,0) -SEP2(VF, 10,0)- SEP3(GF, 20,0) -ES(30,0)- entlang
;; der X-Achse. unit-spans: VF=[1,2], GF=[2,3] (SEP2 gehoert zu beiden
;; Spannen als Grenzmarker, siehe csv:sepliste-partition).
(setq _sep-proj
'(("AS" 1 nil (0.0 0.0 0.0)) ("SEP" 2 "VF" (10.0 0.0 0.0))
("SEP" 3 "GF" (20.0 0.0 0.0)) ("ES" 4 nil (30.0 0.0 0.0))))
(setq _spans-proj (csv:sepliste-partition _sep-proj))
(tu-eq "csv:sepliste-partition/proj-spans" '(("VF" (1 2)) ("GF" (3 4))) _spans-proj)
;; Zwei Elemente: eines bei x=5 (klar in der VF-Spanne [0,10]), eines bei
;; x=17 (in der Luecke zwischen den Spannen-Grenzpunkten [10,20], aber
;; naeher an der GF-Grenze 20 als an der VF-Grenze 10 - eindeutig GF, kein
;; Gleichstand wie bei x=15 in der Luecken-Mitte).
(setq _elem-vf (list 'PROXY1 'WRAPPER "STRECKENELEMENT" '(5.0 0.0 0.0)))
(setq _elem-gf (list 'PROXY2 'WRAPPER "STRECKENELEMENT" '(17.0 0.0 0.0)))
(setq _zuord (csv:element-unit-zuordnung _spans-proj _sep-proj (list _elem-vf _elem-gf)))
(tu-eq "csv:element-unit-zuordnung/vf-treffer" (list _elem-vf) (nth 0 _zuord))
(tu-eq "csv:element-unit-zuordnung/gf-treffer" (list _elem-gf) (nth 1 _zuord))
;; Sortierung innerhalb einer Spanne: zwei Elemente in der VF-Spanne,
;; absichtlich in "falscher" Reihenfolge uebergeben - das Ergebnis muss
;; nach x sortiert sein (frueheres x zuerst).
(setq _elem-a (list 'PROXY_A 'WRAPPER "BOGEN" '(8.0 0.0 0.0)))
(setq _elem-b (list 'PROXY_B 'WRAPPER "BOGEN" '(2.0 0.0 0.0)))
(setq _zuord2 (csv:element-unit-zuordnung _spans-proj _sep-proj (list _elem-a _elem-b)))
(tu-eq "csv:element-unit-zuordnung/sortiert" (list _elem-b _elem-a) (nth 0 _zuord2))
;; --- csv:kette-ordnen: Vektorkette K2 -> K1, unabhaengig von der
;; Eingangsreihenfolge; Start am AS. rec wird dafuer nicht gebraucht (nil).
(setq _t-as (list "AS" nil '(0.0 0.0 0.0) '(10.0 0.0 0.0)))
(setq _t-s1 (list "STRECKENELEMENT" nil '(10.0 0.0 0.0) '(20.0 0.0 0.0)))
(setq _t-b (list "BOGEN" nil '(20.0 0.0 0.0) '(20.0 10.0 0.0)))
(setq _t-es (list "ES" nil '(20.0 10.0 0.0) '(30.0 10.0 0.0)))
(tu-eq "csv:kette-ordnen/verwuerfelt"
(list _t-as _t-s1 _t-b _t-es)
(csv:kette-ordnen (list _t-b _t-es _t-as _t-s1) "TEST"))
;; Innerhalb der Toleranz (2 mm) gilt ein Anschluss noch als geschlossen.
(setq _t-s1b (list "STRECKENELEMENT" nil '(11.5 0.0 0.0) '(20.0 0.0 0.0)))
(tu-eq "csv:kette-ordnen/toleranz"
(list _t-as _t-s1b)
(csv:kette-ordnen (list _t-s1b _t-as) "TEST"))
;; --- csv:export-modus-setzen / -p-Praedikate: 3 Modi + Ungueltig-Schutz --
;; Bestehenden Modus sichern/wiederherstellen, damit dieser Testblock keine