CSV-Export: Nachbarschaft/bbox fuer Kreisel-Kreisel-Weichen, sepliste-Ketten und Separatoren repariert
Kreisel-Kreisel-Weichen fehlten bisher im Kreisel-Umlauf (compute_kreisel_umlauf), wodurch angrenzende Separatoren auf falsche/fehlende Nachbarn zeigten. Die kreisel_bounds-Matching-Box ignorierte ausserdem die tatsaechliche Kreiseldrehung (faelschlich als achsparallel angenommen), was bei gedrehten langen Kreiseln (Mubea Kreisel 0002) zu falschen Nachbarschaften fuehrte. Die sepliste-XDATA (AS/SEP/ES-Reihenfolge je VF_n/GF_n) wurde nie ans Python-Item durchgereicht - die gesamte Ketten-Nachbarschaftserkennung lief dadurch ins Leere. Nach dem Beheben entstand eine Doppelzaehlung: die in einer Kette verpackten Separatoren existieren bereits als echte Proxy-INSERTs (csv:sep-proxies-erzeugen); build_separator_kette_items erzeugte zusaetzlich eigene Zeilen aus der sepliste. Umgebaut zu map_separator_kette_items: ordnet die sepliste-Reihenfolge den vorhandenen Proxy-Items zu (Match ueber Wrapper-ID + INSERT-Punkt) statt neue Zeilen zu erzeugen - keine Doppelzaehlung mehr, Nachbarn korrekt gesetzt. Ausserdem: vla-getboundingbox scheiterte in einem realen Export bei ALLEN Bloecken (fehlende bbox ueberall), der Fehler wurde bisher still verschluckt. REGENALL vor der Export-Schleife behebt das; eine neue Diagnose (exp-bbox-fail) meldet kuenftige Ausfaelle statt sie zu verschlucken. Freie/eingebettete Separatoren ohne bbox bekommen zusaetzlich einen Fallback aus einer konfigurierbaren Symbol-Groesse (cfg/export.cfg [Boundingbox] separator_box_*). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_export_separator_bbox.py - Feste Separator-Symbol-Bounding-Box.
|
||||
Laeuft komplett ohne BricsCAD (reine Python-Logik).
|
||||
|
||||
Regressionstest fuer einen realen Mubea-Befund: die meisten Separatoren
|
||||
blieben ohne "Nachbarn", weil in ihrem export_raw.json KEINE Bounding-Box
|
||||
stand. Zwei Ursachen:
|
||||
|
||||
1. In eine VF_n/GF_n-Kette EINGEBETTETE Separatoren sind seit der Umstellung
|
||||
auf die "sepliste"-XDATA kein reales INSERT mehr - vla-getboundingbox kann
|
||||
fuer sie keine Box liefern.
|
||||
2. Frei platzierte Separator_SP-INSERTs, deren vla-getboundingbox beim Export
|
||||
scheiterte (z.B. eingefrorener Layer).
|
||||
|
||||
Beide bekommen jetzt eine feste Symbol-Box aus cfg [Boundingbox]
|
||||
separator_box_* (siehe export_neighbors.separator_bbox / load_separator_box_mm),
|
||||
damit sie ein "_bbox" haben und an der Nachbarschafts-/Umlauf-/Zuordnungs-
|
||||
erkennung teilnehmen.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
|
||||
|
||||
from export_neighbors import ( # noqa: E402
|
||||
separator_bbox,
|
||||
load_separator_box_mm,
|
||||
SEPARATOR_BOX_LAENGE_MM,
|
||||
SEPARATOR_BOX_BREITE_MM,
|
||||
SEPARATOR_BOX_HOEHE_MM,
|
||||
)
|
||||
from export_csv import ( # noqa: E402
|
||||
bbox_columns,
|
||||
_fallback_separator_bbox,
|
||||
)
|
||||
|
||||
|
||||
class TestSeparatorBboxGeometrie:
|
||||
|
||||
def test_unrotiert(self):
|
||||
box = (210.0, 150.0, 14.0)
|
||||
bb = separator_bbox(1000.0, 2000.0, 0.0, separator_box_mm=box)
|
||||
assert bb["cx"] == 1000.0 and bb["cy"] == 2000.0
|
||||
assert abs(bb["dx"] - 210.0) < 1e-6, "Laenge liegt bei Drehung 0 entlang X"
|
||||
assert abs(bb["dy"] - 150.0) < 1e-6, "Breite liegt bei Drehung 0 entlang Y"
|
||||
assert abs(bb["dz"] - 14.0) < 1e-6
|
||||
|
||||
def test_um_90_grad_gedreht_vertauscht_dx_dy(self):
|
||||
box = (210.0, 150.0, 14.0)
|
||||
bb = separator_bbox(0.0, 0.0, 90.0, separator_box_mm=box)
|
||||
assert abs(bb["dx"] - 150.0) < 1e-6, "bei 90 Grad liegt die Breite entlang X"
|
||||
assert abs(bb["dy"] - 210.0) < 1e-6, "bei 90 Grad liegt die Laenge entlang Y"
|
||||
|
||||
def test_default_masse_aus_cfg(self):
|
||||
# load_separator_box_mm liest cfg/export.cfg; die Werte muessen positiv
|
||||
# und plausibel sein (Symbol ~210 x 150 x 14).
|
||||
laenge, breite, hoehe = load_separator_box_mm()
|
||||
assert laenge > 0 and breite > 0 and hoehe > 0
|
||||
# Fallback-Konstanten muessen mit dem gemessenen Symbol uebereinstimmen
|
||||
assert (SEPARATOR_BOX_LAENGE_MM, SEPARATOR_BOX_BREITE_MM, SEPARATOR_BOX_HOEHE_MM) == (210.0, 150.0, 14.0)
|
||||
|
||||
|
||||
class TestFallbackSeparatorBbox:
|
||||
|
||||
def test_fehlende_bbox_wird_ergaenzt(self):
|
||||
"""Ein Separator-INSERT ohne bbox (vla-getboundingbox scheiterte)
|
||||
bekommt aus Position/Drehung + fester Box eine _bbox."""
|
||||
block = {"block_name": "Separator_SP_3D", "x": 5000.0, "y": -3000.0,
|
||||
"z": 100.0, "rotation": 90.0, "attribs": {"ID": "0050"}}
|
||||
cols = bbox_columns(block)
|
||||
assert cols["_bbox"] is None, "Vorbedingung: Block hat keine bbox"
|
||||
|
||||
cols = _fallback_separator_bbox(cols, block, separator_box_mm=(210.0, 150.0, 14.0))
|
||||
assert cols["_bbox"] is not None, "Fallback hat keine _bbox gesetzt"
|
||||
assert cols["_bbox"]["cx"] == 5000.0 and cols["_bbox"]["cy"] == -3000.0
|
||||
# 90 Grad -> dx=Breite, dy=Laenge
|
||||
assert abs(cols["_bbox"]["dx"] - 150.0) < 1e-6
|
||||
assert abs(cols["_bbox"]["dy"] - 210.0) < 1e-6
|
||||
assert cols["boundingbox"] != "", "Boundingbox-Spalte muss nun gefuellt sein"
|
||||
|
||||
def test_vorhandene_bbox_bleibt_unveraendert(self):
|
||||
"""Hat der Block bereits eine echte bbox (vla-getboundingbox erfolgreich),
|
||||
darf der Fallback sie NICHT ueberschreiben."""
|
||||
block = {"block_name": "Separator_SP_3D", "x": 5000.0, "y": -3000.0,
|
||||
"rotation": 90.0,
|
||||
"bbox": {"cx": 5001.0, "cy": -3002.0, "cz": 7.0,
|
||||
"dx": 209.9, "dy": 150.0, "dz": 3.0}}
|
||||
cols = bbox_columns(block)
|
||||
assert cols["_bbox"] is not None
|
||||
original = dict(cols["_bbox"])
|
||||
|
||||
cols2 = _fallback_separator_bbox(cols, block)
|
||||
assert cols2["_bbox"] == original, "echte bbox wurde vom Fallback ueberschrieben"
|
||||
Reference in New Issue
Block a user