sepliste: Bauteil-IDs eintragen, AS/ES ueber dauerhaften Marker sichtbar machen
Stufe 1 (LISP-Export-Pfad): export_raw.json enthaelt jetzt fuer jeden sepliste-Eintrag (AS/SEP/ES) einer VF_n/GF_n-Kette die ID des realen Bauteils, das ihn vertritt. - csv:sep-proxies-erzeugen (Separatoren) und die neuen AS/ES-Marker teilen sich csv:nested-welt-records/csv:block-einfuegen-welt zur Platzierung. - csv:sepliste-paare ordnet sepliste-Eintraege ihrem Bauteil exklusiv nach Abstand zu (kuerzester zuerst, dann verbraucht) statt "jeder Eintrag nimmt sich das naechste" - ein einzelner fehlerhafter Koordinatenwert (bekannter Bug: gespiegeltes x bei lfdnr=2) verschiebt dadurch keine andere Zuordnung mehr. - Neu: ZZ_ASES_MARKER - ein kleines Rechteck an jedem AS/ES-Kettenende, das nach dem Export STEHEN BLEIBT (anders als die temporaeren Separator-Kopien). Macht das sonst unsichtbare, in der Wrapper- Blockdefinition verpackte Kettenende in BricsCAD sichtbar/anklickbar und traegt seine ID unsichtbar am Attribut ID - dadurch behaelt ein AS/ES seine TeileId ueber Exportlaeufe hinweg. Wird bei jedem Export neu aufgebaut (Lage folgt der Kette), Wiederfund ueber ZUORDNUNG+TYP+ LFDNR, verwaiste Marker werden entfernt. In BricsCAD verifiziert (5 HundM05-Ketten, zwei Exportlaeufe: gleiche Marker-IDs, korrekte sepliste-"id"-Felder). Dabei einen realen Bug gefunden und behoben: die Blockdefinition des Markers uebernahm ueber einen ungefilterten entnext-Walk versehentlich auch die Attribute des zuletzt eingefuegten Blocks (VF_5 verlor dadurch alle Attribute inkl. ID) - jetzt auf LWPOLYLINE/ATTDEF gefiltert. 9 neue zeichnungsfreie Unit-Tests (csv:sepliste-paare/*, csv:asmark- finden/*, csv:proxy-typ/*), 4 neue de/en-Textschluessel (Paritaet geprueft). bin\run_tests.bat --lisp: 257/257 bestanden. Noch nicht enthalten (Stufe 2, Python): map_separator_kette_items nutzt das neue "id"-Feld noch nicht, build_strecke_schleus_items synthetisiert AS/ES-Zeilen weiterhin selbst statt die exportierten zu lesen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
9671fe4e04
commit
618ff3d86c
+103
-8
@@ -44,6 +44,7 @@ from export_neighbors import (
|
||||
compute_strecke_kreisel_schleus,
|
||||
compute_sep_kette,
|
||||
compute_kreisel_umlauf,
|
||||
dbg_highlight,
|
||||
SENSOR_TEILEARTEN,
|
||||
WEICHE_TEILEART,
|
||||
GF_VF_TEILEARTEN,
|
||||
@@ -683,9 +684,30 @@ def map_separator_kette_items(items, sep_kette, sep_kette_by_key=None, dbg=None)
|
||||
if zuordnung:
|
||||
sep_items_by_zuordnung.setdefault(zuordnung, []).append(it)
|
||||
|
||||
if dbg:
|
||||
dbg("=== map_separator_kette_items: Kandidatenpools je Zuordnung ===")
|
||||
for zuordnung, kandidaten in sep_items_by_zuordnung.items():
|
||||
dbg(f" Zuordnung {zuordnung!r}: "
|
||||
f"{[k.get('teileid', '') for k in kandidaten]}")
|
||||
dbg("")
|
||||
dbg("=== map_separator_kette_items: sepliste-SEP -> Separator-Item ===")
|
||||
|
||||
# id(item) -> Liste der sep_kette-Keys, die genau dieses Item als
|
||||
# naechsten Kandidaten gewaehlt haben - Diagnose der NICHT-exklusiven
|
||||
# Zuordnung (jeder Key sucht unabhaengig im vollen Pool, siehe Docstring):
|
||||
# waehlen zwei Keys denselben Kandidaten, bleibt ein anderer Kandidat im
|
||||
# selben Pool ohne jeden sep_kette-Eintrag und damit ohne Nachbarn/TrackIds.
|
||||
gewaehlt_von = {}
|
||||
# Zuordnungen, fuer die es ueberhaupt sepliste-SEP-Schluessel gibt. Nur in
|
||||
# DIESEN Pools ist ein nicht gewaehlter Kandidat auffaellig; frei auf einem
|
||||
# Kreisel platzierte Separatoren tragen keine sepliste und bekommen ihre
|
||||
# Nachbarn aus dem Kreisel-Umlauf (siehe resolve_umlauf_und_sepliste_nachbarn).
|
||||
zuordnungen_mit_sepliste = set()
|
||||
|
||||
for key, eintrag in sep_kette.items():
|
||||
if eintrag.get("typ") != "SEP":
|
||||
continue
|
||||
zuordnungen_mit_sepliste.add(eintrag["strecke_id"])
|
||||
kandidaten = sep_items_by_zuordnung.get(eintrag["strecke_id"], [])
|
||||
if not kandidaten:
|
||||
if dbg:
|
||||
@@ -694,18 +716,48 @@ def map_separator_kette_items(items, sep_kette, sep_kette_by_key=None, dbg=None)
|
||||
continue
|
||||
# Naechster INSERT-Punkt zur sepliste-Koordinate.
|
||||
sx, sy = eintrag.get("x", 0.0), eintrag.get("y", 0.0)
|
||||
best = min(kandidaten,
|
||||
key=lambda it: math.hypot(it.get("_x", 0.0) - sx,
|
||||
it.get("_y", 0.0) - sy))
|
||||
entfernungen = sorted(
|
||||
((math.hypot(it.get("_x", 0.0) - sx, it.get("_y", 0.0) - sy), it)
|
||||
for it in kandidaten),
|
||||
key=lambda p: p[0])
|
||||
best_d, best = entfernungen[0]
|
||||
sep_kette_by_key[key] = best
|
||||
gewaehlt_von.setdefault(id(best), []).append(key)
|
||||
if dbg:
|
||||
dbg(f" sepliste-SEP {key} -> Separator-Item TeileId "
|
||||
f"{best.get('teileid', '')!r}")
|
||||
rangliste = ", ".join(f"{it.get('teileid', '')!r}={d:.0f}mm"
|
||||
for d, it in entfernungen)
|
||||
dbg(f" sepliste-SEP {key} (x={sx:.0f},y={sy:.0f}) -> Separator-Item "
|
||||
f"TeileId {best.get('teileid', '')!r} (d={best_d:.0f}mm); "
|
||||
f"Kandidaten sortiert: [{rangliste}]"
|
||||
f"{dbg_highlight(best.get('teileid', ''))}")
|
||||
|
||||
if dbg:
|
||||
dbg("")
|
||||
mehrfach = {iid: keys for iid, keys in gewaehlt_von.items() if len(keys) > 1}
|
||||
if mehrfach:
|
||||
dbg(" WARNUNG: folgende Separator-Items wurden von MEHREREN "
|
||||
"sepliste-Keys unabhaengig als naechster Kandidat gewaehlt "
|
||||
"(nicht-exklusive Zuordnung):")
|
||||
for keys in mehrfach.values():
|
||||
teileid = sep_kette_by_key[keys[0]].get("teileid", "")
|
||||
dbg(f" TeileId {teileid!r} gewaehlt von Keys: {keys}"
|
||||
f"{dbg_highlight(teileid)}")
|
||||
gewaehlte_ids = set(gewaehlt_von.keys())
|
||||
for zuordnung, kandidaten in sep_items_by_zuordnung.items():
|
||||
if zuordnung not in zuordnungen_mit_sepliste:
|
||||
continue
|
||||
for it in kandidaten:
|
||||
if id(it) not in gewaehlte_ids:
|
||||
teileid = it.get("teileid", "")
|
||||
dbg(f" Separator-Item TeileId {teileid!r} (Zuordnung "
|
||||
f"{zuordnung!r}) wurde von KEINEM sepliste-Key gewaehlt "
|
||||
f"- bleibt ohne sep_kette-Eintrag, damit ohne Nachbarn/"
|
||||
f"TrackIds{dbg_highlight(teileid)}")
|
||||
return sep_kette_by_key
|
||||
|
||||
|
||||
def resolve_umlauf_und_sepliste_nachbarn(items, kreisel_umlauf, sep_kette,
|
||||
sep_kette_by_key, weiche_by_key):
|
||||
sep_kette_by_key, weiche_by_key, dbg=None):
|
||||
"""Loest die "Nachbarn"-Spalte aller Zeilen auf, die build_kreisel_weiche_items/
|
||||
build_strecke_schleus_items/map_separator_kette_items zunaechst leer
|
||||
gelassen (bzw. deren Item nur registriert) haben, weil ihr(e) Nachbar(n)
|
||||
@@ -740,13 +792,27 @@ def resolve_umlauf_und_sepliste_nachbarn(items, kreisel_umlauf, sep_kette,
|
||||
if it.get("teileart", "") in KREISEL_UMLAUF_TEILEARTEN}
|
||||
alle_schluessel = {**sep_kette_by_key, **weiche_by_key, **freipunkt_by_id}
|
||||
|
||||
if dbg:
|
||||
dbg("=== resolve_umlauf_und_sepliste_nachbarn ===")
|
||||
dbg(f" freipunkt_by_id: {sorted(freipunkt_by_id.keys())}")
|
||||
dbg(f" sep_kette_by_key: "
|
||||
f"{[(k, v.get('teileid', '')) for k, v in sep_kette_by_key.items()]}")
|
||||
dbg(f" weiche_by_key: "
|
||||
f"{[(k, v.get('teileid', '')) for k, v in weiche_by_key.items()]}")
|
||||
dbg("")
|
||||
dbg(" -- Pass 1: aus kreisel_umlauf --")
|
||||
|
||||
# Freie Separatoren, Weichen UND die AS/ES-Enden andockender Ketten
|
||||
# bekommen ihre "Nachbarn"-Spalte aus dem Kreisel-Umlauf.
|
||||
if kreisel_umlauf:
|
||||
for punkte in kreisel_umlauf.values():
|
||||
for kreisel_tid, punkte in kreisel_umlauf.items():
|
||||
for punkt_key, vorgaenger, nachfolger in punkte:
|
||||
item = alle_schluessel.get(punkt_key)
|
||||
if item is None:
|
||||
if dbg:
|
||||
dbg(f" Kreisel {kreisel_tid} Punkt {punkt_key!r}: "
|
||||
f"KEIN Item in alle_schluessel gefunden - "
|
||||
f"uebersprungen{dbg_highlight(punkt_key)}")
|
||||
continue
|
||||
nachbarn = []
|
||||
for nachbar_key in (vorgaenger, nachfolger):
|
||||
@@ -755,8 +821,26 @@ def resolve_umlauf_und_sepliste_nachbarn(items, kreisel_umlauf, sep_kette,
|
||||
nachbar_item = alle_schluessel.get(nachbar_key)
|
||||
if nachbar_item is not None:
|
||||
nachbarn.append(nachbar_item.get("teileid", ""))
|
||||
elif dbg:
|
||||
dbg(f" Kreisel {kreisel_tid} Punkt {punkt_key!r}: "
|
||||
f"Nachbar-Key {nachbar_key!r} nicht in alle_schluessel "
|
||||
f"aufloesbar{dbg_highlight(item.get('teileid', ''))}")
|
||||
teileid = item.get("teileid", "")
|
||||
if nachbarn:
|
||||
item["nachbarn"] = ", ".join(nachbarn)
|
||||
if dbg:
|
||||
dbg(f" Kreisel {kreisel_tid} Punkt {punkt_key!r} "
|
||||
f"(TeileId {teileid!r}): nachbarn={nachbarn}"
|
||||
f"{dbg_highlight(teileid)}")
|
||||
elif dbg:
|
||||
dbg(f" Kreisel {kreisel_tid} Punkt {punkt_key!r} "
|
||||
f"(TeileId {teileid!r}): KEINE Nachbarn aufgeloest "
|
||||
f"(vorgaenger={vorgaenger!r}, nachfolger={nachfolger!r})"
|
||||
f"{dbg_highlight(teileid)}")
|
||||
|
||||
if dbg:
|
||||
dbg("")
|
||||
dbg(" -- Pass 2: ketteninterne sepliste-Nachbarn --")
|
||||
|
||||
# Ketteninterne sepliste-Nachbarn (SEP <-> SEP, SEP <-> AS/ES) - das
|
||||
# jeweilige Kettenende (vorgaenger/nachfolger ist None, z.B. das AS-Ende
|
||||
@@ -765,6 +849,10 @@ def resolve_umlauf_und_sepliste_nachbarn(items, kreisel_umlauf, sep_kette,
|
||||
for key, eintrag in sep_kette.items():
|
||||
item = sep_kette_by_key.get(key)
|
||||
if item is None:
|
||||
if dbg:
|
||||
dbg(f" sepliste-Key {key!r} (typ={eintrag.get('typ')!r}): "
|
||||
f"KEIN Item in sep_kette_by_key - uebersprungen "
|
||||
f"(siehe map_separator_kette_items){dbg_highlight(key)}")
|
||||
continue
|
||||
nachbarn = []
|
||||
for nachbar_key in (eintrag.get("vorgaenger"), eintrag.get("nachfolger")):
|
||||
@@ -773,8 +861,15 @@ def resolve_umlauf_und_sepliste_nachbarn(items, kreisel_umlauf, sep_kette,
|
||||
nachbar_item = alle_schluessel.get(nachbar_key)
|
||||
if nachbar_item is not None:
|
||||
nachbarn.append(nachbar_item.get("teileid", ""))
|
||||
teileid = item.get("teileid", "")
|
||||
if nachbarn:
|
||||
item["nachbarn"] = ", ".join(nachbarn)
|
||||
if dbg:
|
||||
dbg(f" sepliste-Key {key!r} (TeileId {teileid!r}): "
|
||||
f"vorgaenger={eintrag.get('vorgaenger')!r} "
|
||||
f"nachfolger={eintrag.get('nachfolger')!r} -> "
|
||||
f"nachbarn={nachbarn} item.nachbarn={item.get('nachbarn', '')!r}"
|
||||
f"{dbg_highlight(teileid)}")
|
||||
|
||||
|
||||
def write_separatorliste_merkmale(items, sep_kette_by_key, dbg=None):
|
||||
@@ -1333,7 +1428,7 @@ def process_blocks(blocks, lookup, dbg=None):
|
||||
write_separatorliste_merkmale(items, sep_kette_by_key, dbg=dbg)
|
||||
|
||||
resolve_umlauf_und_sepliste_nachbarn(
|
||||
items, kreisel_umlauf, sep_kette, sep_kette_by_key, weiche_by_key)
|
||||
items, kreisel_umlauf, sep_kette, sep_kette_by_key, weiche_by_key, dbg=dbg)
|
||||
|
||||
# Spalte "TrackIds" je Kreisel: dieselben Umlauf-Punkte (freie
|
||||
# Separatoren, AS/ES-Enden, Kreisel-Kreisel-Weichen), die oben schon die
|
||||
|
||||
Reference in New Issue
Block a user