#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ extract_polylines.py - Liest LWPOLYLINE-Objekte aus einem DXF und schreibt sie als JSON (Handle, Layer, Stuetzpunkte in OCS und Weltkoordinaten, Segment-/Gesamtlaengen). Hintergrund: data/polylines.dxf enthaelt 5 LWPOLYLINE-Objekte auf Layer "Z-6" - Streckenzuege, die reale Foerderer-Pfade der HundM05-Zeichnung nachbilden (siehe data/polylines.md, von Hand ausgewertet). Dieses Skript macht dieselbe Auswertung reproduzierbar und maschinenlesbar, als Grundlage fuer einen neuen Testfall (siehe tests/testdata/object_data.md fuer das allgemeine JSON-Schema). Eine Polylinie kann auf einer gekippten Ebene liegen (Gruppencode 210/220/230 != (0,0,1)); die Gruppencodes 10/20 sind dann OCS-, keine Weltkoordinaten. Die Umrechnung nutzt ezdxf (Arbitrary-Axis-Algorithmus), darum reicht hier ezdxf.readfile() statt eines eigenen Streaming-Parsers - polylines.dxf ist mit ca. 124 MB ASCII-DXF deutlich kleiner als die binaeren GB-Zeichnungen, fuer die dxf_scan_components.py den mmap-Weg braucht. Aufruf: python extract_polylines.py [--layer Z-6] """ import argparse import json import math import os import re import sys import ezdxf PREFIX = "[extract_polylines]" RUND = 4 def arrays_einzeilig(text): """Rein numerische JSON-Arrays auf eine Zeile ziehen (wie in dxf_scan_components.py) - noetig, falls die Datei spaeter ueber ssg-parse-json-array (zeilenweiser LISP-Parser) gelesen wird.""" def zusammen(m): return "[" + " ".join(m.group(1).split()) + "]" return re.sub(r"\[([\s\d.,+\-eE]*?)\]", zusammen, text) def lese_polylinien(dxf_path, layer): doc = ezdxf.readfile(dxf_path) msp = doc.modelspace() ergebnis = [] for pl in msp.query("LWPOLYLINE"): if layer and pl.dxf.layer != layer: continue elevation = pl.dxf.elevation ocs = pl.ocs() punkte_ocs = [(p[0], p[1]) for p in pl.get_points("xy")] punkte_welt = [ocs.to_wcs((x, y, elevation)) for x, y in punkte_ocs] laengen = [0.0] for i in range(1, len(punkte_welt)): a, b = punkte_welt[i - 1], punkte_welt[i] laengen.append(math.dist((a.x, a.y, a.z), (b.x, b.y, b.z))) ergebnis.append({ "handle": pl.dxf.handle, "layer": pl.dxf.layer, "geschlossen": bool(pl.closed), "elevation": round(elevation, RUND), "extrusion": [round(v, RUND) for v in tuple(pl.dxf.extrusion)], "anzahl_punkte": len(punkte_welt), "punkte_ocs": [[round(x, RUND), round(y, RUND)] for x, y in punkte_ocs], "punkte_welt": [[round(p.x, RUND), round(p.y, RUND), round(p.z, RUND)] for p in punkte_welt], "segment_laengen": [round(l, RUND) for l in laengen], "gesamtlaenge": round(sum(laengen), RUND), }) return ergebnis def main(): parser = argparse.ArgumentParser( description="Liest LWPOLYLINE-Objekte aus einem DXF und schreibt " "sie (inkl. OCS->Welt-Umrechnung) als JSON.") parser.add_argument("dxf", help="Quellzeichnung (ASCII- oder " "binaeres DXF)") parser.add_argument("output", help="Ziel-JSON") parser.add_argument("--layer", default="Z-6", help="Nur Polylinien auf diesem Layer (Default: " "Z-6, leer = alle Layer)") args = parser.parse_args() if not os.path.isfile(args.dxf): print("%s FEHLER: DXF nicht gefunden: %s" % (PREFIX, args.dxf)) sys.exit(1) groesse = os.path.getsize(args.dxf) / (1024.0 * 1024.0) print("%s lese %s (%.1f MB) ..." % (PREFIX, args.dxf, groesse)) ergebnis = lese_polylinien(args.dxf, args.layer) print("%s %d LWPOLYLINE-Objekte auf Layer '%s' gefunden" % (PREFIX, len(ergebnis), args.layer or "*")) for e in ergebnis: print("%s Handle %-4s %d Punkte Gesamtlaenge %.2f mm%s" % (PREFIX, e["handle"], e["anzahl_punkte"], e["gesamtlaenge"], " (gekippte Ebene)" if e["extrusion"] != [0.0, 0.0, 1.0] else "")) ziel_dir = os.path.dirname(os.path.abspath(args.output)) if ziel_dir and not os.path.isdir(ziel_dir): os.makedirs(ziel_dir) with open(args.output, "w", encoding="utf-8") as fh: fh.write(arrays_einzeilig(json.dumps(ergebnis, ensure_ascii=False, indent=2))) fh.write("\n") print("%s %d Eintraege geschrieben -> %s" % (PREFIX, len(ergebnis), args.output)) if __name__ == "__main__": main()