initialer commit
This commit is contained in:
@@ -1,8 +1,141 @@
|
||||
# drt-verbindungszaehler
|
||||
# DRT-Verbindungszähler - DXF-Analyse-Tool
|
||||
|
||||
Für die Kalkulation eines DRT-Gerüsts ist neben der Anzahl und Länge von Trägern auch die Anzahl und Art der Verbindungen zwischen den Trägern relevant.
|
||||
Die Verbindungen können aus dem Layout in Form von Schnittpunkten der Gerüstlinien gelesen werden.
|
||||
Ein Python-basiertes Werkzeug zur automatischen Erkennung und Markierung von Schnittpunkten zwischen Linien in DXF-Dateien.
|
||||
|
||||
Dieses Tool zählt alle Schnittpunkte zwischen Linien auf vorgegebenen Layern. Dabei wird zwischen Eckverbindung, T-Verbindung und Kreuzverbindung unterschieden.
|
||||
## Überblick
|
||||
|
||||
Ausgabe ist ein Layout, in dem alle gezählten Verbindungen markiert sind, sowie eine Textdatei, in der die Anzahl und Positionen der Verbindungen aufgeführt werden.
|
||||
Dieses Projekt analysiert DXF-Dateien und identifiziert Verbindungs- bzw. Schnittpunkte zwischen Linien auf verschiedenen Layern. Die gefundenen Schnittpunkte werden mit Kreisen markiert und in einer separaten DXF-Datei sowie einem Textbericht ausgegeben.
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```text
|
||||
verbindungszaehler/
|
||||
├── verbindungszaehler.bat # Hauptskript zum Starten der Analyse
|
||||
├── setenv.bat # Umgebungsvariablen und Verzeichnisstruktur
|
||||
├── defaults.cfg # Standardkonfiguration
|
||||
├── lib/
|
||||
│ ├── analyze.py # Python-Analyseskript
|
||||
│ └── dxf_analyzer.py # Kernlogik
|
||||
├── input/ # Eingabeverzeichnis für DXF-Dateien
|
||||
└── output/ # Ausgabeverzeichnis für Ergebnisse
|
||||
```
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Windows-Betriebssystem
|
||||
- Python 3.x installiert und im PATH verfügbar
|
||||
- Python-Bibliothek für DXF-Verarbeitung `ezdxf`
|
||||
- Modul `dxf_analyzer.py` mit der Klasse `IntersectionAnalyzer`
|
||||
|
||||
## Installation
|
||||
|
||||
1. Projekt in ein beliebiges Verzeichnis entpacken
|
||||
2. Sicherstellen, dass Python installiert ist
|
||||
3. Benötigte Python-Pakete installieren (z.B. `pip install ezdxf`)
|
||||
4. DXF-Dateien im `input/`-Verzeichnis ablegen
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Methode 1: Alle Dateien im Input-Verzeichnis analysieren
|
||||
|
||||
Doppelklick auf `verbindungszaehler.bat` oder per Kommandozeile:
|
||||
|
||||
```batch
|
||||
verbindungszaehler.bat
|
||||
```
|
||||
|
||||
Alle DXF-Dateien im `input/`-Verzeichnis werden automatisch mit den Einstellungen aus `defaults.cfg` analysiert.
|
||||
|
||||
### Methode 2: Einzelne Datei per Drag & Drop
|
||||
|
||||
Ziehen Sie eine DXF-Datei direkt auf die `verbindungszaehler.bat` und lassen Sie sie los. Die Datei wird sofort mit den Einstellungen aus `defaults.cfg` analysiert.
|
||||
|
||||
### Methode 3: Einzelne Datei per Kommandozeile
|
||||
|
||||
```batch
|
||||
verbindungszaehler.bat "C:\Pfad\zur\datei.dxf"
|
||||
```
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### defaults.cfg
|
||||
|
||||
Die Datei `defaults.cfg` enthält alle Einstellungen für die Analyse. Passen Sie diese Werte nach Bedarf an:
|
||||
|
||||
```cfg
|
||||
LAYERS=D-1,D-5
|
||||
TOLERANCE=0.06
|
||||
MARK_RADIUS=150
|
||||
```
|
||||
|
||||
**Parameter-Erklärung:**
|
||||
|
||||
- **LAYERS**: Kommagetrennte Liste der zu analysierenden Layer (z.B. "D-1,D-5,D-3")
|
||||
- **TOLERANCE**: Maximaler Abstand in DXF-Einheiten, bei dem zwei Punkte als identisch gelten (für ungenaue Zeichnungen)
|
||||
- **MARK_RADIUS**: Radius der Kreise in DXF-Einheiten, die Schnittpunkte markieren
|
||||
|
||||
**Hinweis:** Nach Änderungen in `defaults.cfg` werden die neuen Werte bei der nächsten Ausführung automatisch verwendet.
|
||||
|
||||
## Ausgabe
|
||||
|
||||
Das Tool erzeugt für jede analysierte DXF-Datei zwei Dateien im `output/`-Verzeichnis:
|
||||
|
||||
1. **[Dateiname]_markiert_[Datum]_[Zeit].dxf**
|
||||
DXF-Datei mit eingezeichneten Kreisen an allen gefundenen Schnittpunkten
|
||||
|
||||
2. **[Dateiname]_analyse_[Datum]_[Zeit].txt**
|
||||
Textbericht mit Details zu den gefundenen Verbindungen (Koordinaten, Anzahl, etc.)
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
```text
|
||||
zeichnung_markiert_20241202_14-30.dxf
|
||||
zeichnung_analyse_20241202_14-30.txt
|
||||
```
|
||||
|
||||
## Funktionsweise
|
||||
|
||||
1. **setenv.bat** richtet die Verzeichnisstruktur ein und lädt die Konfigurationswerte aus `defaults.cfg`
|
||||
2. **verbindungszaehler.bat** verarbeitet die angegebene Datei (oder alle Dateien im Input-Ordner) und ruft das Python-Skript auf
|
||||
3. **analyze.py** lädt die DXF-Datei und übergibt sie zusammen mit den Konfigurationsparametern an den `IntersectionAnalyzer`
|
||||
4. Der Analyzer findet Schnittpunkte zwischen Linien auf den angegebenen Layern innerhalb der definierten Toleranz
|
||||
5. Gefundene Schnittpunkte werden mit Kreisen (Radius aus `MARK_RADIUS`) markiert
|
||||
6. Ergebnisse werden als neue DXF-Datei und Textbericht gespeichert
|
||||
|
||||
## Workflow-Beispiel
|
||||
|
||||
1. DXF-Dateien in den `input/`-Ordner kopieren
|
||||
2. `defaults.cfg` öffnen und gewünschte Layer-Namen, Toleranz und Markierungsradius eintragen
|
||||
3. `verbindungszaehler.bat` per Doppelklick starten **oder** eine einzelne DXF-Datei auf die Batch-Datei ziehen
|
||||
4. Ergebnisse im `output/`-Ordner überprüfen: **Bevor die Anzahl der Verbindungen weiter verwendet wird, sind die markierten Punkte im Ausgabelayout zwingend auf Fehler zu überprüfen!**
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Stellen Sie sicher, dass die Layer-Namen in `defaults.cfg` exakt mit denen in der DXF-Datei übereinstimmen (Groß-/Kleinschreibung beachten)
|
||||
- Bei sehr großen Dateien kann die Analyse einige Zeit in Anspruch nehmen
|
||||
- Die Toleranz sollte an die Genauigkeit Ihrer Zeichnungen angepasst werden (typische Werte: 0.01 bis 0.1)
|
||||
- Der Markierungsradius sollte groß genug sein, um in der Zeichnung sichtbar zu sein, aber klein genug, um Details nicht zu verdecken
|
||||
- Ausgabedateien werden nicht überschrieben, da jede Datei einen eindeutigen Zeitstempel erhält
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
##### "Datei nicht gefunden"
|
||||
|
||||
Überprüfen Sie den Pfad zur DXF-Datei oder legen Sie Dateien im `input/`-Verzeichnis ab.
|
||||
|
||||
##### "Keine Schnittpunkte gefunden"
|
||||
|
||||
- Prüfen Sie, ob die in `defaults.cfg` angegebenen Layer in der DXF-Datei existieren
|
||||
- Erhöhen Sie den TOLERANCE-Wert in `defaults.cfg`, falls Linien nicht exakt verbunden sind
|
||||
|
||||
##### Python-Fehler
|
||||
|
||||
Stellen Sie sicher, dass alle erforderlichen Python-Bibliotheken installiert sind.
|
||||
|
||||
##### Drag & Drop funktioniert nicht
|
||||
|
||||
Stellen Sie sicher, dass Sie die Datei auf `verbindungszaehler.bat` (nicht auf `setenv.bat`) ziehen.
|
||||
|
||||
## Autor
|
||||
|
||||
Simon Hensch
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
LAYERS=D-1,D-5
|
||||
TOLERANCE=0.06
|
||||
MARK_RADIUS=150
|
||||
@@ -0,0 +1,52 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from dxf_analyzer import IntersectionAnalyzer
|
||||
from datetime import datetime
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Bitte eine DXF-Datei angeben.")
|
||||
sys.exit(1)
|
||||
|
||||
dxf_file = Path(sys.argv[1])
|
||||
if not dxf_file.exists():
|
||||
print(f"Datei nicht gefunden: {dxf_file}")
|
||||
sys.exit(1)
|
||||
|
||||
# Kommandozeilenparameter auslesen oder Standardwerte verwenden
|
||||
layers = sys.argv[2] if len(sys.argv) > 2 else "D-1,D-5"
|
||||
tolerance = float(sys.argv[3]) if len(sys.argv) > 3 else 0.04
|
||||
mark_radius = float(sys.argv[4]) if len(sys.argv) > 4 else 100
|
||||
|
||||
layer_list = [layer.strip() for layer in layers.split(",")]
|
||||
|
||||
base_name = dxf_file.stem
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
output_dir = (script_dir / ".." / "output").resolve()
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
datum = datetime.now().strftime("%Y%m%d_%H-%M")
|
||||
|
||||
output_dxf = output_dir / f"{base_name}_markiert_{datum}.dxf"
|
||||
output_txt = output_dir / f"{base_name}_analyse_{datum}.txt"
|
||||
|
||||
analyzer = IntersectionAnalyzer(
|
||||
dxf_path=str(dxf_file),
|
||||
layer_names=layer_list,
|
||||
tolerance=tolerance,
|
||||
mark_radius=mark_radius
|
||||
)
|
||||
|
||||
success = analyzer.analyze(
|
||||
output_dxf=str(output_dxf),
|
||||
output_txt=str(output_txt)
|
||||
)
|
||||
|
||||
if success:
|
||||
print(f"Ergebnis-DXF: {output_dxf}")
|
||||
print(f"Analyse-Textdatei: {output_txt}")
|
||||
else:
|
||||
print("Analyse konnte nicht erfolgreich abgeschlossen werden.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,406 @@
|
||||
"""
|
||||
DXF Schnittpunkt-Analysator
|
||||
Analysiert Linienschnittpunkte in DXF-Dateien und klassifiziert diese nach Typ
|
||||
"""
|
||||
|
||||
import ezdxf
|
||||
from ezdxf.math import Vec3
|
||||
from collections import defaultdict
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from math import sin, cos, pi
|
||||
from datetime import datetime
|
||||
|
||||
class IntersectionAnalyzer:
|
||||
def __init__(self, dxf_path, layer_names, tolerance=0.1, mark_radius=50):
|
||||
"""
|
||||
Initialisiert den Analysator
|
||||
|
||||
Args:
|
||||
dxf_path: Pfad zur DXF-Datei
|
||||
layer_names: Liste der zu analysierenden Layer
|
||||
tolerance: Toleranz für Punktvergleiche in mm (Standard: 0.01)
|
||||
"""
|
||||
self.dxf_path = dxf_path
|
||||
self.layer_names = layer_names
|
||||
self.tolerance = tolerance
|
||||
self.mark_radius = mark_radius
|
||||
self.doc = None
|
||||
self.lines = []
|
||||
self.intersections = {
|
||||
'Kreuzung': [],
|
||||
'T-Verbindung': [],
|
||||
'Eckverbindung': []
|
||||
}
|
||||
self.debug_info = []
|
||||
|
||||
def load_dxf(self):
|
||||
"""Lädt die DXF-Datei"""
|
||||
try:
|
||||
self.doc = ezdxf.readfile(self.dxf_path)
|
||||
print(f"DXF-Datei erfolgreich geladen: {self.dxf_path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Laden der DXF-Datei: {e}")
|
||||
return False
|
||||
|
||||
def extract_lines(self):
|
||||
"""Extrahiert alle Linien aus den angegebenen Layern"""
|
||||
msp = self.doc.modelspace()
|
||||
|
||||
for entity in msp:
|
||||
if entity.dxftype() == 'LINE' and entity.dxf.layer in self.layer_names:
|
||||
start = Vec3(entity.dxf.start.x, entity.dxf.start.y, 0) # Z ignorieren
|
||||
end = Vec3(entity.dxf.end.x, entity.dxf.end.y, 0)
|
||||
|
||||
# Debug: Überprüfe auf extrem kurze Linien (Punkte)
|
||||
length = start.distance(end)
|
||||
if length < 1e-10:
|
||||
print(f"WARNUNG: Punkt statt Linie gefunden bei ({start.x}, {start.y})")
|
||||
continue
|
||||
|
||||
self.lines.append((start, end, entity))
|
||||
|
||||
print(f"Anzahl gefundener Linien: {len(self.lines)}")
|
||||
print(f"Verwendete Toleranz: {self.tolerance} mm")
|
||||
|
||||
# Debug: Zeige alle Linien mit ihren Koordinaten
|
||||
#print("\n=== LINIEN-DETAILS ===")
|
||||
#for i, (start, end, entity) in enumerate(self.lines):
|
||||
#print(f"Linie {i}: Start=({start.x:.3f}, {start.y:.3f}) End=({end.x:.3f}, {end.y:.3f}) Länge={start.distance(end):.3f}")
|
||||
|
||||
def calculate_intersection(self, line1, line2):
|
||||
p1, p2 = line1[0], line1[1]
|
||||
p3, p4 = line2[0], line2[1]
|
||||
|
||||
# Richtungsvektoren
|
||||
d1 = p2 - p1
|
||||
d2 = p4 - p3
|
||||
|
||||
# Kreuzprodukt zur Prüfung auf Parallelität/Kollinearität
|
||||
cross = d1.x * d2.y - d1.y * d2.x
|
||||
|
||||
# Kollinear oder parallel
|
||||
if abs(cross) < 1e-12:
|
||||
for pt in (p1, p2):
|
||||
if self.point_on_segment(pt, (p3, p4)):
|
||||
return pt
|
||||
for pt in (p3, p4):
|
||||
if self.point_on_segment(pt, (p1, p2)):
|
||||
return pt
|
||||
return None
|
||||
|
||||
# Parametrisierte Schnittpunktberechnung
|
||||
denom = (p1.x - p2.x)*(p3.y - p4.y) - (p1.y - p2.y)*(p3.x - p4.x)
|
||||
t = ((p1.x - p3.x)*(p3.y - p4.y) - (p1.y - p3.y)*(p3.x - p4.x)) / denom
|
||||
u = -((p1.x - p2.x)*(p1.y - p3.y) - (p1.y - p2.y)*(p1.x - p3.x)) / denom
|
||||
|
||||
# Schnittpunkt auf den Segmenten ± Toleranz
|
||||
if -self.tolerance <= t <= 1 + self.tolerance and -self.tolerance <= u <= 1 + self.tolerance:
|
||||
x = p1.x + t * d1.x
|
||||
y = p1.y + t * d1.y
|
||||
intersection = Vec3(x, y, 0)
|
||||
|
||||
dist1 = self.distance_point_to_segment(intersection, (p1, p2))
|
||||
dist2 = self.distance_point_to_segment(intersection, (p3, p4))
|
||||
if dist1 <= self.tolerance and dist2 <= self.tolerance:
|
||||
return intersection
|
||||
|
||||
# Prüfen auf T-Verbindung: Endpunkt von line1 nahe line2
|
||||
for pt in (p1, p2):
|
||||
dist = self.distance_point_to_segment(pt, (p3, p4))
|
||||
if 0 < dist <= self.tolerance:
|
||||
return pt
|
||||
|
||||
# Prüfen auf T-Verbindung: Endpunkt von line2 nahe line1
|
||||
for pt in (p3, p4):
|
||||
dist = self.distance_point_to_segment(pt, (p1, p2))
|
||||
if 0 < dist <= self.tolerance:
|
||||
return pt
|
||||
|
||||
# Keine Kreuzung oder T-Verbindung
|
||||
return None
|
||||
|
||||
def distance_point_to_segment(self, pt, segment):
|
||||
"""Abstand eines Punktes zu einem Liniensegment"""
|
||||
start, end = segment
|
||||
seg_vec = end - start
|
||||
seg_len_sq = seg_vec.x**2 + seg_vec.y**2
|
||||
if seg_len_sq < 1e-12:
|
||||
return pt.distance(start)
|
||||
t = max(0, min(1, ((pt - start).dot(seg_vec)) / seg_len_sq))
|
||||
proj = start + seg_vec * t
|
||||
return pt.distance(proj)
|
||||
|
||||
|
||||
def point_on_segment(self, pt, segment):
|
||||
ax, ay = segment[0].x, segment[0].y
|
||||
bx, by = segment[1].x, segment[1].y
|
||||
px, py = pt.x, pt.y
|
||||
|
||||
# Richtungsvektoren
|
||||
vx, vy = bx - ax, by - ay
|
||||
wx, wy = px - ax, py - ay
|
||||
|
||||
# Kreuzprodukt zur Kollinearitätsprüfung
|
||||
cross = vx * wy - vy * wx
|
||||
if abs(cross) > self.tolerance:
|
||||
return False
|
||||
|
||||
# Skalarprodukt für Projektion
|
||||
dot = wx * vx + wy * vy
|
||||
if dot < -self.tolerance:
|
||||
return False
|
||||
|
||||
# Länge des Segments im Quadrat
|
||||
seg_len_sq = vx * vx + vy * vy
|
||||
|
||||
if dot > seg_len_sq + self.tolerance:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def is_endpoint(self, point, line):
|
||||
start, end = line[0], line[1]
|
||||
return (start.distance(point) <= self.tolerance or end.distance(point) <= self.tolerance)
|
||||
|
||||
|
||||
def classify_intersection(self, point, line1, line2):
|
||||
"""
|
||||
Klassifiziert den Schnittpunkt
|
||||
|
||||
Returns:
|
||||
'Kreuzung', 'T-Verbindung' oder 'Eckverbindung'
|
||||
"""
|
||||
line1_ends = self.is_endpoint(point, line1)
|
||||
line2_ends = self.is_endpoint(point, line2)
|
||||
|
||||
if line1_ends and line2_ends:
|
||||
return 'Eckverbindung'
|
||||
elif line1_ends or line2_ends:
|
||||
return 'T-Verbindung'
|
||||
else:
|
||||
return 'Kreuzung'
|
||||
|
||||
def find_intersections(self):
|
||||
"""Findet und klassifiziert alle Schnittpunkte"""
|
||||
print("\n=== ANALYSIERE SCHNITTPUNKTE ===\n")
|
||||
|
||||
processed_pairs = set()
|
||||
|
||||
for i, line1 in enumerate(self.lines):
|
||||
for j, line2 in enumerate(self.lines):
|
||||
if i >= j:
|
||||
continue
|
||||
|
||||
pair = (i, j)
|
||||
if pair in processed_pairs:
|
||||
continue
|
||||
|
||||
# Prüfe ob Endpunkte übereinstimmen (mit Toleranz)
|
||||
l1_start, l1_end = line1[0], line1[1]
|
||||
l2_start, l2_end = line2[0], line2[1]
|
||||
|
||||
# Finde übereinstimmende Endpunkte
|
||||
matches = []
|
||||
dist_ss = l1_start.distance(l2_start)
|
||||
dist_se = l1_start.distance(l2_end)
|
||||
dist_es = l1_end.distance(l2_start)
|
||||
dist_ee = l1_end.distance(l2_end)
|
||||
|
||||
if dist_ss < self.tolerance:
|
||||
matches.append(('start', 'start', l1_start))
|
||||
if dist_se < self.tolerance:
|
||||
matches.append(('start', 'end', l1_start))
|
||||
if dist_es < self.tolerance:
|
||||
matches.append(('end', 'start', l1_end))
|
||||
if dist_ee < self.tolerance:
|
||||
matches.append(('end', 'end', l1_end))
|
||||
|
||||
# Debug: Zeige nahe Punkte, die knapp außerhalb der Toleranz liegen
|
||||
min_dist = min(dist_ss, dist_se, dist_es, dist_ee)
|
||||
#if self.tolerance <= min_dist < self.tolerance * 10:
|
||||
#print(f"FAST-MATCH Linie {i}-{j}: min_dist={min_dist:.6f} (Toleranz={self.tolerance})")
|
||||
|
||||
# Wenn Endpunkte übereinstimmen, ist es eine Eckverbindung
|
||||
if matches:
|
||||
for match_type, _, point in matches:
|
||||
self.intersections['Eckverbindung'].append(point)
|
||||
processed_pairs.add(pair)
|
||||
#print(f"DEBUG: Eckverbindung Linie {i}-{j} bei ({point.x:.3f}, {point.y:.3f})")
|
||||
continue
|
||||
|
||||
# Berechne geometrischen Schnittpunkt
|
||||
point = self.calculate_intersection(line1, line2)
|
||||
|
||||
if point is not None:
|
||||
intersection_type = self.classify_intersection(point, line1, line2)
|
||||
self.intersections[intersection_type].append(point)
|
||||
processed_pairs.add(pair)
|
||||
|
||||
# Erweiterte Debug-Info
|
||||
l1_ends = self.is_endpoint(point, line1)
|
||||
l2_ends = self.is_endpoint(point, line2)
|
||||
#print(f"DEBUG: {intersection_type} Linie {i}-{j} bei ({point.x:.3f}, {point.y:.3f}) [L1_end={l1_ends}, L2_end={l2_ends}]")
|
||||
|
||||
total = sum(len(v) for v in self.intersections.values())
|
||||
print(f"\n=== ZUSAMMENFASSUNG ===")
|
||||
print(f"Gefundene Schnittpunkte: {total}")
|
||||
for itype, points in self.intersections.items():
|
||||
print(f" {itype}: {len(points)}")
|
||||
|
||||
def create_marking_layers(self):
|
||||
"""Erstellt Layer für die Markierungen"""
|
||||
layer_names = {
|
||||
'Kreuzung': 'D-KREUZUNG',
|
||||
'T-Verbindung': 'D-T_VERBINDUNG',
|
||||
'Eckverbindung': 'D-ECKVERBINDUNG'
|
||||
}
|
||||
|
||||
colors = {
|
||||
'Kreuzung': 1, # Rot
|
||||
'T-Verbindung': 3, # Grün
|
||||
'Eckverbindung': 5 # Blau
|
||||
}
|
||||
|
||||
for itype, layer_name in layer_names.items():
|
||||
if layer_name not in self.doc.layers:
|
||||
self.doc.layers.add(
|
||||
layer_name,
|
||||
color=colors[itype]
|
||||
)
|
||||
|
||||
return layer_names
|
||||
|
||||
|
||||
|
||||
def add_marking_circles(self, layer_names):
|
||||
"""Fügt Markierungskreise an allen Schnittpunkten hinzu"""
|
||||
msp = self.doc.modelspace()
|
||||
radius = self.mark_radius
|
||||
|
||||
for itype, points in self.intersections.items():
|
||||
layer_name = layer_names[itype]
|
||||
|
||||
for point in points:
|
||||
msp.add_circle(
|
||||
center=(point.x, point.y),
|
||||
radius=radius,
|
||||
dxfattribs={'layer': layer_name}
|
||||
)
|
||||
|
||||
print("\nMarkierungskreise hinzugefügt")
|
||||
|
||||
def save_results(self, output_dxf, output_txt):
|
||||
"""Speichert die Ergebnisse mit Fehlerhandling, falls die Dateien blockiert sind."""
|
||||
from pathlib import Path
|
||||
print(f"\nSpeichern in {output_dxf}...")
|
||||
# DXF speichern
|
||||
try:
|
||||
self.doc.saveas(output_dxf)
|
||||
print(f"\nMarkierte DXF-Datei gespeichert: {output_dxf}")
|
||||
except PermissionError:
|
||||
print("\nFEHLER: Die DXF-Datei konnte nicht gespeichert werden.")
|
||||
print(f"Grund: '{Path(output_dxf).name}' ist vermutlich noch geöffnet.")
|
||||
print("Bitte schließen und speichern erneut ausführen.\n")
|
||||
return False
|
||||
except Exception as e:
|
||||
print("\nUnerwarteter Fehler beim Speichern der DXF:")
|
||||
print(e)
|
||||
return False
|
||||
|
||||
# TXT speichern
|
||||
try:
|
||||
dt = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
|
||||
with open(output_txt, 'w', encoding='utf-8') as f:
|
||||
f.write("=" * 60 + "\n")
|
||||
f.write(f"DXF SCHNITTPUNKT-ANALYSE ERGEBNIS - {dt} \n")
|
||||
f.write("=" * 60 + "\n\n")
|
||||
|
||||
f.write(f"Eingabedatei: {self.dxf_path}\n")
|
||||
f.write(f"Analysierte Layer: {', '.join(self.layer_names)}\n")
|
||||
f.write(f"Anzahl analysierter Linien: {len(self.lines)}\n\n")
|
||||
|
||||
f.write("-" * 60 + "\n")
|
||||
f.write("SCHNITTPUNKT-STATISTIK\n")
|
||||
f.write("-" * 60 + "\n\n")
|
||||
|
||||
total = sum(len(v) for v in self.intersections.values())
|
||||
|
||||
for itype in ['Kreuzung', 'T-Verbindung', 'Eckverbindung']:
|
||||
count = len(self.intersections[itype])
|
||||
percentage = (count / total * 100) if total > 0 else 0
|
||||
f.write(f"{itype:20s}: {count:5d} ({percentage:5.1f}%)\n")
|
||||
|
||||
f.write(f"\n{'GESAMT':20s}: {total:5d}\n\n")
|
||||
|
||||
f.write("-" * 60 + "\n")
|
||||
f.write("DETAILS DER SCHNITTPUNKTE\n")
|
||||
f.write("-" * 60 + "\n\n")
|
||||
|
||||
for itype in ['Kreuzung', 'T-Verbindung', 'Eckverbindung']:
|
||||
points = self.intersections[itype]
|
||||
f.write(f"\n{itype} ({len(points)} Stück):\n")
|
||||
f.write("-" * 40 + "\n")
|
||||
|
||||
for idx, point in enumerate(points, 1):
|
||||
f.write(f" {idx:3d}. X={point.x:10.3f}, Y={point.y:10.3f}\n")
|
||||
|
||||
print(f"Ergebnisdatei gespeichert: {output_txt}")
|
||||
|
||||
except PermissionError:
|
||||
print("\nFEHLER: Die Analyse-Textdatei konnte nicht gespeichert werden.")
|
||||
print(f"Grund: '{Path(output_txt).name}' ist vermutlich noch geöffnet.")
|
||||
print("Bitte schließen und speichern erneut ausführen.\n")
|
||||
return False
|
||||
except Exception as e:
|
||||
print("\nUnerwarteter Fehler beim Speichern der Textdatei:")
|
||||
print(e)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def analyze(self, output_dxf=None, output_txt=None):
|
||||
"""Führt die komplette Analyse durch"""
|
||||
print("\n" + "=" * 60)
|
||||
print("DXF SCHNITTPUNKT-ANALYSE")
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
if not self.load_dxf():
|
||||
return False
|
||||
|
||||
self.extract_lines()
|
||||
|
||||
if len(self.lines) == 0:
|
||||
print("Keine Linien auf den angegebenen Layern gefunden!")
|
||||
return False
|
||||
|
||||
self.find_intersections()
|
||||
|
||||
layer_names = self.create_marking_layers()
|
||||
self.add_marking_circles(layer_names)
|
||||
|
||||
# Standardausgabedateinamen generieren
|
||||
if output_dxf is None:
|
||||
base = Path(self.dxf_path).stem
|
||||
output_dxf = f"{base}_markiert.dxf"
|
||||
|
||||
if output_txt is None:
|
||||
base = Path(self.dxf_path).stem
|
||||
output_txt = f"{base}_analyse.txt"
|
||||
|
||||
# Speichern und Fehler überprüfen
|
||||
ok = self.save_results(output_dxf, output_txt)
|
||||
if not ok:
|
||||
print("\nAnalyse abgebrochen wegen Speicherfehler.\n")
|
||||
return False
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("ANALYSE ERFOLGREICH ABGESCHLOSSEN")
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
@echo off
|
||||
REM Basisverzeichnis bestimmen
|
||||
pushd %~dp0\
|
||||
|
||||
|
||||
set PROJECT=%cd%
|
||||
set SCRIPT_DIR=%PROJECT%\lib
|
||||
set INPUT_DIR=%PROJECT%\input
|
||||
set OUTPUT_DIR=%PROJECT%\output
|
||||
|
||||
if not exist "%SCRIPT_DIR%" mkdir "%SCRIPT_DIR%"
|
||||
if not exist "%INPUT_DIR%" mkdir "%INPUT_DIR%"
|
||||
if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%"
|
||||
|
||||
REM Defaults aus cfg-Datei laden
|
||||
if exist defaults.cfg (
|
||||
for /f "usebackq tokens=1,2 delims==" %%A in ("defaults.cfg") do (
|
||||
set %%A=%%B
|
||||
)
|
||||
)
|
||||
|
||||
set PATH=%SCRIPT_DIR%;%PATH%
|
||||
|
||||
popd
|
||||
goto :eof
|
||||
@@ -0,0 +1,39 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM Umgebungsvariablen laden – immer relativ zum Batch-Datei-Verzeichnis
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
REM Kommandozeilenparameter prüfen und Defaults verwenden, falls Parameter fehlen
|
||||
if not "%~2"=="" (
|
||||
set LAYERS=%~2
|
||||
) else if not defined LAYERS (
|
||||
set LAYERS=D-1,D-5
|
||||
)
|
||||
|
||||
if not "%~3"=="" (
|
||||
set TOLERANCE=%~3
|
||||
) else if not defined TOLERANCE (
|
||||
set TOLERANCE=0.05
|
||||
)
|
||||
|
||||
if not "%~4"=="" (
|
||||
set MARK_RADIUS=%~4
|
||||
) else if not defined MARK_RADIUS (
|
||||
set MARK_RADIUS=200
|
||||
)
|
||||
|
||||
REM Analyse starten
|
||||
if "%~1"=="" (
|
||||
echo Keine Datei angegeben. Suche in input\ ...
|
||||
for %%f in ("%INPUT_DIR%\*.dxf") do (
|
||||
echo Starte Analyse fuer %%f mit Layer=!LAYERS!, Tolerance=!TOLERANCE!, Mark Radius=!MARK_RADIUS!
|
||||
python "%SCRIPT_DIR%\analyze.py" "%%f" "!LAYERS!" "!TOLERANCE!" "!MARK_RADIUS!"
|
||||
)
|
||||
) else (
|
||||
echo Starte Analyse fuer %~1 mit Layer=%LAYERS%, Tolerance=%TOLERANCE%, Mark Radius=%MARK_RADIUS%
|
||||
python "%SCRIPT_DIR%\analyze.py" "%~1" "%LAYERS%" "%TOLERANCE%" "%MARK_RADIUS%"
|
||||
)
|
||||
|
||||
echo Fertig.
|
||||
pause
|
||||
Reference in New Issue
Block a user