K2 der Bögen wird erzeugt
This commit is contained in:
+20
-173
@@ -30,8 +30,11 @@ import os
|
||||
import sys
|
||||
|
||||
import ezdxf
|
||||
from ezdxf import bbox as ezdxf_bbox
|
||||
from ezdxf.addons.importer import Importer
|
||||
|
||||
from utils import (
|
||||
ROW_GROUPS, TEXT_HEIGHT, TEXT_MARGIN, CROSS_SIZE, ROW_LABEL_WIDTH,
|
||||
build_row_layout, import_element_as_block, draw_cross,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -333,195 +336,43 @@ def process_dxf(data_dir, label, json_file, filter_func, insertion_func,
|
||||
# show-omniflo
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def find_min_y_point(doc):
|
||||
"""Findet den Punkt mit dem geringsten Y-Wert im Modelspace."""
|
||||
min_y = float('inf')
|
||||
min_point = None
|
||||
for e in doc.modelspace():
|
||||
points = []
|
||||
if e.dxftype() == 'LINE':
|
||||
points = [(e.dxf.start[0], e.dxf.start[1]),
|
||||
(e.dxf.end[0], e.dxf.end[1])]
|
||||
elif e.dxftype() == 'ARC':
|
||||
cx, cy = e.dxf.center[0], e.dxf.center[1]
|
||||
r = e.dxf.radius
|
||||
a_s = math.radians(e.dxf.start_angle)
|
||||
a_e = math.radians(e.dxf.end_angle)
|
||||
points = [
|
||||
(cx + r * math.cos(a_s), cy + r * math.sin(a_s)),
|
||||
(cx + r * math.cos(a_e), cy + r * math.sin(a_e)),
|
||||
]
|
||||
for p in points:
|
||||
if p[1] < min_y:
|
||||
min_y = p[1]
|
||||
min_point = p
|
||||
return min_point
|
||||
|
||||
|
||||
ROW_GROUPS = [
|
||||
("Boegen 22.5", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 22.5),
|
||||
("Boegen 45", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 45),
|
||||
("Boegen 67.5", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 67.5),
|
||||
("Boegen 90", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 90),
|
||||
("Boegen 180", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 180),
|
||||
("Weichenkoerper Einzel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 22.5),
|
||||
("Weichenkoerper Doppel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 22.5),
|
||||
("Weichenkoerper Dreiwege", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 22.5),
|
||||
("Einzelweiche 45", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 45),
|
||||
("Einzelweiche 90", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 90),
|
||||
("Einzelweiche Parallel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 0),
|
||||
("Doppelweiche 45", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 45),
|
||||
("Doppelweiche 90", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 90),
|
||||
("Doppelweiche Parallel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 0),
|
||||
("Dreiwegeweiche 45", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 45),
|
||||
("Dreiwegeweiche 90", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 90),
|
||||
("Dreiwegeweiche Parallel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 0),
|
||||
("Dreifachweiche", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreifachweiche"),
|
||||
("Deltaweiche", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Deltaweiche"),
|
||||
("Sternweiche", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Sternweiche"),
|
||||
]
|
||||
|
||||
PADDING_X = 200
|
||||
PADDING_Y = 400
|
||||
TEXT_HEIGHT = 30
|
||||
CROSS_SIZE = 40
|
||||
TEXT_MARGIN = 20
|
||||
ROW_LABEL_WIDTH = 600
|
||||
|
||||
|
||||
def show_omniflo(data_dir, results_dir):
|
||||
"""Erzeugt eine Uebersichts-DXF mit allen Omniflo-Elementen in Reihen."""
|
||||
boegen_path = os.path.join(data_dir, "json", "omniflo_boegen.json")
|
||||
weichen_path = os.path.join(data_dir, "json", "omniflo_weichen.json")
|
||||
|
||||
with open(boegen_path, "r", encoding="utf-8") as f:
|
||||
boegen_data = json.load(f)
|
||||
with open(weichen_path, "r", encoding="utf-8") as f:
|
||||
weichen_data = json.load(f)
|
||||
|
||||
sources = {"boegen": boegen_data, "weichen": weichen_data}
|
||||
omniflo_dir = os.path.join(data_dir, "omniflo")
|
||||
sources, rows = build_row_layout(data_dir, results_dir)
|
||||
|
||||
target = ezdxf.new(dxfversion='R2010')
|
||||
target_msp = target.modelspace()
|
||||
target.layers.add('ANNOTATION', color=7)
|
||||
target.layers.add('INSPOINT', color=1)
|
||||
target.layers.add('INSLINE', color=5)
|
||||
target.layers.add('ROW_LABEL', color=3)
|
||||
|
||||
cursor_y = 0.0
|
||||
block_counter = 0
|
||||
|
||||
for label, source_key, filter_func in ROW_GROUPS:
|
||||
items = [item for item in sources[source_key] if filter_func(item)]
|
||||
if not items:
|
||||
continue
|
||||
|
||||
row_elements = []
|
||||
for item in items:
|
||||
sivasnr = str(item["Sivasnr"])
|
||||
dxf_path_result = os.path.join(results_dir, f"{sivasnr}.dxf")
|
||||
dxf_path_orig = os.path.join(omniflo_dir, f"{sivasnr}.dxf")
|
||||
dxf_path = dxf_path_result if os.path.exists(dxf_path_result) else dxf_path_orig
|
||||
|
||||
if not os.path.exists(dxf_path):
|
||||
print(f" WARNUNG: {sivasnr}.dxf nicht gefunden, ueberspringe.")
|
||||
continue
|
||||
|
||||
source_doc = ezdxf.readfile(dxf_path)
|
||||
bb = ezdxf_bbox.extents(source_doc.modelspace())
|
||||
if not bb.has_data:
|
||||
continue
|
||||
|
||||
min_y_pt = find_min_y_point(source_doc)
|
||||
insbase = source_doc.header.get("$INSBASE", (0, 0, 0))
|
||||
|
||||
row_elements.append({
|
||||
'sivasnr': sivasnr,
|
||||
'source': source_doc,
|
||||
'extmin': bb.extmin,
|
||||
'extmax': bb.extmax,
|
||||
'width': bb.extmax[0] - bb.extmin[0],
|
||||
'height': bb.extmax[1] - bb.extmin[1],
|
||||
'insbase': insbase,
|
||||
'min_y_point': min_y_pt,
|
||||
})
|
||||
|
||||
if not row_elements:
|
||||
continue
|
||||
|
||||
max_height = max(e['height'] for e in row_elements)
|
||||
|
||||
label_y = cursor_y + max_height / 2
|
||||
for row in rows:
|
||||
label_y = row['cursor_y'] + row['max_height'] / 2
|
||||
target_msp.add_mtext(
|
||||
label,
|
||||
row['label'],
|
||||
dxfattribs={
|
||||
'layer': 'ROW_LABEL',
|
||||
'char_height': TEXT_HEIGHT * 1.2,
|
||||
}
|
||||
).set_location(insert=(-ROW_LABEL_WIDTH, label_y))
|
||||
|
||||
cursor_x = 0.0
|
||||
for elem in row_elements:
|
||||
for elem in row['elements']:
|
||||
block_name = f"BLK_{block_counter}"
|
||||
block_counter += 1
|
||||
|
||||
importer = Importer(elem['source'], target)
|
||||
importer.import_tables()
|
||||
blk = target.blocks.new(name=block_name)
|
||||
for entity in elem['source'].modelspace():
|
||||
importer.import_entity(entity, blk)
|
||||
importer.finalize()
|
||||
import_element_as_block(elem['source'], target, block_name)
|
||||
target_msp.add_blockref(block_name,
|
||||
insert=(elem['offset_x'], elem['offset_y']))
|
||||
|
||||
offset_x = cursor_x - elem['extmin'][0]
|
||||
offset_y = cursor_y - elem['extmin'][1]
|
||||
target_msp.add_blockref(block_name, insert=(offset_x, offset_y))
|
||||
insbase = elem['source'].header.get("$INSBASE", (0, 0, 0))
|
||||
ipx = insbase[0] + elem['offset_x']
|
||||
ipy = insbase[1] + elem['offset_y']
|
||||
draw_cross(target_msp, ipx, ipy, CROSS_SIZE, 1, 'INSPOINT')
|
||||
|
||||
ipx = elem['insbase'][0] + offset_x
|
||||
ipy = elem['insbase'][1] + offset_y
|
||||
|
||||
target_msp.add_line(
|
||||
(ipx - CROSS_SIZE, ipy),
|
||||
(ipx + CROSS_SIZE, ipy),
|
||||
dxfattribs={'layer': 'INSPOINT', 'color': 1}
|
||||
)
|
||||
target_msp.add_line(
|
||||
(ipx, ipy - CROSS_SIZE),
|
||||
(ipx, ipy + CROSS_SIZE),
|
||||
dxfattribs={'layer': 'INSPOINT', 'color': 1}
|
||||
)
|
||||
|
||||
if elem['min_y_point']:
|
||||
low_x = elem['min_y_point'][0] + offset_x
|
||||
low_y = elem['min_y_point'][1] + offset_y
|
||||
target_msp.add_line(
|
||||
(ipx, ipy),
|
||||
(low_x, low_y),
|
||||
dxfattribs={'layer': 'INSLINE', 'color': 5}
|
||||
)
|
||||
|
||||
text_x = cursor_x
|
||||
text_y = cursor_y + elem['height'] + TEXT_MARGIN
|
||||
text_x = elem['offset_x'] + elem['extmin'][0]
|
||||
text_y = row['cursor_y'] + elem['height'] + TEXT_MARGIN
|
||||
target_msp.add_mtext(
|
||||
elem['sivasnr'],
|
||||
dxfattribs={
|
||||
@@ -530,14 +381,10 @@ def show_omniflo(data_dir, results_dir):
|
||||
}
|
||||
).set_location(insert=(text_x, text_y))
|
||||
|
||||
cursor_x += elem['width'] + PADDING_X
|
||||
|
||||
cursor_y -= max_height + TEXT_HEIGHT + TEXT_MARGIN * 2 + PADDING_Y
|
||||
|
||||
out_path = os.path.join(results_dir, "omniflo_uebersicht.dxf")
|
||||
target.saveas(out_path)
|
||||
print(f"Uebersicht gespeichert: {out_path}")
|
||||
print(f" {block_counter} Elemente in {sum(1 for l, s, f in ROW_GROUPS if any(f(i) for i in sources[s]))} Reihen")
|
||||
print(f" {block_counter} Elemente in {len(rows)} Reihen")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
+276
-37
@@ -1,20 +1,42 @@
|
||||
"""
|
||||
Erzeugt benannte Koordinatensystem-Bloecke in DXF-Dateien.
|
||||
Setzt Koordinatensystem-Bloecke (K1, K2, ...) in Omniflo DXF-Dateien.
|
||||
|
||||
Jeder Block enthaelt drei Linien vom Ursprung:
|
||||
- rot(1) X-Achse, Laenge 1
|
||||
- gruen(3) Y-Achse, Laenge 2
|
||||
- blau(5) Z-Achse, Laenge 3
|
||||
|
||||
Die 3D-Rotation erfolgt ueber die Transformation des INSERT-Entities.
|
||||
Schalter:
|
||||
--k1set K1 an alle Boegen und Weichen setzen.
|
||||
Boegen: Beginn der obersten horizontalen Linie,
|
||||
x rechts, y oben, z aus Zeichenebene.
|
||||
Weichen: Beginn der untersten vertikalen Linie,
|
||||
x oben, y rechts, z aus Zeichenebene.
|
||||
--show-omniflo Uebersichts-DXF mit K-Positionen als farbige Kreuze.
|
||||
--test Erzeugt Testdatei mit 4 KOS und verifiziert diese.
|
||||
--number SIVA Nur diese eine 9-stellige Sivasnr verarbeiten.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ezdxf
|
||||
from ezdxf.math import Matrix44
|
||||
|
||||
from utils import (
|
||||
ROW_GROUPS, TEXT_HEIGHT, TEXT_MARGIN, CROSS_SIZE, ROW_LABEL_WIDTH,
|
||||
load_omniflo_data, build_row_layout, import_element_as_block,
|
||||
draw_cross, is_bogen,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Block-Definition
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Block-Linien: (color, dx, dy, dz)
|
||||
KS_LINES = [
|
||||
(1, 1.0, 0.0, 0.0), # rot, X-Achse, Laenge 1
|
||||
(3, 0.0, 2.0, 0.0), # gruen, Y-Achse, Laenge 2
|
||||
@@ -41,6 +63,10 @@ def _ensure_layer(doc, layer_name="_KOORDINATENSYSTEME"):
|
||||
doc.layers.add(layer_name)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# insert / read / verify
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def insert_ks(msp, name, point, rx=0, ry=0, rz=0):
|
||||
"""
|
||||
Erzeugt ein benanntes Koordinatensystem als Block-Referenz.
|
||||
@@ -66,7 +92,6 @@ def insert_ks(msp, name, point, rx=0, ry=0, rz=0):
|
||||
dxfattribs={"layer": "_KOORDINATENSYSTEME"},
|
||||
)
|
||||
|
||||
# 3D-Rotation via Transformationsmatrix
|
||||
m = Matrix44.chain(
|
||||
Matrix44.translate(-point[0], -point[1], -point[2]),
|
||||
Matrix44.x_rotate(math.radians(rx)),
|
||||
@@ -83,28 +108,18 @@ def read_ks(doc, name):
|
||||
"""
|
||||
Liest ein Koordinatensystem aus einer DXF-Datei zurueck.
|
||||
|
||||
Bestimmt Position und Rotationswinkel (rx, ry, rz) aus den
|
||||
tatsaechlichen Linienendpunkten des aufgeloesten INSERT-Blocks.
|
||||
|
||||
Args:
|
||||
doc: ezdxf DXF-Dokument.
|
||||
name: Block-Name (z.B. "K1").
|
||||
|
||||
Returns:
|
||||
Dict mit 'name', 'point', 'rx', 'ry', 'rz' oder None.
|
||||
"""
|
||||
msp = doc.modelspace()
|
||||
for entity in msp:
|
||||
if entity.dxftype() == "INSERT" and entity.dxf.name == name:
|
||||
# Linien aus Block-Referenz aufloesen (virtuelle Entities)
|
||||
lines = [e for e in entity.virtual_entities() if e.dxftype() == "LINE"]
|
||||
if len(lines) != 3:
|
||||
return None
|
||||
|
||||
# Gemeinsamer Startpunkt = Einfuegepunkt
|
||||
point = (lines[0].dxf.start[0], lines[0].dxf.start[1], lines[0].dxf.start[2])
|
||||
|
||||
# Richtungsvektoren aus den Endpunkten extrahieren
|
||||
axes = {}
|
||||
for line in lines:
|
||||
end = line.dxf.end
|
||||
@@ -114,18 +129,10 @@ def read_ks(doc, name):
|
||||
length = (dx**2 + dy**2 + dz**2) ** 0.5
|
||||
axes[line.dxf.color] = (dx / length, dy / length, dz / length)
|
||||
|
||||
# X-Achse (color=1), Y-Achse (color=3), Z-Achse (color=5)
|
||||
x_axis = axes.get(1, (1, 0, 0))
|
||||
y_axis = axes.get(3, (0, 1, 0))
|
||||
z_axis = axes.get(5, (0, 0, 1))
|
||||
|
||||
# Rotationswinkel aus Rotationsmatrix R = Rz * Ry * Rx
|
||||
# Spalten von R: x_axis, y_axis, z_axis
|
||||
# R20 = -sin(ry) = x_axis[2]
|
||||
# R21 = cos(ry)*sin(rx) = y_axis[2]
|
||||
# R22 = cos(ry)*cos(rx) = z_axis[2]
|
||||
# R10 = sin(rz)*cos(ry) = x_axis[1]
|
||||
# R00 = cos(rz)*cos(ry) = x_axis[0]
|
||||
ry = math.asin(max(-1, min(1, -x_axis[2])))
|
||||
cos_ry = math.cos(ry)
|
||||
|
||||
@@ -133,7 +140,6 @@ def read_ks(doc, name):
|
||||
rx = math.atan2(y_axis[2] / cos_ry, z_axis[2] / cos_ry)
|
||||
rz = math.atan2(x_axis[1] / cos_ry, x_axis[0] / cos_ry)
|
||||
else:
|
||||
# Gimbal Lock: ry = +/-90, rx und rz nicht unabhaengig
|
||||
rz = 0
|
||||
rx = math.atan2(-z_axis[0], y_axis[0])
|
||||
|
||||
@@ -148,16 +154,7 @@ def read_ks(doc, name):
|
||||
|
||||
|
||||
def verify_ks(dxf_path, expected):
|
||||
"""
|
||||
Liest KS-Bloecke aus einer DXF-Datei und vergleicht mit Erwartungswerten.
|
||||
|
||||
Args:
|
||||
dxf_path: Pfad zur DXF-Datei.
|
||||
expected: Liste von Dicts mit 'name', 'point', 'rx', 'ry', 'rz'.
|
||||
|
||||
Returns:
|
||||
True wenn alle Werte uebereinstimmen (Toleranz 0.01).
|
||||
"""
|
||||
"""Liest KS-Bloecke und vergleicht mit Erwartungswerten (Toleranz 0.01)."""
|
||||
doc = ezdxf.readfile(dxf_path)
|
||||
all_ok = True
|
||||
tol = 0.01
|
||||
@@ -191,9 +188,197 @@ def verify_ks(dxf_path, expected):
|
||||
return all_ok
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
# ---------------------------------------------------------------------------
|
||||
# K1-Positionsbestimmung
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def find_topmost_horizontal_line(doc):
|
||||
"""Findet die am weitesten oben liegende horizontale Linie."""
|
||||
best = None
|
||||
best_y = -float('inf')
|
||||
for entity in doc.modelspace():
|
||||
if entity.dxftype() != "LINE":
|
||||
continue
|
||||
s, e = entity.dxf.start, entity.dxf.end
|
||||
if abs(e[1] - s[1]) > 0.01:
|
||||
continue
|
||||
if s[1] > best_y:
|
||||
best_y = s[1]
|
||||
best = entity
|
||||
return best
|
||||
|
||||
|
||||
def find_bottommost_vertical_line(doc):
|
||||
"""Findet die am weitesten unten liegende rein vertikale Linie."""
|
||||
best = None
|
||||
best_min_y = float('inf')
|
||||
for entity in doc.modelspace():
|
||||
if entity.dxftype() != "LINE":
|
||||
continue
|
||||
s, e = entity.dxf.start, entity.dxf.end
|
||||
if abs(e[0] - s[0]) > 0.01:
|
||||
continue
|
||||
min_y = min(s[1], e[1])
|
||||
if min_y < best_min_y:
|
||||
best_min_y = min_y
|
||||
best = entity
|
||||
return best
|
||||
|
||||
|
||||
def k1_point_bogen(doc):
|
||||
"""K1-Position fuer Boegen: Beginn der obersten horizontalen Linie.
|
||||
Rotation: x rechts, y oben, z aus Zeichenebene (Standard, rz=0).
|
||||
"""
|
||||
hline = find_topmost_horizontal_line(doc)
|
||||
if hline is None:
|
||||
return None
|
||||
s, e = hline.dxf.start, hline.dxf.end
|
||||
left_x = min(s[0], e[0])
|
||||
y = s[1]
|
||||
return (left_x, y, 0), 0, 0, 0
|
||||
|
||||
|
||||
def k1_point_weiche(doc):
|
||||
"""K1-Position fuer Weichen: Beginn der untersten vertikalen Linie.
|
||||
Rotation: x oben, y links, z aus Zeichenebene (rz=90).
|
||||
"""
|
||||
vline = find_bottommost_vertical_line(doc)
|
||||
if vline is None:
|
||||
return None
|
||||
s, e = vline.dxf.start, vline.dxf.end
|
||||
x = s[0]
|
||||
bottom_y = min(s[1], e[1])
|
||||
return (x, bottom_y, 0), 0, 0, 90
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# --k1set
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def process_k1set(data_dir, results_dir, number=None):
|
||||
"""Setzt K1-Block an alle Boegen und Weichen."""
|
||||
sources = load_omniflo_data(data_dir)
|
||||
omniflo_dir = os.path.join(data_dir, "omniflo")
|
||||
|
||||
all_items = []
|
||||
for b in sources["boegen"]:
|
||||
all_items.append((str(b["Sivasnr"]), b["ProfilTyp"], True))
|
||||
for w in sources["weichen"]:
|
||||
all_items.append((str(w["Sivasnr"]), w["ProfilTyp"], False))
|
||||
|
||||
if number:
|
||||
all_items = [(s, p, ib) for s, p, ib in all_items if s == str(number)]
|
||||
|
||||
if not all_items:
|
||||
print("Keine Elemente gefunden.")
|
||||
return
|
||||
|
||||
print("=== K1 setzen ===")
|
||||
for sivasnr, profil, is_bog in all_items:
|
||||
dxf_path = os.path.join(results_dir, f"{sivasnr}.dxf")
|
||||
if not os.path.exists(dxf_path):
|
||||
dxf_path = os.path.join(omniflo_dir, f"{sivasnr}.dxf")
|
||||
if not os.path.exists(dxf_path):
|
||||
continue
|
||||
|
||||
doc = ezdxf.readfile(dxf_path)
|
||||
|
||||
# Bestehende K1-Referenzen entfernen
|
||||
msp = doc.modelspace()
|
||||
for entity in list(msp):
|
||||
if entity.dxftype() == "INSERT" and entity.dxf.name == "K1":
|
||||
msp.delete_entity(entity)
|
||||
|
||||
if is_bog:
|
||||
result = k1_point_bogen(doc)
|
||||
else:
|
||||
result = k1_point_weiche(doc)
|
||||
|
||||
if result is None:
|
||||
print(f"WARNUNG: K1 in {sivasnr}.dxf nicht bestimmbar, ueberspringe.")
|
||||
continue
|
||||
|
||||
point, rx, ry, rz = result
|
||||
msp = doc.modelspace()
|
||||
insert_ks(msp, "K1", point, rx, ry, rz)
|
||||
|
||||
out_path = os.path.join(results_dir, f"{sivasnr}.dxf")
|
||||
doc.saveas(out_path)
|
||||
print(f"{sivasnr}: K1 at ({point[0]:.2f},{point[1]:.2f},{point[2]:.2f}) "
|
||||
f"rx={rx} ry={ry} rz={rz} [{profil}]")
|
||||
|
||||
print(f"\nErgebnisse in: {results_dir}")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# --show-omniflo
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Farben: K1=rot(1), K2=orange(30), K3=gelb(2), K4=gruen(3)
|
||||
K_COLORS = {"K1": 1, "K2": 30, "K3": 2, "K4": 3}
|
||||
|
||||
|
||||
def show_omniflo(data_dir, results_dir):
|
||||
"""Uebersichts-DXF mit K-Positionen als farbige Kreuze."""
|
||||
sources, rows = build_row_layout(data_dir, results_dir)
|
||||
|
||||
target = ezdxf.new(dxfversion='R2010')
|
||||
target_msp = target.modelspace()
|
||||
target.layers.add('ANNOTATION', color=7)
|
||||
target.layers.add('K_POINTS', color=1)
|
||||
target.layers.add('ROW_LABEL', color=3)
|
||||
|
||||
block_counter = 0
|
||||
|
||||
for row in rows:
|
||||
label_y = row['cursor_y'] + row['max_height'] / 2
|
||||
target_msp.add_mtext(
|
||||
row['label'],
|
||||
dxfattribs={
|
||||
'layer': 'ROW_LABEL',
|
||||
'char_height': TEXT_HEIGHT * 1.2,
|
||||
}
|
||||
).set_location(insert=(-ROW_LABEL_WIDTH, label_y))
|
||||
|
||||
for elem in row['elements']:
|
||||
block_name = f"BLK_{block_counter}"
|
||||
block_counter += 1
|
||||
|
||||
import_element_as_block(elem['source'], target, block_name)
|
||||
target_msp.add_blockref(block_name,
|
||||
insert=(elem['offset_x'], elem['offset_y']))
|
||||
|
||||
# K-Positionen aus dem Quell-DXF auslesen und anzeigen
|
||||
for k_name, k_color in K_COLORS.items():
|
||||
k_data = read_ks(elem['source'], k_name)
|
||||
if k_data is None:
|
||||
continue
|
||||
kx = k_data['point'][0] + elem['offset_x']
|
||||
ky = k_data['point'][1] + elem['offset_y']
|
||||
draw_cross(target_msp, kx, ky, CROSS_SIZE, k_color, 'K_POINTS')
|
||||
|
||||
text_x = elem['offset_x'] + elem['extmin'][0]
|
||||
text_y = row['cursor_y'] + elem['height'] + TEXT_MARGIN
|
||||
target_msp.add_mtext(
|
||||
elem['sivasnr'],
|
||||
dxfattribs={
|
||||
'layer': 'ANNOTATION',
|
||||
'char_height': TEXT_HEIGHT,
|
||||
}
|
||||
).set_location(insert=(text_x, text_y))
|
||||
|
||||
out_path = os.path.join(results_dir, "koords_uebersicht.dxf")
|
||||
target.saveas(out_path)
|
||||
print(f"Uebersicht gespeichert: {out_path}")
|
||||
print(f" {block_counter} Elemente in {len(rows)} Reihen")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# --test
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def run_test(results_dir):
|
||||
"""Erzeugt Testdatei mit 4 KOS und verifiziert Ruecklesen."""
|
||||
doc = ezdxf.new(dxfversion="R2010")
|
||||
msp = doc.modelspace()
|
||||
|
||||
@@ -207,8 +392,6 @@ if __name__ == "__main__":
|
||||
for tc in test_cases:
|
||||
insert_ks(msp, tc["name"], tc["point"], tc["rx"], tc["ry"], tc["rz"])
|
||||
|
||||
results_dir = os.environ.get("DXFM_RESULTS", "results")
|
||||
os.makedirs(results_dir, exist_ok=True)
|
||||
out_path = os.path.join(results_dir, "ks_test.dxf")
|
||||
doc.saveas(out_path)
|
||||
print(f"Testdatei gespeichert: {out_path}\n")
|
||||
@@ -216,3 +399,59 @@ if __name__ == "__main__":
|
||||
print("Verifikation:")
|
||||
ok = verify_ks(out_path, test_cases)
|
||||
print(f"\nErgebnis: {'ALLE OK' if ok else 'FEHLER GEFUNDEN'}")
|
||||
return ok
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Setzt Koordinatensystem-Bloecke in Omniflo DXF-Dateien"
|
||||
)
|
||||
parser.add_argument("--k1set", action="store_true",
|
||||
help="K1-Block an alle Boegen und Weichen setzen")
|
||||
parser.add_argument("--show-omniflo", action="store_true",
|
||||
help="Uebersichts-DXF mit K-Positionen erzeugen")
|
||||
parser.add_argument("--test", action="store_true",
|
||||
help="Testdatei mit 4 KOS erzeugen und verifizieren")
|
||||
parser.add_argument("--number", type=int,
|
||||
help="Nur diese 9-stellige Sivasnr verarbeiten")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.number and len(str(args.number)) != 9:
|
||||
print("FEHLER: --number muss eine 9-stellige Ganzzahl sein.")
|
||||
sys.exit(1)
|
||||
|
||||
if not args.k1set and not args.show_omniflo and not args.test:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
data_dir = os.environ.get("DXFM_DATA")
|
||||
results_dir = os.environ.get("DXFM_RESULTS")
|
||||
|
||||
if not data_dir or not results_dir:
|
||||
if args.test:
|
||||
results_dir = results_dir or "results"
|
||||
os.makedirs(results_dir, exist_ok=True)
|
||||
run_test(results_dir)
|
||||
return
|
||||
print("FEHLER: Umgebungsvariablen DXFM_DATA und DXFM_RESULTS muessen gesetzt sein.")
|
||||
sys.exit(1)
|
||||
|
||||
os.makedirs(results_dir, exist_ok=True)
|
||||
|
||||
if args.test:
|
||||
run_test(results_dir)
|
||||
|
||||
if args.k1set:
|
||||
process_k1set(data_dir, results_dir, args.number)
|
||||
|
||||
if args.show_omniflo:
|
||||
print("=== Koordinaten Uebersicht ===")
|
||||
show_omniflo(data_dir, results_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
Gemeinsame Hilfsfunktionen fuer DXF-Makros.
|
||||
|
||||
Enthaelt die Reihen-Gruppierung fuer Omniflo-Elemente und Layout-Funktionen
|
||||
fuer die Uebersichts-DXF Erzeugung.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import ezdxf
|
||||
from ezdxf import bbox as ezdxf_bbox
|
||||
from ezdxf.addons.importer import Importer
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reihen-Gruppen: (Label, Quell-Key, Filter-Funktion)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
ROW_GROUPS = [
|
||||
("Boegen 22.5", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 22.5),
|
||||
("Boegen 45", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 45),
|
||||
("Boegen 67.5", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 67.5),
|
||||
("Boegen 90", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 90),
|
||||
("Boegen 180", "boegen",
|
||||
lambda b: b["KurvenWinkel"] == 180),
|
||||
("Weichenkoerper Einzel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 22.5),
|
||||
("Weichenkoerper Doppel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 22.5),
|
||||
("Weichenkoerper Dreiwege", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 22.5),
|
||||
("Einzelweiche 45", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 45),
|
||||
("Einzelweiche 90", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 90),
|
||||
("Einzelweiche Parallel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 0),
|
||||
("Doppelweiche 45", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 45),
|
||||
("Doppelweiche 90", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 90),
|
||||
("Doppelweiche Parallel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 0),
|
||||
("Dreiwegeweiche 45", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 45),
|
||||
("Dreiwegeweiche 90", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 90),
|
||||
("Dreiwegeweiche Parallel", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 0),
|
||||
("Dreifachweiche", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Dreifachweiche"),
|
||||
("Deltaweiche", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Deltaweiche"),
|
||||
("Sternweiche", "weichen",
|
||||
lambda w: w["WeichenTyp"] == "Sternweiche"),
|
||||
]
|
||||
|
||||
PADDING_X = 200
|
||||
PADDING_Y = 400
|
||||
TEXT_HEIGHT = 30
|
||||
CROSS_SIZE = 40
|
||||
TEXT_MARGIN = 20
|
||||
ROW_LABEL_WIDTH = 600
|
||||
|
||||
|
||||
def load_omniflo_data(data_dir):
|
||||
"""Laedt Boegen- und Weichen-JSON und gibt ein Dict {boegen, weichen} zurueck."""
|
||||
boegen_path = os.path.join(data_dir, "json", "omniflo_boegen.json")
|
||||
weichen_path = os.path.join(data_dir, "json", "omniflo_weichen.json")
|
||||
|
||||
with open(boegen_path, "r", encoding="utf-8") as f:
|
||||
boegen_data = json.load(f)
|
||||
with open(weichen_path, "r", encoding="utf-8") as f:
|
||||
weichen_data = json.load(f)
|
||||
|
||||
return {"boegen": boegen_data, "weichen": weichen_data}
|
||||
|
||||
|
||||
def is_bogen(sivasnr, sources):
|
||||
"""Prueft ob eine Sivasnr ein Bogen ist."""
|
||||
return any(str(b["Sivasnr"]) == str(sivasnr) for b in sources["boegen"])
|
||||
|
||||
|
||||
def build_row_layout(data_dir, results_dir):
|
||||
"""
|
||||
Berechnet das Layout aller Omniflo-Elemente in Reihen.
|
||||
|
||||
Returns:
|
||||
(sources, rows) wobei rows eine Liste von Dicts ist:
|
||||
[{
|
||||
'label': str,
|
||||
'source_key': str,
|
||||
'elements': [{'sivasnr', 'source', 'extmin', 'extmax',
|
||||
'width', 'height', 'offset_x', 'offset_y'}],
|
||||
'cursor_y': float,
|
||||
'max_height': float,
|
||||
}]
|
||||
"""
|
||||
sources = load_omniflo_data(data_dir)
|
||||
omniflo_dir = os.path.join(data_dir, "omniflo")
|
||||
|
||||
rows = []
|
||||
cursor_y = 0.0
|
||||
|
||||
for label, source_key, filter_func in ROW_GROUPS:
|
||||
items = [item for item in sources[source_key] if filter_func(item)]
|
||||
if not items:
|
||||
continue
|
||||
|
||||
row_elements = []
|
||||
for item in items:
|
||||
sivasnr = str(item["Sivasnr"])
|
||||
dxf_path_result = os.path.join(results_dir, f"{sivasnr}.dxf")
|
||||
dxf_path_orig = os.path.join(omniflo_dir, f"{sivasnr}.dxf")
|
||||
dxf_path = dxf_path_result if os.path.exists(dxf_path_result) else dxf_path_orig
|
||||
|
||||
if not os.path.exists(dxf_path):
|
||||
continue
|
||||
|
||||
source_doc = ezdxf.readfile(dxf_path)
|
||||
bb = ezdxf_bbox.extents(source_doc.modelspace())
|
||||
if not bb.has_data:
|
||||
continue
|
||||
|
||||
row_elements.append({
|
||||
'sivasnr': sivasnr,
|
||||
'source': source_doc,
|
||||
'extmin': bb.extmin,
|
||||
'extmax': bb.extmax,
|
||||
'width': bb.extmax[0] - bb.extmin[0],
|
||||
'height': bb.extmax[1] - bb.extmin[1],
|
||||
})
|
||||
|
||||
if not row_elements:
|
||||
continue
|
||||
|
||||
max_height = max(e['height'] for e in row_elements)
|
||||
|
||||
cursor_x = 0.0
|
||||
for elem in row_elements:
|
||||
elem['offset_x'] = cursor_x - elem['extmin'][0]
|
||||
elem['offset_y'] = cursor_y - elem['extmin'][1]
|
||||
cursor_x += elem['width'] + PADDING_X
|
||||
|
||||
rows.append({
|
||||
'label': label,
|
||||
'source_key': source_key,
|
||||
'elements': row_elements,
|
||||
'cursor_y': cursor_y,
|
||||
'max_height': max_height,
|
||||
})
|
||||
|
||||
cursor_y -= max_height + TEXT_HEIGHT + TEXT_MARGIN * 2 + PADDING_Y
|
||||
|
||||
return sources, rows
|
||||
|
||||
|
||||
def import_element_as_block(source_doc, target_doc, block_name):
|
||||
"""Importiert alle Modelspace-Entities eines Quell-Dokuments als Block."""
|
||||
importer = Importer(source_doc, target_doc)
|
||||
importer.import_tables()
|
||||
blk = target_doc.blocks.new(name=block_name)
|
||||
for entity in source_doc.modelspace():
|
||||
importer.import_entity(entity, blk)
|
||||
importer.finalize()
|
||||
return block_name
|
||||
|
||||
|
||||
def draw_cross(msp, x, y, size, color, layer):
|
||||
"""Zeichnet ein Kreuz an (x, y)."""
|
||||
msp.add_line((x - size, y), (x + size, y),
|
||||
dxfattribs={'layer': layer, 'color': color})
|
||||
msp.add_line((x, y - size), (x, y + size),
|
||||
dxfattribs={'layer': layer, 'color': color})
|
||||
Reference in New Issue
Block a user