Files
dxfmakros/lib/extract_polylines.py
T
m.stangl b4f9c43ae3 chore: verwaiste stueckliste.dcl und create_tests.bat entfernt, object_data-Verweise umgestellt
- dcl/stueckliste.dcl: wurde von keinem Code geladen (ssg-sl-dialog existiert nicht)
- bin/create_tests.bat: rief das nicht mehr vorhandene tests/ILS_test.scr auf;
  Ersatz ist run_tests.bat bzw. SSG_RUN_ALL_TESTS
- Kommentare in lib/ und tests/ verweisen statt auf das geloeschte
  tests/testdata/object_data.md auf doc/json-input-lisp.md, mit den dort
  gueltigen Abschnittsnummern (alt 5 -> 2, 3.1 -> 3, 2.1 -> 4.1, 8 -> 6)
- CLAUDE.md / doc/kommandos.md / doc/json-input-lisp.md nachgezogen

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:22:27 +01:00

125 lines
4.6 KiB
Python

#!/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 doc/json-input-lisp.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 <dxf> <output.json> [--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()