[FIX] Eindeutige IDs und korrekte ZUORDNUNG der verpackten Separatoren
Beim CSV-Export von Mubea trugen Gefaellestrecke/VF/Kreisel und die aus ihnen erzeugten Separator-Kopien dieselbe ID (26 Dubletten 0001-0026), und die ZUORDNUNG der Separatoren zeigte auf den falschen Carrier. ssg-id-check-all: neue Phase 0 ermittelt das globale Maximum der bereits vergebenen IDs, BEVOR die erste neue ID vergeben wird (ausgelagert nach ssg-id-max-in-ss, das auch ssg-id-max nutzt). Bisher wurde max-id erst waehrend Phase 1 mitgezogen (Start 0); da (ssget "X" ...) in BricsCAD die zuletzt erzeugten Entities zuerst liefert, standen die noch ID-losen Separator-Kopien ganz vorne und bekamen 0001, 0002, ... - genau die IDs der weiter hinten liegenden Wrapper-Bloecke. Phase 2 konnte das nicht heilen, weil frisch vergebene IDs nicht in id-map landen. ssg-collect-nested-inserts: fuehrt Position, Z-Drehung und Skalierung jetzt ueber alle Verschachtelungsebenen mit (Records statt nackter Entity-Namen). Vorher gab die Rekursion Entities tieferer Ebenen mit ihrer ROH-Position aus der Zwischen-Blockdefinition zurueck - zwei Separatoren an derselben lokalen Stelle in zwei verschieden platzierten Zwischenbloecken landeten dadurch auf exakt derselben Weltposition. csv:sep-proxies-erzeugen: die Kopie steht jetzt exakt auf Position/Drehung/ Skalierung ihres verpackten Vorbilds; der kosmetische Versatz von 500 mm ist weg (er kippte Separatoren am Kettenende in die Boundingbox des Nachbar- Carriers). Der vla-InsertBlock-Aufruf ist gekapselt, damit ein Sonderfall nur diese eine Kopie ausfallen laesst statt den ganzen Export abzubrechen. csv:sep-proxies-zuordnung-setzen (neu, laeuft NACH ssg-id-check-all, da ein frisch gebauter Wrapper vorher keine ID hat): fuer einen VERPACKTEN Separator ist der Carrier bekannt - es ist der Wrapper, in dem er steckt. ZUORDNUNG wird daher auf die Wrapper-ID gesetzt (Separator in VF 0010 -> ZUORDNUNG 0010) und ueber *cs-sep-fix-by-handle* festgenagelt: cs-zuordnung-lauf uebernimmt sie unveraendert statt sie geometrisch neu zu raten, und csv:block-to-json reicht sie als "zuordnung_fix" an export_csv.py durch. Ohne das ueberschrieb compute_sensor_zuordnung (Prioritaet GF > Foerderer > Kreiselhaelfte) den bekannten Wert - in der Mubea-Zeichnung landeten 26 in VF/GF verpackte Separatoren so bei einer Kreiselhaelfte. export_csv.py: respektiert "zuordnung_fix" und ersetzt eine vorhandene ZUORDNUNG nicht mehr durch "nicht zugeordnet" - nur ein echter Treffer ueberschreibt, wie im Docstring von compute_sensor_zuordnung beschrieben. tests/test_export_ids.py (neu): prueft je tests/output/*_export.csv, dass jede TeileId hoechstens einmal vorkommt und jede Sensor-Zuordnung auf eine existierende TeileId zeigt. Gegen die fehlerhafte Mubea-CSV schlaegt der Test mit allen 26 Dubletten an. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -395,7 +395,8 @@ Sammelt INSERT-Blöcke aus der Zeichnung und exportiert sie als JSON oder ruft P
|
||||
| `c:EXPORTSIVAS` | Alle Blöcke als JSON sammeln und `export_sivas.py` aufrufen (Sivas-CSV) |
|
||||
| `c:EXPORTCSV` | Alle Blöcke als JSON sammeln und `export_csv.py` aufrufen (einfache Item-Liste, inkl. Omniflo Bogen/Weiche/Gerade und abschließender "Omniflo Sum"-Zeile) |
|
||||
| `csv:collect-export-blocks` | Alle INSERT-Blöcke der Zeichnung als JSON-Array sammeln |
|
||||
| `csv:run-export py-skript json-datei output-csv` | Python-Skript mit gesammelten Daten aufrufen; legt vorher fuer in VF_n/GF_n/KREISEL_n **verpackte** Separator_SP_2D/_3D-Sub-INSERTs (Sensor-Symbol; siehe `ssg-collect-nested-inserts` in `ssg_core.lsp` - NICHT der interne Staustrecke_Separator_SP-Trennsteg) je eine echte, temporaere Kopie daneben an (`csv:sep-proxies-erzeugen`), damit sie ganz normal ueber `ssg-id-check-all`/`csv:collect-export-blocks` erfasst werden, und entfernt sie danach wieder (`csv:sep-proxies-loeschen`) |
|
||||
| `csv:run-export py-skript json-datei output-csv` | Python-Skript mit gesammelten Daten aufrufen; legt vorher fuer in VF_n/GF_n/KREISEL_n **verpackte** Separator_SP_2D/_3D-Sub-INSERTs (Sensor-Symbol; siehe `ssg-collect-nested-inserts` in `ssg_core.lsp` - NICHT der interne Staustrecke_Separator_SP-Trennsteg) je eine echte, temporaere Kopie **an derselben Stelle** an (`csv:sep-proxies-erzeugen`, Position/Drehung/Skalierung ueber alle Verschachtelungsebenen hinweg identisch zum Vorbild - kein kosmetischer Versatz mehr, der die geometrische Sensor-Zuordnung verfaelschen wuerde), damit sie ganz normal ueber `ssg-id-check-all`/`csv:collect-export-blocks` erfasst werden, und entfernt sie danach wieder (`csv:sep-proxies-loeschen`). Reihenfolge ist wesentlich: **erst alle** Kopien aus allen Wrappern erzeugen, **dann** `ssg-id-check-all` (vergibt IDs oberhalb des globalen Maximums), **dann** `csv:sep-proxies-zuordnung-setzen` |
|
||||
| `csv:sep-proxies-zuordnung-setzen proxy-liste` | ZUORDNUNG jeder Separator-Kopie auf die **ID ihres Wrapper-Blocks** setzen (ein in VF_n mit ID 0010 verpackter Separator erhaelt ZUORDNUNG 0010) und die Zuordnung ueber `*cs-sep-fix-by-handle*` fuer `cs-zuordnung-lauf` festnageln - fuer verpackte Separatoren ist der Carrier bekannt und wird nicht per Boundingbox geraten. Muss NACH `ssg-id-check-all` laufen, da ein frisch gebauter Wrapper vorher noch keine ID hat |
|
||||
| `csv:read-attribs ename` | Attribute eines INSERT als JSON-Objekt lesen |
|
||||
| `c:OMNI_UPDATE_ATTRIBS` | HOEHE/DREHUNG aller Omniflo-Elemente aus der Zeichnung aktualisieren |
|
||||
|
||||
|
||||
+55
-4
@@ -60,6 +60,16 @@
|
||||
;;; Box, damit Sensoren knapp am Rand noch erfasst werden.
|
||||
;;; 6. Bei zugeordneten Sensoren wird die ID des Carriers (Attribut
|
||||
;;; "ID") in das Sensor-Attribut ZUORDNUNG geschrieben.
|
||||
;;; 6a. AUSNAHME zu 1-5: Separatoren, deren Entity-Handle in
|
||||
;;; *cs-sep-fix-by-handle* steht, werden NICHT geometrisch
|
||||
;;; zugeordnet, sondern dem dort genannten Carrier. Das sind die
|
||||
;;; temporaeren Kopien der in VF_n/GF_n/KREISEL_n VERPACKTEN
|
||||
;;; Separator_SP-Symbole (csv:sep-proxies-erzeugen/-zuordnung-
|
||||
;;; setzen, export.lsp): fuer sie ist der Carrier bekannt - es ist
|
||||
;;; der Wrapper-Block, in dem sie stecken -, und Raten per
|
||||
;;; Boundingbox waere schlechter als die bekannte Wahrheit (am
|
||||
;;; Kettenende ueberlappen sich Carrier-Boxen). Existiert der
|
||||
;;; genannte Carrier nicht (mehr), greifen wieder 1-5.
|
||||
;;; 7. Sensoren, zu denen ueberhaupt kein Carrier existiert (leere
|
||||
;;; Zeichnung), werden mit einem Kreis auf dem Layer *mark-layer*
|
||||
;;; markiert (zentriert auf den Sensor).
|
||||
@@ -92,6 +102,17 @@
|
||||
;; zugeordneten Carriers geschrieben wird.
|
||||
(setq *zuordnung-tag* "ZUORDNUNG")
|
||||
|
||||
;; Festgenagelte (nicht geometrisch zu ermittelnde) Separator-Zuordnungen:
|
||||
;; Assoc-Liste (entity-handle . carrier-id). Wird von csv:sep-proxies-
|
||||
;; zuordnung-setzen (export.lsp) fuer die temporaeren Kopien der in
|
||||
;; VF_n/GF_n/KREISEL_n VERPACKTEN Separator_SP-Symbole gefuellt - fuer die ist
|
||||
;; der Carrier bekannt (es ist der Wrapper, in dem sie stecken) und muss
|
||||
;; nicht ueber Boundingbox/Distanz geraten werden. Bei ueberlappenden
|
||||
;; Carrier-Boxen (Kettenende an einem Kreisel) wuerde die Geometrie sonst
|
||||
;; leicht den Nachbarn treffen. Leer = alles rein geometrisch (Normalfall
|
||||
;; beim interaktiven ZAEHLE_SEP_SCAN).
|
||||
(if (null *cs-sep-fix-by-handle*) (setq *cs-sep-fix-by-handle* nil))
|
||||
|
||||
;; Markierung von Sensoren ohne jeglichen Carrier: Kreis auf eigenem Layer.
|
||||
(setq *mark-layer* "S_SENSOR_UNZUGEORDNET")
|
||||
(setq *mark-color* 2) ;; 2 = Gelb
|
||||
@@ -362,6 +383,21 @@
|
||||
)
|
||||
)
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Carrier-Record ueber seine ID finden (siehe *cs-sep-fix-by-handle*).
|
||||
;;; carriers = Liste von Records (ename tagScan tagSep minx miny maxx
|
||||
;;; maxy cx cy id label), id an Position 9.
|
||||
;;; Rueckgabe: Record oder nil, wenn kein Carrier diese ID traegt.
|
||||
;;; ------------------------------------------------------------
|
||||
(defun cs-carrier-by-id (id carriers / rec treffer)
|
||||
(if (and id (/= id ""))
|
||||
(foreach rec carriers
|
||||
(if (and (null treffer) (= (nth 9 rec) id))
|
||||
(setq treffer rec)))
|
||||
)
|
||||
treffer
|
||||
)
|
||||
|
||||
;;; ------------------------------------------------------------
|
||||
;;; Zaehler in einer Assoc-Liste (Schluessel = ename) erhoehen.
|
||||
;;; ------------------------------------------------------------
|
||||
@@ -600,6 +636,7 @@
|
||||
scanCounts sepCounts n-scan-unassigned n-sep-unassigned
|
||||
n-strittig n-zuordnung-fehlt alt
|
||||
s sent spt res mode rec best-d second-rec second-d
|
||||
vorgabe-id vorgabe-rec
|
||||
scanner-id contested handle
|
||||
en nm2 ns nsep newS newP okS okP eintrag)
|
||||
(setq carriers nil scanlist nil seplist nil
|
||||
@@ -719,8 +756,19 @@
|
||||
;; ANZAHL_SEPARATOR-Summe auch bei Sonderfaellen stimmt). ---
|
||||
(foreach s seplist
|
||||
(setq sent (car s) spt (cadr s))
|
||||
(setq res (cs-resolve-sensor spt carriers grid))
|
||||
(setq mode (nth 0 res) rec (nth 1 res))
|
||||
;; Festgenagelte Zuordnung (verpackter Separator, Carrier bekannt -
|
||||
;; siehe *cs-sep-fix-by-handle*) hat Vorrang vor der Geometrie. Nur
|
||||
;; wenn der genannte Carrier gar nicht (mehr) existiert, wird
|
||||
;; geometrisch zugeordnet.
|
||||
(setq vorgabe-id (cdr (assoc (cdr (assoc 5 (entget sent))) *cs-sep-fix-by-handle*)))
|
||||
(setq vorgabe-rec (cs-carrier-by-id vorgabe-id carriers))
|
||||
(if vorgabe-rec
|
||||
(setq mode 'vorgabe rec vorgabe-rec)
|
||||
(progn
|
||||
(setq res (cs-resolve-sensor spt carriers grid))
|
||||
(setq mode (nth 0 res) rec (nth 1 res))
|
||||
)
|
||||
)
|
||||
(cond
|
||||
((eq mode 'none)
|
||||
(setq n-sep-unassigned (1+ n-sep-unassigned))
|
||||
@@ -728,12 +776,15 @@
|
||||
((null rec) nil) ;; Sicherheitsnetz - tritt bei bbox/distance nicht auf
|
||||
(t
|
||||
(setq sepCounts (cs-bump sepCounts (car rec)))
|
||||
;; Abweichung nur bei geometrischer Zuordnung als Korrektur melden -
|
||||
;; bei mode 'vorgabe ist der geschriebene Wert die Vorgabe selbst,
|
||||
;; keine Korrektur einer Fehl-Zuordnung.
|
||||
(setq alt (cs-get-att-str sent *zuordnung-tag*))
|
||||
(if (and alt (/= alt "") (/= alt (nth 9 rec)))
|
||||
(if (and (not (eq mode 'vorgabe)) alt (/= alt "") (/= alt (nth 9 rec)))
|
||||
(setq *cs-korrekturen*
|
||||
(cons (list "Separator" (cs-get-att-str sent "ID") alt (nth 9 rec))
|
||||
*cs-korrekturen*)))
|
||||
(if (member mode '(bbox distance))
|
||||
(if (member mode '(vorgabe bbox distance))
|
||||
(if (not (cs-set-att sent *zuordnung-tag* (nth 9 rec)))
|
||||
(setq n-zuordnung-fehlt (1+ n-zuordnung-fehlt))))
|
||||
)
|
||||
|
||||
+142
-30
@@ -463,7 +463,8 @@
|
||||
;; 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 warnung-json warnung-eintrag)
|
||||
insert-quat insert-kos k-list warnung-json warnung-eintrag
|
||||
zuordnung-fix-json zuordnung-fix-eintrag)
|
||||
(setq ed (entget ename))
|
||||
(setq blk-name (cdr (assoc 2 ed)))
|
||||
(setq layer (cdr (assoc 8 ed)))
|
||||
@@ -517,6 +518,22 @@
|
||||
(if warnung-eintrag
|
||||
(setq warnung-json (strcat ",\"warnung\":\"" (csv:json-escape (cdr warnung-eintrag)) "\""))
|
||||
)
|
||||
;; Bekannte (nicht geratene) Separator-Zuordnung durchreichen: fuer die
|
||||
;; temporaeren Kopien VERPACKTER Separator_SP-Symbole steht der Carrier fest
|
||||
;; (der Wrapper-Block, in dem das Symbol steckt) - siehe
|
||||
;; csv:sep-proxies-zuordnung-setzen und *cs-sep-fix-by-handle*. export_csv.py
|
||||
;; berechnet die Zuordnung sonst ein zweites Mal per Boundingbox-
|
||||
;; Ueberschneidung (compute_sensor_zuordnung, Prioritaet GF > VF > Kreisel-
|
||||
;; haelfte) und wuerde den bekannten Wert damit ueberschreiben - z.B. auf
|
||||
;; eine Gefaellestrecke, in deren Box der Separator zufaellig hineinragt,
|
||||
;; obwohl er in einem VF-Block verpackt ist. Mit diesem Feld uebernimmt
|
||||
;; export_csv.py den Wert unveraendert.
|
||||
(setq zuordnung-fix-json "")
|
||||
(setq zuordnung-fix-eintrag (assoc handle *cs-sep-fix-by-handle*))
|
||||
(if zuordnung-fix-eintrag
|
||||
(setq zuordnung-fix-json
|
||||
(strcat ",\"zuordnung_fix\":\"" (csv:json-escape (cdr zuordnung-fix-eintrag)) "\""))
|
||||
)
|
||||
(strcat
|
||||
" {\"block_name\":\"" (csv:json-escape blk-name) "\""
|
||||
",\"layer\":\"" (csv:json-escape layer) "\""
|
||||
@@ -533,6 +550,7 @@
|
||||
",\"k4\":\"" (csv:k-kos k-list "K4") "\""
|
||||
bbox-json
|
||||
warnung-json
|
||||
zuordnung-fix-json
|
||||
"}"
|
||||
)
|
||||
)
|
||||
@@ -561,25 +579,38 @@
|
||||
(strcat (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*") ",VF_*,GF_*")
|
||||
)
|
||||
|
||||
;; --- Versatz (mm, in Blockrichtung +X) der temporaeren Separator-Kopien ---
|
||||
;; "Einfach ausserhalb" der Original-Geometrie, damit sie nicht exakt auf
|
||||
;; ihr verpacktes Vorbild zu liegen kommen (rein kosmetisch/zur Unter-
|
||||
;; scheidbarkeit waehrend der - sehr kurzen - Lebensdauer der Kopie).
|
||||
(setq *csv-sep-proxy-versatz-mm* 500.0)
|
||||
;; --- Skalierungsfaktor absichern ---
|
||||
;; vla-InsertBlock verweigert den Faktor 0 (entartete Blockreferenz). Der
|
||||
;; kann nur aus einer fehlerhaften Blockdefinition kommen; 1.0 ist dann die
|
||||
;; brauchbarste Annahme - die Kopie steht wenigstens an der richtigen Stelle.
|
||||
(defun csv:skalierung-oder-eins (f)
|
||||
(if (or (null f) (equal f 0.0 1e-12)) 1.0 f)
|
||||
)
|
||||
|
||||
;; --- Fuer jedes verpackte Separator_SP-Symbol eine ECHTE, temporaere Kopie
|
||||
;; daneben in den Modellraum einfuegen ---
|
||||
;; an DERSELBEN Stelle in den Modellraum einfuegen ---
|
||||
;; Separator_SP-Sensor-Symbole, die beim Zusammenbau von VF_n/GF_n/
|
||||
;; KREISEL_n in die Compound-Blockdefinition "verpackt" wurden (siehe
|
||||
;; ssg-collect-nested-inserts), sind fuer (ssget "X" ...) unsichtbar - und
|
||||
;; damit weder fuer ssg-id-check-all (eigene ID) noch csv:collect-export-
|
||||
;; blocks (JSON-Zeile) erreichbar. Statt beide Sammel-Funktionen dafuer
|
||||
;; anzupassen, wird hier VOR der ID-Vergabe (csv:run-export) fuer jeden Fund
|
||||
;; eine ECHTE Kopie desselben Blocks an seiner Weltposition (+ Versatz in
|
||||
;; Blockrichtung, damit sie nicht exakt auf dem Original liegt) eingefuegt.
|
||||
;; eine ECHTE Kopie desselben Blocks eingefuegt.
|
||||
;; Diese Kopie ist ein normaler, top-level INSERT und durchlaeuft ID-
|
||||
;; Vergabe/Export danach unveraendert ueber die bestehenden Funktionen -
|
||||
;; deren Blockname-Muster (Separator_SP*) trifft ja bereits zu.
|
||||
;;
|
||||
;; Die Kopie steht bewusst EXAKT auf der Weltposition/-drehung/-skalierung
|
||||
;; ihres verpackten Vorbilds (frueher: +500 mm Versatz in Blockrichtung, rein
|
||||
;; kosmetisch). Der Versatz war schaedlich: die geometrische Sensor-Zuordnung
|
||||
;; in count_sep_scan.lsp arbeitet ueber die Carrier-Boundingbox, und ein um
|
||||
;; 500 mm verschobener Separator kippt am Kettenende in die Box des Nachbar-
|
||||
;; Carriers - die Kopie soll ihr Vorbild in der Zeichenebene 1:1
|
||||
;; repraesentieren. Die Platzierung der Ebenen zwischen Wrapper und Symbol
|
||||
;; steckt in den Records von ssg-collect-nested-inserts (Position/Drehung/
|
||||
;; Skalierung relativ zur Wrapper-Definition); hier kommt nur noch die
|
||||
;; Platzierung des top-level INSERT darauf.
|
||||
;;
|
||||
;; Einfuegen ueber vla-InsertBlock (ActiveX) statt "command _.INSERT": das
|
||||
;; legt fuer einen Block mit ATTDEFs automatisch ATTRIB-Entities mit den
|
||||
;; Vorgabewerten an (wie TEFInsert.lsp/tefl-insert-element, ssg_ks_insert.lsp
|
||||
@@ -593,11 +624,15 @@
|
||||
;; koennten in dieser Sitzung noch gar nicht geladen worden sein, falls die
|
||||
;; Zeichnung mit bereits vorhandenen VF_n/GF_n-Bloecken geoeffnet und direkt
|
||||
;; exportiert wird, ohne vorher FOERDERANLAGE/GEFAELLESTRECKE aufzurufen.
|
||||
;; Rueckgabe: Liste der neu eingefuegten Entity-Namen (fuer csv:sep-proxies-
|
||||
;; loeschen nach dem Export).
|
||||
;;
|
||||
;; Rueckgabe: Liste von Paaren (proxy-ename . wrapper-ename) - der Wrapper
|
||||
;; wird mitgefuehrt, weil erst NACH der ID-Vergabe daraus die ZUORDNUNG der
|
||||
;; Kopie gesetzt werden kann (csv:sep-proxies-zuordnung-setzen). Die
|
||||
;; proxy-enames dienen ausserdem csv:sep-proxies-loeschen nach dem Export.
|
||||
(defun csv:sep-proxies-erzeugen ( / ms ss-all i ename ed bname nested pt rotation
|
||||
nested-ent nested-ed nested-bname nested-rotation
|
||||
local-pt world-pt block-obj proxy-liste)
|
||||
psx psy psz c si rec nested-ent nested-bname
|
||||
local-pt nested-rot lx ly lz world-pt
|
||||
block-obj proxy-liste)
|
||||
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
|
||||
(if ss-all
|
||||
(progn
|
||||
@@ -614,23 +649,45 @@
|
||||
(if nested
|
||||
(progn
|
||||
(setq pt (cdr (assoc 10 ed)))
|
||||
(setq rotation (cdr (assoc 50 ed)))
|
||||
(if (null rotation) (setq rotation 0.0))
|
||||
(foreach nested-ent nested
|
||||
(setq nested-ed (entget nested-ent))
|
||||
(setq nested-bname (cdr (assoc 2 nested-ed)))
|
||||
(setq local-pt (cdr (assoc 10 nested-ed)))
|
||||
(setq nested-rotation (cdr (assoc 50 nested-ed)))
|
||||
(if (null nested-rotation) (setq nested-rotation 0.0))
|
||||
(setq world-pt (csv:local-to-world pt rotation local-pt))
|
||||
(setq world-pt (list (+ (car world-pt) *csv-sep-proxy-versatz-mm*)
|
||||
(cadr world-pt)
|
||||
(caddr world-pt)))
|
||||
(setq rotation (cond ((cdr (assoc 50 ed))) (0.0)))
|
||||
(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 c (cos rotation) si (sin rotation))
|
||||
(foreach rec nested
|
||||
(setq nested-ent (nth 0 rec))
|
||||
(setq local-pt (nth 1 rec))
|
||||
(setq nested-rot (nth 2 rec))
|
||||
(setq nested-bname (cdr (assoc 2 (entget nested-ent))))
|
||||
(setq lx (* psx (car local-pt))
|
||||
ly (* psy (cadr local-pt))
|
||||
lz (* psz (caddr local-pt)))
|
||||
(setq world-pt
|
||||
(list (+ (car pt) (- (* lx c) (* ly si)))
|
||||
(+ (cadr pt) (+ (* lx si) (* ly c)))
|
||||
(+ (cond ((caddr pt)) (0.0)) lz)))
|
||||
;; Rotation fuer vla-InsertBlock in RADIANT (ActiveX-Konvention,
|
||||
;; anders als der Grad-Winkel von "command _.INSERT").
|
||||
(setq block-obj (vla-InsertBlock ms (vlax-3D-point world-pt)
|
||||
nested-bname 1.0 1.0 1.0 (+ rotation nested-rotation)))
|
||||
(setq proxy-liste (cons (vlax-vla-object->ename block-obj) proxy-liste))
|
||||
;; Gekapselt, weil hier - anders als beim frueheren festen
|
||||
;; 1.0/1.0/1.0 - die tatsaechlichen Skalierungsfaktoren
|
||||
;; durchgereicht werden: ein einzelner Sonderfall (entartete
|
||||
;; Skalierung, gespiegelter Zwischenblock) darf nicht den
|
||||
;; kompletten Export der Zeichnung abbrechen, sondern nur diese
|
||||
;; eine Kopie ausfallen lassen - mit Meldung, welcher Block.
|
||||
(setq block-obj
|
||||
(vl-catch-all-apply 'vla-InsertBlock
|
||||
(list ms (vlax-3D-point world-pt) nested-bname
|
||||
(csv:skalierung-oder-eins (* psx (nth 3 rec)))
|
||||
(csv:skalierung-oder-eins (* psy (nth 4 rec)))
|
||||
(csv:skalierung-oder-eins (* psz (nth 5 rec)))
|
||||
(+ rotation nested-rot))))
|
||||
(if (vl-catch-all-error-p block-obj)
|
||||
(princ (ssg-textf "exp-sep-proxy-fehler"
|
||||
(list nested-bname bname
|
||||
(vl-catch-all-error-message block-obj))))
|
||||
(setq proxy-liste
|
||||
(cons (cons (vlax-vla-object->ename block-obj) ename) proxy-liste))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -641,10 +698,56 @@
|
||||
proxy-liste
|
||||
)
|
||||
|
||||
;; --- 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
|
||||
;; Erzeugen der Kopien noch gar keine ID haben.
|
||||
;;
|
||||
;; Die Zuordnung eines VERPACKTEN Separators ist keine Schaetzung, sondern
|
||||
;; bekannt: er steckt in genau diesem Wrapper. Ein Separator, der in einem
|
||||
;; VF-Block mit ID 0010 verpackt ist, bekommt also ZUORDNUNG = 0010. Die
|
||||
;; geometrische Zuordnung in count_sep_scan.lsp (Carrier-Boundingbox bzw.
|
||||
;; naechster Carrier) wuerde diesen Wert bei jedem Lauf neu berechnen und ggf.
|
||||
;; ueberschreiben - bei ueberlappenden Boxen (Kettenende an einem Kreisel)
|
||||
;; landet der Separator dort leicht beim Nachbarn. Darum wird die bekannte
|
||||
;; Zuordnung ueber *cs-sep-fix-by-handle* (Key = Entity-Handle der Kopie)
|
||||
;; festgenagelt; cs-zuordnung-lauf uebernimmt sie unveraendert und zaehlt den
|
||||
;; Separator beim richtigen Carrier.
|
||||
;; Rueckgabe: Anzahl festgenagelter Kopien.
|
||||
(defun csv:sep-proxies-zuordnung-setzen (proxy-liste / paar proxy wrapper wrapper-id
|
||||
handle gesetzt ohne-id)
|
||||
(setq gesetzt 0 ohne-id 0)
|
||||
(setq *cs-sep-fix-by-handle* nil)
|
||||
(foreach paar proxy-liste
|
||||
(setq proxy (car paar))
|
||||
(setq wrapper (cdr paar))
|
||||
(setq wrapper-id (cdr (assoc "ID" (ssg-attrib-read wrapper))))
|
||||
(if (and wrapper-id (> (strlen wrapper-id) 0))
|
||||
(progn
|
||||
(ssg-attrib-set-on proxy (list (cons "ZUORDNUNG" wrapper-id)))
|
||||
(setq handle (cdr (assoc 5 (entget proxy))))
|
||||
(setq *cs-sep-fix-by-handle*
|
||||
(cons (cons handle wrapper-id) *cs-sep-fix-by-handle*))
|
||||
(setq gesetzt (1+ gesetzt))
|
||||
)
|
||||
;; Wrapper ohne ID-Attribut (Altbestand ohne ID-ATTDEF): keine bekannte
|
||||
;; Zuordnung - die Kopie faellt auf die geometrische Zuordnung zurueck.
|
||||
(setq ohne-id (1+ ohne-id))
|
||||
)
|
||||
)
|
||||
(if (> gesetzt 0)
|
||||
(princ (ssg-textf "exp-sep-zuordnung" (list (itoa gesetzt)))))
|
||||
(if (> ohne-id 0)
|
||||
(princ (ssg-textf "exp-sep-zuordnung-ohne-id" (list (itoa ohne-id)))))
|
||||
gesetzt
|
||||
)
|
||||
|
||||
;; --- Temporaere Separator_SP-Kopien (csv:sep-proxies-erzeugen) nach dem
|
||||
;; Export wieder entfernen ---
|
||||
;; proxy-liste = Paare (proxy-ename . wrapper-ename), nur der car wird geloescht.
|
||||
(defun csv:sep-proxies-loeschen (proxy-liste)
|
||||
(foreach ent proxy-liste (entdel ent))
|
||||
(foreach paar proxy-liste (entdel (car paar)))
|
||||
(setq *cs-sep-fix-by-handle* nil)
|
||||
(princ)
|
||||
)
|
||||
|
||||
@@ -736,9 +839,18 @@
|
||||
;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende
|
||||
;; dieser Funktion wieder entfernt (csv:sep-proxies-loeschen).
|
||||
(setq sep-proxies (csv:sep-proxies-erzeugen))
|
||||
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren
|
||||
;; 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
|
||||
;; dabei zuerst das globale Maximum der bereits vergebenen IDs (Phase 0)
|
||||
;; und nummeriert die noch ID-losen Kopien erst danach - sonst bekaemen sie
|
||||
;; IDs, die die Wrapper (VF_n/GF_n/Kreisel) selbst schon tragen.
|
||||
(princ (ssg-textf "exp-check-ids" (list label)))
|
||||
(ssg-id-check-all)
|
||||
;; Jetzt steht die ID jedes Wrappers fest: die bekannte (nicht geratene)
|
||||
;; ZUORDNUNG der verpackten Separatoren auf die Wrapper-ID setzen und fuer
|
||||
;; cs-zuordnung-lauf festnageln.
|
||||
(csv:sep-proxies-zuordnung-setzen sep-proxies)
|
||||
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
|
||||
(princ (ssg-textf "exp-update-hoehe-drehung" (list label)))
|
||||
(omni:update-all-attribs)
|
||||
|
||||
+56
-10
@@ -761,26 +761,72 @@
|
||||
;; die durchlaeuft ID-Vergabe/Export danach ganz normal ueber die
|
||||
;; unveraenderten ssg-id-check-all/csv:collect-export-blocks. Rekursiv,
|
||||
;; falls ein Treffer selbst nochmal in einem weiteren Sub-Compound-Block
|
||||
;; steckt. Rueckgabe: Liste von Entity-Namen (aus der Blockdefinition, kein
|
||||
;; Selection-Set - sie sind nicht Space-resident und daher fuer ssadd nicht
|
||||
;; sicher verwendbar), oder nil. Die gefundenen Entity-Namen bleiben ueber
|
||||
;; entget normal lesbar (Blockname/Einfuegepunkt/Rotation), sollten aber
|
||||
;; NICHT direkt exportiert werden - eine eigene Bounding-Box
|
||||
;; (vla-getboundingbox) liefert fuer sie keine sinnvollen Weltkoordinaten,
|
||||
;; da sie nicht Space-resident sind.
|
||||
(defun ssg-collect-nested-inserts (bname pattern / blk-tbl sub-ent sub-ed sub-bname result)
|
||||
;; steckt.
|
||||
;;
|
||||
;; Rueckgabe: Liste von Treffer-Records, oder nil. Ein Record ist
|
||||
;; (ename (x y z) rot-rad sx sy sz)
|
||||
;; mit der Platzierung des Treffers RELATIV zur abgefragten Blockdefinition
|
||||
;; bname - ueber alle Verschachtelungsebenen hinweg aufsummiert (Position,
|
||||
;; Z-Drehung und Skalierung). Der Aufrufer muss also nur noch die Platzierung
|
||||
;; des top-level INSERT daraufrechnen. Der rohe Einfuegepunkt aus
|
||||
;; (entget ename) ist bei mehr als einer Ebene NICHT verwendbar - er gilt nur
|
||||
;; innerhalb der unmittelbaren Zwischen-Blockdefinition.
|
||||
;;
|
||||
;; Kein Selection-Set: die Entities sind nicht Space-resident und damit fuer
|
||||
;; ssadd nicht sicher verwendbar. Sie bleiben ueber entget normal lesbar
|
||||
;; (Blockname, Attribute), sollten aber NICHT direkt exportiert werden - eine
|
||||
;; eigene Bounding-Box (vla-getboundingbox) liefert fuer sie keine sinnvollen
|
||||
;; Weltkoordinaten.
|
||||
(defun ssg-collect-nested-inserts (bname pattern)
|
||||
(ssg-collect-nested-inserts-rel bname pattern '(0.0 0.0 0.0) 0.0 1.0 1.0 1.0)
|
||||
)
|
||||
|
||||
;; --- Rekursions-Rumpf mit mitgefuehrter Platzierung ---
|
||||
;; basis-pt/basis-rot/bsx/bsy/bsz = Platzierung der gerade durchsuchten
|
||||
;; Blockdefinition im Koordinatensystem des URSPRUENGLICH abgefragten Blocks.
|
||||
;; Auf jeder Ebene wird der Einfuegepunkt des Sub-INSERT erst mit der
|
||||
;; Skalierung, dann mit der Drehung der Ebene darueber verrechnet und
|
||||
;; aufaddiert - genau das fehlte frueher: die Rekursion gab die Entities
|
||||
;; tieferer Ebenen mit ihrer ROH-Position aus der jeweiligen Zwischen-
|
||||
;; Blockdefinition zurueck. Zwei Separatoren, die in zwei verschieden
|
||||
;; platzierten Zwischenbloecken an derselben lokalen Stelle sitzen, landeten
|
||||
;; dadurch auf exakt derselben Weltposition (und damit beim falschen Carrier).
|
||||
;;
|
||||
;; Modellannahme wie im uebrigen Export (csv:local-to-world): reine
|
||||
;; Z-Drehung, keine OCS-/Extrusionsrichtung. Fuer die Wrapper-Ketten
|
||||
;; VF_n/GF_n/KREISEL_n trifft das zu.
|
||||
(defun ssg-collect-nested-inserts-rel (bname pattern basis-pt basis-rot bsx bsy bsz
|
||||
/ blk-tbl sub-ent sub-ed sub-bname loc-pt
|
||||
rot sx sy sz c si lx ly lz wx wy wz result)
|
||||
(setq blk-tbl (tblsearch "BLOCK" bname))
|
||||
(if blk-tbl
|
||||
(progn
|
||||
(setq c (cos basis-rot) si (sin basis-rot))
|
||||
(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 (cdr (assoc 2 sub-ed)))
|
||||
(setq loc-pt (cdr (assoc 10 sub-ed)))
|
||||
(setq rot (cond ((cdr (assoc 50 sub-ed))) (0.0)))
|
||||
(setq sx (cond ((cdr (assoc 41 sub-ed))) (1.0)))
|
||||
(setq sy (cond ((cdr (assoc 42 sub-ed))) (1.0)))
|
||||
(setq sz (cond ((cdr (assoc 43 sub-ed))) (1.0)))
|
||||
(setq lx (* bsx (car loc-pt))
|
||||
ly (* bsy (cadr loc-pt))
|
||||
lz (* bsz (cond ((caddr loc-pt)) (0.0))))
|
||||
(setq wx (+ (car basis-pt) (- (* lx c) (* ly si)))
|
||||
wy (+ (cadr basis-pt) (+ (* lx si) (* ly c)))
|
||||
wz (+ (caddr basis-pt) lz))
|
||||
(if (wcmatch sub-bname pattern)
|
||||
(setq result (cons sub-ent result))
|
||||
(setq result (append result (ssg-collect-nested-inserts sub-bname pattern)))
|
||||
(setq result (cons (list sub-ent (list wx wy wz) (+ basis-rot rot)
|
||||
(* bsx sx) (* bsy sy) (* bsz sz))
|
||||
result))
|
||||
(setq result (append result
|
||||
(ssg-collect-nested-inserts-rel sub-bname pattern
|
||||
(list wx wy wz) (+ basis-rot rot)
|
||||
(* bsx sx) (* bsy sy) (* bsz sz))))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
+40
-8
@@ -62,13 +62,17 @@
|
||||
)
|
||||
|
||||
|
||||
;; --- Hoechste ID in der Zeichnung ermitteln ---
|
||||
;; Durchsucht alle exportierbaren Bloecke nach dem Attribut "ID".
|
||||
;; --- Hoechste bereits vergebene ID in einem Auswahlsatz ermitteln ---
|
||||
;; ss = Auswahlsatz (i.d.R. aus ssg-id-collect-blocks) oder nil.
|
||||
;; Rueckgabe: Integer (hoechste ID) oder 0 wenn keine gefunden.
|
||||
(defun ssg-id-max ( / ss i ename attribs id-val id-num max-id)
|
||||
(dbgf "ssg-id-max")
|
||||
;;
|
||||
;; Bewusst als eigene Funktion: ssg-id-check-all MUSS diesen Wert ueber den
|
||||
;; GESAMTEN Auswahlsatz kennen, BEVOR es die erste neue ID vergibt (siehe
|
||||
;; Phase 0 dort) - sonst haengt die Neuvergabe von der (beliebigen)
|
||||
;; ssget-Reihenfolge ab und vergibt IDs, die weiter hinten im Auswahlsatz
|
||||
;; schon belegt sind.
|
||||
(defun ssg-id-max-in-ss (ss / i ename attribs id-val id-num max-id)
|
||||
(setq max-id 0)
|
||||
(setq ss (ssg-id-collect-blocks))
|
||||
(if ss
|
||||
(progn
|
||||
(setq i 0)
|
||||
@@ -87,6 +91,16 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
max-id
|
||||
)
|
||||
|
||||
|
||||
;; --- Hoechste ID in der Zeichnung ermitteln ---
|
||||
;; Durchsucht alle exportierbaren Bloecke nach dem Attribut "ID".
|
||||
;; Rueckgabe: Integer (hoechste ID) oder 0 wenn keine gefunden.
|
||||
(defun ssg-id-max ( / max-id)
|
||||
(dbgf "ssg-id-max")
|
||||
(setq max-id (ssg-id-max-in-ss (ssg-id-collect-blocks)))
|
||||
(dbgreturn max-id)
|
||||
)
|
||||
|
||||
@@ -156,10 +170,26 @@
|
||||
0
|
||||
)
|
||||
(progn
|
||||
;; Phase 1: Alle IDs sammeln und hoechste ID ermitteln
|
||||
;; Phase 0: Hoechste BEREITS vergebene ID ueber den GESAMTEN Auswahlsatz
|
||||
;; ermitteln - zwingend VOR der ersten Neuvergabe in Phase 1.
|
||||
;;
|
||||
;; Frueher wurde max-id erst waehrend Phase 1 mitgefuehrt (Start 0) und
|
||||
;; eine fehlende ID sofort mit max-id+1 belegt. Die Reihenfolge von
|
||||
;; (ssget "X" ...) ist aber beliebig - in BricsCAD kommen die zuletzt
|
||||
;; erzeugten Entities zuerst. Genau das trifft die temporaeren
|
||||
;; Separator-Kopien aus csv:sep-proxies-erzeugen (export.lsp): sie haben
|
||||
;; noch keine ID, stehen ganz vorne und bekamen daher 0001, 0002, ... -
|
||||
;; also exakt die IDs, die die weiter hinten liegenden Kreisel/VF_n/GF_n
|
||||
;; laengst tragen. Phase 2 konnte das nicht heilen, weil die frisch
|
||||
;; vergebenen IDs nicht in id-map landen (und auch nicht muessen: sie
|
||||
;; liegen jetzt garantiert oberhalb jeder vorhandenen ID).
|
||||
(dbgp "=== Phase 0: hoechste vorhandene ID ermitteln ===")
|
||||
(setq max-id (ssg-id-max-in-ss ss))
|
||||
(dbgp (strcat "Phase 0: hoechste vorhandene ID = " (itoa max-id)))
|
||||
|
||||
;; Phase 1: Alle IDs sammeln, Bloecke ohne ID neu nummerieren
|
||||
(dbgp "=== Phase 1: IDs sammeln ===")
|
||||
(setq id-map nil)
|
||||
(setq max-id 0)
|
||||
(setq i 0)
|
||||
(while (setq ename (ssname ss i))
|
||||
(setq attribs (ssg-attrib-read ename))
|
||||
@@ -171,7 +201,9 @@
|
||||
(if (and id-val (> (strlen id-val) 0))
|
||||
(progn
|
||||
(setq id-num (atoi id-val))
|
||||
(if (> id-num max-id) (setq max-id id-num))
|
||||
;; max-id NICHT mehr hier nachziehen - es steht seit Phase 0 auf
|
||||
;; dem globalen Maximum und dient ab jetzt nur noch als Zaehler
|
||||
;; fuer die Neuvergabe.
|
||||
;; In Map eintragen: (id-num . (ename1 ename2 ...))
|
||||
(setq entry (assoc id-num id-map))
|
||||
(if entry
|
||||
|
||||
Reference in New Issue
Block a user