[FEAT] Werkzeuge zum Auslesen fremder Zeichnungen und der VF-Eingabe-Journale
Grundlage fuer reproduzierbare Nachbau-Testfaelle aus echten Projekt- zeichnungen (HundM05). - lib/vf_journal_export.py (neu): liest die Eingabe-Journale der VF_n-Bloecke aus der XDATA (App SSG_VF_EDIT, Marker "linienzug") und schreibt sie als Frage-Antwort-Protokoll im Schema von tests/testdata/linienzug_tests.json - Gegenstueck zu vfl-entry->string, ohne ezdxf (reine Gruppencode-Lesung). Optional --csv fuer Strecken-ID und Erwartungswerte je Kette. - lib/extract_polylines.py (neu): LWPOLYLINE-Objekte als JSON, inkl. OCS->Welt-Umrechnung fuer gekippte Ebenen (Handles, Segment-/Gesamtlaengen). - lib/dxf_scan_components.py: erkennt jetzt zusaetzlich, ob ein Block ein 2D-Schema oder ein 3D-Modell ist (TYPEN_3D, ist_3d_block/darstellung_von) - eine Anlagenzeichnung enthaelt beides. Dazu Sensor-Erfassung, Zwillings-Zusammenfuehrung und Hoehenschaetzung aus der Umgebung. - lib/dxf_abbild.py: gibt die erkannte Darstellung je Element mit aus. - tests/test_vf_journal_grammatik.py (neu): prueft den Journal-Dekoder gegen handgebaute Journale - ohne CAD und ohne die grosse DXF. Deckt die zwei Zweige ab, die in den echten Ketten nicht vorkommen: Glied "Linie-VF" und eine VF-Einheit mit gewinkeltem Erstkoerper (dort fragt vfl-vf-einheit KEINE Separator-/Endpunkt-Fragen vorab, anders als beim horizontalen). - data/polylines.md, tests/testdata/hundm05_linienzug_polylines.json: ausgewertete Streckenzuege der Quellzeichnung. - .gitignore: die Quellzeichnungen selbst (data/polylines.dxf 124 MB, data/polylines.dwg, tests/HM5.dwg) bleiben draussen - eingecheckt ist die Auswertung, nicht die Zeichnung. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+18
-7
@@ -46,7 +46,8 @@ from ezdxf.math import Matrix44
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from dxf_scan_components import ( # noqa: E402
|
||||
RUND, familie_von, insert_matrix, lese_inserts, im_fenster)
|
||||
RUND, darstellung_von, familie_von, insert_matrix, lese_inserts,
|
||||
im_fenster)
|
||||
|
||||
PREFIX = "[dxf_abbild]"
|
||||
|
||||
@@ -146,13 +147,19 @@ def welt_daten(rec, mutter, block_base):
|
||||
return mat, ursprung, rot
|
||||
|
||||
|
||||
def element_dict(rec, ursprung, rot, tiefe):
|
||||
def element_dict(rec, ursprung, rot, tiefe, block_info=None,
|
||||
dim_memo=None):
|
||||
"""Einen INSERT als Abbild-Eintrag beschreiben."""
|
||||
attribs = dict(rec["attribs"])
|
||||
typ, typ_quelle = typ_von(attribs, rec["name"])
|
||||
eintrag = collections.OrderedDict()
|
||||
eintrag["block"] = rec["name"]
|
||||
eintrag["layer"] = rec["layer"] or ""
|
||||
if block_info is not None:
|
||||
# 2D-Schema oder 3D-Modell - die Zeichnung enthaelt beides fuer
|
||||
# dieselbe Anlage (siehe dxf_scan_components.TYPEN_3D).
|
||||
eintrag["darstellung"] = darstellung_von(rec["name"], block_info,
|
||||
dim_memo)
|
||||
eintrag["typ"] = typ
|
||||
eintrag["typ_quelle"] = typ_quelle
|
||||
eintrag["position"] = [round(ursprung.x, RUND) + 0.0,
|
||||
@@ -183,13 +190,14 @@ def element_dict(rec, ursprung, rot, tiefe):
|
||||
|
||||
|
||||
def baue_abbild(ms_inserts, block_inserts, block_base, fenster=None,
|
||||
max_tiefe=12, ohne_container=False):
|
||||
max_tiefe=12, ohne_container=False, block_info=None):
|
||||
"""Alle Modelspace-INSERTs als Abbild-Baum beschreiben.
|
||||
|
||||
Rueckgabe: (elemente, statistik). Jedes Element der obersten Ebene enthaelt
|
||||
seine aufgeloesten Unterkomponenten unter "unterkomponenten".
|
||||
"""
|
||||
stat = collections.Counter()
|
||||
dim_memo = {}
|
||||
|
||||
def kinder_von(name, mat, tiefe, pfad):
|
||||
"""Verschachtelte INSERTs eines Blocks rekursiv beschreiben."""
|
||||
@@ -203,7 +211,8 @@ def baue_abbild(ms_inserts, block_inserts, block_base, fenster=None,
|
||||
stat["container_uebersprungen"] += 1
|
||||
continue
|
||||
kind_mat, ursprung, rot = welt_daten(rec, mat, block_base)
|
||||
eintrag = element_dict(rec, ursprung, rot, tiefe)
|
||||
eintrag = element_dict(rec, ursprung, rot, tiefe, block_info,
|
||||
dim_memo)
|
||||
stat["unterkomponenten"] += 1
|
||||
if rec["name"] not in pfad:
|
||||
enkel = kinder_von(rec["name"], kind_mat, tiefe + 1,
|
||||
@@ -224,7 +233,7 @@ def baue_abbild(ms_inserts, block_inserts, block_base, fenster=None,
|
||||
if ohne_container and GEOMETRIE_CONTAINER.match(rec["name"]):
|
||||
stat["container_uebersprungen"] += 1
|
||||
continue
|
||||
eintrag = element_dict(rec, ursprung, rot, 0)
|
||||
eintrag = element_dict(rec, ursprung, rot, 0, block_info, dim_memo)
|
||||
eintrag["nr"] = nr
|
||||
eintrag.move_to_end("nr", last=False)
|
||||
kinder = kinder_von(rec["name"], mat, 1, frozenset({rec["name"]}))
|
||||
@@ -264,14 +273,16 @@ def main():
|
||||
|
||||
groesse = os.path.getsize(args.dxf) / (1024.0 * 1024.0)
|
||||
print("%s lese %s (%.1f MB) ..." % (PREFIX, args.dxf, groesse))
|
||||
ms_inserts, block_inserts, block_base, statistik = lese_inserts(args.dxf)
|
||||
(ms_inserts, block_inserts, block_base, statistik,
|
||||
block_info) = lese_inserts(args.dxf)
|
||||
print("%s %d INSERTs im Modelspace, %d INSERTs gesamt, %d mit gekippter OCS"
|
||||
% (PREFIX, len(ms_inserts), statistik["inserts"],
|
||||
statistik["ocs_gekippt"]))
|
||||
|
||||
elemente, stat = baue_abbild(ms_inserts, block_inserts, block_base,
|
||||
fenster=fenster, max_tiefe=args.max_tiefe,
|
||||
ohne_container=args.ohne_geometrie_container)
|
||||
ohne_container=args.ohne_geometrie_container,
|
||||
block_info=block_info)
|
||||
|
||||
# Kennzahlen, die belegen, wie belastbar die Typangaben sind
|
||||
quellen = collections.Counter()
|
||||
|
||||
Reference in New Issue
Block a user