Export: ILS-Weiche an Kreisel-Beruehrpunkten + Ein-/Ausschleuselemente an Strecken
- compute_kreisel_touch_switches: erzeugt an jedem Beruehrpunkt zweier Kreisel eine "ILS Weiche" (Kapsel-Geometrie, Position=Beruehrpunkt, statische Box 250x100x30 mit Laengsseite senkrecht zur Kreiselachse, Nachbarn=beide Kreisel) - compute_strecke_kreisel_schleus: an Anfang/Ende jeder Gefaellestrecke bzw. jedes Foerderers (ILS 2.0 Strecke) ein Aus-/Einschleuselement an der Kollisionsposition zum naechsten BBox-Nachbar-Kreisel (Nachbarn=Kreisel+Strecke) - TeileId zaehlt fortlaufend aus der hoechsten vorhandenen ID hoch (Weichen zuerst, dann Schleuselemente) - Warnung (Konsole + neue CSV-Spalte "Warnungen"), wenn eine Strecke mehr als zwei Kreisel-BoundingBoxen beruehrt (erwartet: ein Anfang + ein Ende) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+90
-4
@@ -15,12 +15,25 @@ K1-K4/Insertpoint leer, Boundingbox = statische Default-Box (WEICHE_BOX_*),
|
|||||||
mit der Laengsseite senkrecht zur Achse des beruehrenden Kreisels; die TeileId
|
mit der Laengsseite senkrecht zur Achse des beruehrenden Kreisels; die TeileId
|
||||||
zaehlt aus der hoechsten bereits vergebenen numerischen ID hoch.
|
zaehlt aus der hoechsten bereits vergebenen numerischen ID hoch.
|
||||||
|
|
||||||
|
Anschliessend (TeileId-Zaehlung laeuft ueber die Weichen hinweg weiter) wird
|
||||||
|
an jedem Ende einer Gefaellestrecke/eines Foerderers, an dem ein Kreisel
|
||||||
|
anschliesst, ein Aus- (Anfang) bzw. Einschleuselement (Ende) an der Kollisions-
|
||||||
|
position Kreisel<->Strecke angehaengt (siehe build_strecke_schleus_items /
|
||||||
|
export_neighbors.compute_strecke_kreisel_schleus) - Format wie die Weiche,
|
||||||
|
Nachbarn = Kreisel-ID + Strecken-ID. Zugeordnet wird je Streckenende der
|
||||||
|
naechstgelegene ihrer BBox-Kreisel-Nachbarn.
|
||||||
|
|
||||||
|
Warnungen = Spalte fuer Auffaelligkeiten. Aktuell: eine Strecke sollte genau
|
||||||
|
zwei Kreisel (Anfang + Ende) beruehren - beruehrt ihre (in x/y gepruefte)
|
||||||
|
Bounding-Box mehr, steht hier ein Hinweis und es wird zusaetzlich eine
|
||||||
|
Konsolen-WARNUNG ausgegeben (mit exakten 3D-BBoxen sollte das nicht auftreten).
|
||||||
|
|
||||||
Aufruf:
|
Aufruf:
|
||||||
python export_csv.py <export_raw.json> <data_dir> <output.csv>
|
python export_csv.py <export_raw.json> <data_dir> <output.csv>
|
||||||
|
|
||||||
CSV-Format:
|
CSV-Format:
|
||||||
Elementnummer;TeileArt;TeileId;Bezeichnung;Planquadrat;Anzahl;Position;Boundingbox;
|
Elementnummer;TeileArt;TeileId;Bezeichnung;Planquadrat;Anzahl;Position;Boundingbox;
|
||||||
Insertpoint;K1;K2;K3;K4;Nachbarn;Fehler;Merkmale
|
Insertpoint;K1;K2;K3;K4;Nachbarn;Fehler;Warnungen;Merkmale
|
||||||
|
|
||||||
Position und Boundingbox stammen aus der von export.lsp (csv:get-bbox, per
|
Position und Boundingbox stammen aus der von export.lsp (csv:get-bbox, per
|
||||||
vla-getboundingbox) ermittelten Bounding-Box je Block:
|
vla-getboundingbox) ermittelten Bounding-Box je Block:
|
||||||
@@ -90,8 +103,10 @@ from export_neighbors import (
|
|||||||
compute_sensor_zuordnung,
|
compute_sensor_zuordnung,
|
||||||
compute_scanner_nearest_separator,
|
compute_scanner_nearest_separator,
|
||||||
compute_kreisel_touch_switches,
|
compute_kreisel_touch_switches,
|
||||||
|
compute_strecke_kreisel_schleus,
|
||||||
SENSOR_TEILEARTEN,
|
SENSOR_TEILEARTEN,
|
||||||
WEICHE_TEILEART,
|
WEICHE_TEILEART,
|
||||||
|
GF_VF_TEILEARTEN,
|
||||||
)
|
)
|
||||||
|
|
||||||
BLOCKPATTERNS = load_patterns()
|
BLOCKPATTERNS = load_patterns()
|
||||||
@@ -462,6 +477,65 @@ def build_kreisel_weiche_items(items, planquadrat_cfg, start_elem_nr, dbg=None):
|
|||||||
return elem_nr
|
return elem_nr
|
||||||
|
|
||||||
|
|
||||||
|
def build_strecke_schleus_items(items, planquadrat_cfg, start_elem_nr, dbg=None):
|
||||||
|
"""Haengt fuer jedes Strecken-Ende mit anschliessendem Kreisel eine
|
||||||
|
"ILS Ausschleuselement"- (Anfang) bzw. "ILS Einschleuselement"-Zeile
|
||||||
|
(Ende) an items an (siehe export_neighbors.compute_strecke_kreisel_schleus).
|
||||||
|
|
||||||
|
Muss NACH build_kreisel_weiche_items laufen, damit die TeileId-Zaehlung
|
||||||
|
(max_numeric_teileid) auch die bereits erzeugten Weichen einschliesst.
|
||||||
|
Position = Kollisionsposition Kreisel<->Strecke; Nachbarn = Kreisel-ID +
|
||||||
|
Strecken-ID; Anzahl 1; K1-K4/Insertpoint leer; Boundingbox = die statische,
|
||||||
|
an der Kreiselachse ausgerichtete Default-Box.
|
||||||
|
|
||||||
|
Rueckgabe: fortgeschriebene Elementnummer.
|
||||||
|
"""
|
||||||
|
schleus, warnings = compute_strecke_kreisel_schleus(
|
||||||
|
items, durchmesser_mm=KREISEL_DURCHMESSER_MM, dbg=dbg)
|
||||||
|
|
||||||
|
# Warnungen (>2 Kreisel-BBox-Beruehrungen je Strecke) auf die Strecken-Zeile
|
||||||
|
# schreiben (Spalte "Warnungen") und in die Konsole ausgeben.
|
||||||
|
if warnings:
|
||||||
|
strecke_by_id = {it.get("teileid", ""): it for it in items
|
||||||
|
if it.get("teileart", "") in GF_VF_TEILEARTEN}
|
||||||
|
for sid, text in warnings.items():
|
||||||
|
if sid in strecke_by_id:
|
||||||
|
strecke_by_id[sid]["warnung"] = text
|
||||||
|
print(f"[export_csv] WARNUNG: Strecke {sid} {text}")
|
||||||
|
|
||||||
|
naechste_id = max_numeric_teileid(items)
|
||||||
|
elem_nr = start_elem_nr
|
||||||
|
zaehler = {}
|
||||||
|
for sp in schleus:
|
||||||
|
elem_nr += 1
|
||||||
|
naechste_id += 1
|
||||||
|
teileid = f"{naechste_id:04d}"
|
||||||
|
teileart = sp["teileart"]
|
||||||
|
zaehler[teileart] = zaehler.get(teileart, 0) + 1
|
||||||
|
planquadrat = (compute_planquadrat(sp["x"], sp["y"], planquadrat_cfg)
|
||||||
|
if planquadrat_cfg is not None else "")
|
||||||
|
items.append({
|
||||||
|
"nr": elem_nr,
|
||||||
|
"teileart": teileart,
|
||||||
|
"teileid": teileid,
|
||||||
|
"bezeichnung": f"{teileart} :{zaehler[teileart]}",
|
||||||
|
"planquadrat": planquadrat,
|
||||||
|
"position": f'{sp["x"]:.2f}, {sp["y"]:.2f}, {sp["z"]:.2f}',
|
||||||
|
"boundingbox": f'{sp["dx"]:.2f}, {sp["dy"]:.2f}, {sp["dz"]:.2f}',
|
||||||
|
"insertpoint": "",
|
||||||
|
"k1": "", "k2": "", "k3": "", "k4": "",
|
||||||
|
"nachbarn": ", ".join([sp["kreisel_id"], sp["strecke_id"]]),
|
||||||
|
"fehler": "",
|
||||||
|
"merkmale": {
|
||||||
|
"Erzeugt aus": "Kreisel-Strecke-Kollision",
|
||||||
|
"Kreisel": sp["kreisel_id"],
|
||||||
|
"Strecke": sp["strecke_id"],
|
||||||
|
"Streckenende": sp["ende_label"],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return elem_nr
|
||||||
|
|
||||||
|
|
||||||
def process_blocks(blocks, lookup, dbg=None):
|
def process_blocks(blocks, lookup, dbg=None):
|
||||||
# x_origin_mm/y_origin_mm einmalig fuer diesen Exportlauf aufloesen (siehe
|
# x_origin_mm/y_origin_mm einmalig fuer diesen Exportlauf aufloesen (siehe
|
||||||
# export_planquadrat.resolve_origins): automatisch aus den Bloecken, falls
|
# export_planquadrat.resolve_origins): automatisch aus den Bloecken, falls
|
||||||
@@ -565,6 +639,10 @@ def process_blocks(blocks, lookup, dbg=None):
|
|||||||
"bezeichnung": f"VarioFoerderer :{vf_count}",
|
"bezeichnung": f"VarioFoerderer :{vf_count}",
|
||||||
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
||||||
"merkmale": build_variofoerderer_merkmale(block),
|
"merkmale": build_variofoerderer_merkmale(block),
|
||||||
|
# x/y (Block-Einfuegepunkt = Kettenanfang) fuer die Anfang/Ende-
|
||||||
|
# Bestimmung der Ein-/Ausschleuselemente (compute_strecke_kreisel_schleus)
|
||||||
|
"x": block.get("x", 0.0),
|
||||||
|
"y": block.get("y", 0.0),
|
||||||
**bbox_columns(block),
|
**bbox_columns(block),
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
@@ -580,6 +658,10 @@ def process_blocks(blocks, lookup, dbg=None):
|
|||||||
"bezeichnung": f"Gefaellestrecke :{gf_count}",
|
"bezeichnung": f"Gefaellestrecke :{gf_count}",
|
||||||
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
||||||
"merkmale": build_variofoerderer_merkmale(block),
|
"merkmale": build_variofoerderer_merkmale(block),
|
||||||
|
# x/y (Block-Einfuegepunkt = Kettenanfang) fuer die Anfang/Ende-
|
||||||
|
# Bestimmung der Ein-/Ausschleuselemente (compute_strecke_kreisel_schleus)
|
||||||
|
"x": block.get("x", 0.0),
|
||||||
|
"y": block.get("y", 0.0),
|
||||||
**bbox_columns(block),
|
**bbox_columns(block),
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
@@ -728,8 +810,11 @@ def process_blocks(blocks, lookup, dbg=None):
|
|||||||
# Nach der Kollisions-/Nachbarschaftserkennung: an jedem Kreisel-
|
# Nach der Kollisions-/Nachbarschaftserkennung: an jedem Kreisel-
|
||||||
# Beruehrpunkt eine ILS-Weiche anhaengen (eigene Zeilen am Listenende -
|
# Beruehrpunkt eine ILS-Weiche anhaengen (eigene Zeilen am Listenende -
|
||||||
# sie nehmen an der Nachbarschaftserkennung selbst nicht teil, ihre
|
# sie nehmen an der Nachbarschaftserkennung selbst nicht teil, ihre
|
||||||
# Nachbarn sind fest die beiden beruehrenden Kreisel).
|
# Nachbarn sind fest die beiden beruehrenden Kreisel). Danach an jedem
|
||||||
build_kreisel_weiche_items(items, planquadrat_cfg, elem_nr, dbg=dbg)
|
# Strecken-Ende mit anschliessendem Kreisel ein Aus-/Einschleuselement -
|
||||||
|
# die TeileId-Zaehlung laeuft ueber beide hinweg weiter (Weichen zuerst).
|
||||||
|
elem_nr = build_kreisel_weiche_items(items, planquadrat_cfg, elem_nr, dbg=dbg)
|
||||||
|
build_strecke_schleus_items(items, planquadrat_cfg, elem_nr, dbg=dbg)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
@@ -756,6 +841,7 @@ def format_csv_line(item):
|
|||||||
f';"{csv_quote(item["k4"])}"'
|
f';"{csv_quote(item["k4"])}"'
|
||||||
f';"{csv_quote(item["nachbarn"])}"'
|
f';"{csv_quote(item["nachbarn"])}"'
|
||||||
f';"{csv_quote(item["fehler"])}"'
|
f';"{csv_quote(item["fehler"])}"'
|
||||||
|
f';"{csv_quote(item.get("warnung", ""))}"'
|
||||||
f';{merkmale_json}'
|
f';{merkmale_json}'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -812,7 +898,7 @@ def main():
|
|||||||
print(f"[export_csv] Kollisions-Debuglog geschrieben: {NEIGHBOR_DEBUG_TARGET}")
|
print(f"[export_csv] Kollisions-Debuglog geschrieben: {NEIGHBOR_DEBUG_TARGET}")
|
||||||
|
|
||||||
header = ("Elementnummer;TeileArt;TeileId;Bezeichnung;Planquadrat;Anzahl;Position;Boundingbox;"
|
header = ("Elementnummer;TeileArt;TeileId;Bezeichnung;Planquadrat;Anzahl;Position;Boundingbox;"
|
||||||
"Insertpoint;K1;K2;K3;K4;Nachbarn;Fehler;Merkmale")
|
"Insertpoint;K1;K2;K3;K4;Nachbarn;Fehler;Warnungen;Merkmale")
|
||||||
with open(output_csv, "w", encoding="utf-8") as f:
|
with open(output_csv, "w", encoding="utf-8") as f:
|
||||||
f.write(header + "\n")
|
f.write(header + "\n")
|
||||||
for item in items:
|
for item in items:
|
||||||
|
|||||||
@@ -83,6 +83,17 @@ KREISEL_SPLIT_TEILEART = "ILS 2.0 Kreisel"
|
|||||||
# (compute_kreisel_touch_switches / export_csv.py).
|
# (compute_kreisel_touch_switches / export_csv.py).
|
||||||
WEICHE_TEILEART = "ILS Weiche"
|
WEICHE_TEILEART = "ILS Weiche"
|
||||||
|
|
||||||
|
# TeileArten der automatisch an den Strecken-Enden (Kollision Kreisel <->
|
||||||
|
# Gefaellestrecke/Foerderer) erzeugten Ein-/Ausschleuselemente
|
||||||
|
# (compute_strecke_kreisel_schleus / export_csv.py).
|
||||||
|
AUSSCHLEUS_TEILEART = "ILS Ausschleuselement"
|
||||||
|
EINSCHLEUS_TEILEART = "ILS Einschleuselement"
|
||||||
|
|
||||||
|
# Strecken-TeileArten, die Ein-/Ausschleuselemente bekommen: Gefaellestrecke
|
||||||
|
# und (Vario-)Foerderer. "ILS 2.0 Strecke - Modul" (Einzelkomponenten) bewusst
|
||||||
|
# NICHT - nur die kompletten GF_n/VF_n-Bloecke.
|
||||||
|
GF_VF_TEILEARTEN = {"ILS 2.0 Gefaellestrecke", "ILS 2.0 Strecke"}
|
||||||
|
|
||||||
# Statische Default-Boundingbox einer solchen ILS-Weiche (Laenge x Breite x
|
# Statische Default-Boundingbox einer solchen ILS-Weiche (Laenge x Breite x
|
||||||
# Hoehe in mm). Die Laengsseite (WEICHE_BOX_LAENGE_MM) wird senkrecht zur
|
# Hoehe in mm). Die Laengsseite (WEICHE_BOX_LAENGE_MM) wird senkrecht zur
|
||||||
# Achse desjenigen Kreisels ausgerichtet, dessen Endkreis den Beruehrpunkt
|
# Achse desjenigen Kreisels ausgerichtet, dessen Endkreis den Beruehrpunkt
|
||||||
@@ -945,3 +956,175 @@ def compute_kreisel_touch_switches(items, tolerance_mm, durchmesser_mm=800.0, db
|
|||||||
dbg(f"Erzeugte ILS-Weichen: {len(switches)}")
|
dbg(f"Erzeugte ILS-Weichen: {len(switches)}")
|
||||||
|
|
||||||
return switches
|
return switches
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Kreisel <-> Strecken-Enden -> Ein-/Ausschleuselemente
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _closest_pt_on_segment(a, b, p):
|
||||||
|
"""Naechstgelegener Punkt auf der 2D-Strecke [a,b] zum Punkt p."""
|
||||||
|
abx, aby = b[0] - a[0], b[1] - a[1]
|
||||||
|
denom = abx * abx + aby * aby
|
||||||
|
if denom <= 1e-9:
|
||||||
|
return (a[0], a[1])
|
||||||
|
t = _clamp(((p[0] - a[0]) * abx + (p[1] - a[1]) * aby) / denom, 0.0, 1.0)
|
||||||
|
return (a[0] + abx * t, a[1] + aby * t)
|
||||||
|
|
||||||
|
|
||||||
|
def _strecke_ends(bbox, insert_xy):
|
||||||
|
"""Die beiden Enden einer GF/VF-Strecke als (Anfang, Ende) in Welt-x/y.
|
||||||
|
|
||||||
|
Enden = Mitten der beiden kurzen Stirnseiten der (achsparallelen) Bounding-
|
||||||
|
Box entlang ihrer laengeren Ausdehnung. "Anfang" ist das Ende, das dem
|
||||||
|
Block-Einfuegepunkt (insert_xy = Kettenanfang/startpunkt, siehe
|
||||||
|
ssg-block-wrap-welt) naeher liegt; "Ende" das andere.
|
||||||
|
"""
|
||||||
|
cx, cy = bbox.get("cx", 0.0), bbox.get("cy", 0.0)
|
||||||
|
dx, dy = bbox.get("dx", 0.0), bbox.get("dy", 0.0)
|
||||||
|
if dx >= dy:
|
||||||
|
e0, e1 = (cx - dx / 2.0, cy), (cx + dx / 2.0, cy)
|
||||||
|
else:
|
||||||
|
e0, e1 = (cx, cy - dy / 2.0), (cx, cy + dy / 2.0)
|
||||||
|
ix, iy = insert_xy
|
||||||
|
if math.hypot(e0[0] - ix, e0[1] - iy) <= math.hypot(e1[0] - ix, e1[1] - iy):
|
||||||
|
return e0, e1
|
||||||
|
return e1, e0
|
||||||
|
|
||||||
|
|
||||||
|
def _distinct_kreisel_ids(nachbarn):
|
||||||
|
"""Eindeutige Kreisel-TeileIds aus einem Nachbarn-String ("0009-L, 0002-R,
|
||||||
|
0001-L" -> ["0009", "0002", "0001"]). Das Haelften-Suffix -L/-R (siehe
|
||||||
|
kreisel_half_bboxes) wird entfernt, Reihenfolge bleibt erhalten."""
|
||||||
|
ids = []
|
||||||
|
for part in (nachbarn or "").split(","):
|
||||||
|
p = part.strip()
|
||||||
|
if not p:
|
||||||
|
continue
|
||||||
|
if p.endswith("-L") or p.endswith("-R"):
|
||||||
|
p = p[:-2]
|
||||||
|
if p not in ids:
|
||||||
|
ids.append(p)
|
||||||
|
return ids
|
||||||
|
|
||||||
|
|
||||||
|
def compute_strecke_kreisel_schleus(items, durchmesser_mm=800.0, dbg=None):
|
||||||
|
"""Erzeugt je Gefaellestrecke/Foerderer (GF_VF_TEILEARTEN) an Anfang und
|
||||||
|
Ende ein Aus- bzw. Einschleuselement an der Kollisionsposition zum
|
||||||
|
anschliessenden Kreisel.
|
||||||
|
|
||||||
|
Grundlage sind NICHT eigene Abstandsschwellen, sondern die bereits per
|
||||||
|
Bounding-Box ermittelten Kreisel-Nachbarn der Strecke (Spalte "Nachbarn",
|
||||||
|
siehe compute_neighbor_ids). Jeder der beiden Streckenenden (Anfang = dem
|
||||||
|
Block-Einfuegepunkt naeheres Ende, siehe _strecke_ends) wird der unter
|
||||||
|
diesen Nachbarn naechstgelegene Kreisel zugeordnet:
|
||||||
|
- Am ANFANG entsteht ein Ausschleuselement (AUSSCHLEUS_TEILEART),
|
||||||
|
- am ENDE ein Einschleuselement (EINSCHLEUS_TEILEART).
|
||||||
|
Beruehrt eine Strecke nur EINEN Kreisel (z.B. ein Foerderer, der zum selben
|
||||||
|
Kreisel zurueckfuehrt), wird dieser beiden Enden zugeordnet. Beruehrt sie
|
||||||
|
KEINEN Kreisel, entstehen keine Elemente.
|
||||||
|
|
||||||
|
Eine Strecke sollte genau zwei Kreisel-Bounding-Boxen beruehren (ein Anfang
|
||||||
|
+ ein Ende). Beruehrt ihre BBox MEHR als zwei (mit exakten 3D-BBoxen sollte
|
||||||
|
das nicht vorkommen - hier wird in x/y ohne Hoehe geprueft), entsteht eine
|
||||||
|
Warnung: die beiden Enden bekommen weiterhin ihren jeweils naechsten
|
||||||
|
Kreisel, die ueberzaehligen werden ignoriert und in der Warnung genannt.
|
||||||
|
|
||||||
|
Position eines Elements = der Punkt auf dem Kreiselrand in Richtung
|
||||||
|
Streckenende (Beruehrstelle Kreisel<->Strecke). Die statische Box
|
||||||
|
(WEICHE_BOX_*) wird - wie bei der Weiche - mit ihrer Laengsseite senkrecht
|
||||||
|
zur Kreiselachse ausgerichtet (Welt-AABB, siehe _oriented_box_aabb).
|
||||||
|
|
||||||
|
Rueckgabe: (result, warnings)
|
||||||
|
result = Liste von dicts:
|
||||||
|
{"teileart": AUSSCHLEUS_/EINSCHLEUS_TEILEART,
|
||||||
|
"x","y","z": Kollisionsposition (mm),
|
||||||
|
"dx","dy","dz": Welt-AABB der ausgerichteten Box (mm),
|
||||||
|
"kreisel_id": TeileId des anschliessenden Kreisels,
|
||||||
|
"strecke_id": TeileId der GF/VF,
|
||||||
|
"ende_label": "Anfang" | "Ende"}
|
||||||
|
warnings = dict {strecke_teileid: warntext} fuer Strecken mit > 2
|
||||||
|
Kreisel-BBox-Beruehrungen.
|
||||||
|
"""
|
||||||
|
kreisel = {}
|
||||||
|
for it in items:
|
||||||
|
if it.get("teileart", "") == KREISEL_SPLIT_TEILEART:
|
||||||
|
kreisel[it.get("teileid", "")] = _kreisel_capsule(it, durchmesser_mm)
|
||||||
|
|
||||||
|
if dbg:
|
||||||
|
dbg("")
|
||||||
|
dbg("=== Strecken-Enden <-> Kreisel -> Ein-/Ausschleuselemente ===")
|
||||||
|
dbg(f"Echte Kreisel: {len(kreisel)}; Zuordnung ueber die BBox-Nachbarn "
|
||||||
|
f"der Strecke (naechster Kreisel je Ende), erwartet 2 Kreisel/Strecke")
|
||||||
|
|
||||||
|
result = []
|
||||||
|
warnings = {}
|
||||||
|
for it in items:
|
||||||
|
if it.get("teileart", "") not in GF_VF_TEILEARTEN:
|
||||||
|
continue
|
||||||
|
bbox = it.get("_bbox")
|
||||||
|
if not bbox:
|
||||||
|
continue
|
||||||
|
sid = it.get("teileid", "")
|
||||||
|
nb_ids = [k for k in _distinct_kreisel_ids(it.get("nachbarn", "")) if k in kreisel]
|
||||||
|
if not nb_ids:
|
||||||
|
if dbg:
|
||||||
|
dbg(f" Strecke {sid}: kein Kreisel-Nachbar -> keine Schleuselemente")
|
||||||
|
continue
|
||||||
|
|
||||||
|
insert_xy = (it.get("x", bbox.get("cx", 0.0)) or 0.0,
|
||||||
|
it.get("y", bbox.get("cy", 0.0)) or 0.0)
|
||||||
|
anfang_end, ende_end = _strecke_ends(bbox, insert_xy)
|
||||||
|
scz = bbox.get("cz", 0.0)
|
||||||
|
|
||||||
|
def _nearest(end_pt):
|
||||||
|
best = None
|
||||||
|
for kid in nb_ids:
|
||||||
|
cap = kreisel[kid]
|
||||||
|
p = _closest_pt_on_segment(cap["a"], cap["b"], end_pt)
|
||||||
|
d = math.hypot(p[0] - end_pt[0], p[1] - end_pt[1])
|
||||||
|
if best is None or d < best[3]:
|
||||||
|
best = (kid, cap, p, d)
|
||||||
|
return best
|
||||||
|
|
||||||
|
a_best = _nearest(anfang_end)
|
||||||
|
e_best = _nearest(ende_end)
|
||||||
|
|
||||||
|
if len(nb_ids) > 2:
|
||||||
|
genutzt = {a_best[0], e_best[0]}
|
||||||
|
extra = [k for k in nb_ids if k not in genutzt]
|
||||||
|
warnings[sid] = (
|
||||||
|
f"beruehrt {len(nb_ids)} Kreisel-BoundingBoxen "
|
||||||
|
f"(erwartet 2 = Anfang + Ende): {', '.join(nb_ids)}"
|
||||||
|
+ (f"; ueberzaehlig/ignoriert: {', '.join(extra)}" if extra else "")
|
||||||
|
+ " - mit exakten 3D-BoundingBoxen sollte das nicht auftreten")
|
||||||
|
if dbg:
|
||||||
|
dbg(f" Strecke {sid}: WARNUNG - {warnings[sid]}")
|
||||||
|
|
||||||
|
for best, teileart, label, end_pt in (
|
||||||
|
(a_best, AUSSCHLEUS_TEILEART, "Anfang", anfang_end),
|
||||||
|
(e_best, EINSCHLEUS_TEILEART, "Ende", ende_end)):
|
||||||
|
kid, cap, p, d = best
|
||||||
|
if d > 1e-6:
|
||||||
|
ux, uy = (end_pt[0] - p[0]) / d, (end_pt[1] - p[1]) / d
|
||||||
|
contact = (p[0] + ux * cap["r"], p[1] + uy * cap["r"])
|
||||||
|
else:
|
||||||
|
contact = p
|
||||||
|
dx, dy, dz = _oriented_box_aabb(
|
||||||
|
cap["rot"], WEICHE_BOX_LAENGE_MM, WEICHE_BOX_BREITE_MM, WEICHE_BOX_HOEHE_MM)
|
||||||
|
result.append({
|
||||||
|
"teileart": teileart,
|
||||||
|
"x": contact[0], "y": contact[1], "z": (cap["z"] + scz) / 2.0,
|
||||||
|
"dx": dx, "dy": dy, "dz": dz,
|
||||||
|
"kreisel_id": kid, "strecke_id": sid, "ende_label": label,
|
||||||
|
})
|
||||||
|
if dbg:
|
||||||
|
dbg(f" Strecke {sid} {label}: Kreisel {kid} (Abstand Ende<->Achse "
|
||||||
|
f"{d:.0f}mm) -> {teileart} bei ({contact[0]:.0f}, {contact[1]:.0f}, "
|
||||||
|
f"{(cap['z'] + scz) / 2.0:.0f}), Box-AABB ({dx:.0f}, {dy:.0f}, {dz:.0f})")
|
||||||
|
|
||||||
|
if dbg:
|
||||||
|
dbg(f"Erzeugte Ein-/Ausschleuselemente: {len(result)}; "
|
||||||
|
f"Strecken mit Warnung (>2 Kreisel): {len(warnings)}")
|
||||||
|
|
||||||
|
return result, warnings
|
||||||
|
|||||||
Reference in New Issue
Block a user