Kanten an Aufgaben rechtwinklig ansetzen (#23)
- orthogonal_points(): Ist nur ein Ende eine Raute, bekommt auch das Aufgaben-Ende einen geraden Stub senkrecht zu seiner Seite (RECHTECK_STUB = 20); danach wie zwischen zwei Rauten von Stub zu Stub. Die beiden Sonderzweige für "nur Quelle/nur Ziel ist Gateway" entfallen. - Beispiele neu erzeugt: 17 Kantenenden (2 + 2 + 13), die parallel zur Aufgabenkante ansetzten, sind korrigiert. - Strukturtest test_kanten_setzen_an_aufgaben_rechtwinklig_an über alle Beispiele und Einzeltest für vier Achsen-Kombinationen. - Belastungstest (126 Fälle): schräge Anschlüsse 378 -> 0; Kreuzungen 22 -> 24, alle in Fällen mit Querverbindung zwischen Zweigen (#22). - Tests: python -m pytest -q -> 124 bestanden, 1 übersprungen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -118,14 +118,16 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2">
|
||||
<di:waypoint x="400" y="360"/>
|
||||
<di:waypoint x="400" y="310"/>
|
||||
<di:waypoint x="420" y="360"/>
|
||||
<di:waypoint x="420" y="310"/>
|
||||
<di:waypoint x="515" y="310"/>
|
||||
<di:waypoint x="515" y="335"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_3_di" bpmnElement="Flow_3">
|
||||
<di:waypoint x="515" y="385"/>
|
||||
<di:waypoint x="515" y="410"/>
|
||||
<di:waypoint x="680" y="410"/>
|
||||
<di:waypoint x="660" y="410"/>
|
||||
<di:waypoint x="660" y="445"/>
|
||||
<di:waypoint x="680" y="445"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="523" y="389.5" width="30" height="14"/>
|
||||
@@ -135,6 +137,7 @@
|
||||
<di:waypoint x="540" y="360"/>
|
||||
<di:waypoint x="565" y="360"/>
|
||||
<di:waypoint x="565" y="275"/>
|
||||
<di:waypoint x="660" y="275"/>
|
||||
<di:waypoint x="680" y="275"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="537.5" y="366" width="30" height="14"/>
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<bpmndi:BPMNShape id="Gateway_S3_di" bpmnElement="Gateway_S3" isMarkerVisible="true">
|
||||
<dc:Bounds x="165" y="420" width="50" height="50"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="245" y="390.5" width="90" height="42"/>
|
||||
<dc:Bounds x="202.5" y="390.5" width="90" height="42"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="SendTask_S4_di" bpmnElement="SendTask_S4">
|
||||
@@ -102,7 +102,8 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2">
|
||||
<di:waypoint x="215" y="330"/>
|
||||
<di:waypoint x="215" y="395"/>
|
||||
<di:waypoint x="215" y="350"/>
|
||||
<di:waypoint x="190" y="350"/>
|
||||
<di:waypoint x="190" y="395"/>
|
||||
<di:waypoint x="190" y="420"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
@@ -110,6 +111,7 @@
|
||||
<di:waypoint x="190" y="470"/>
|
||||
<di:waypoint x="190" y="495"/>
|
||||
<di:waypoint x="300" y="495"/>
|
||||
<di:waypoint x="300" y="570"/>
|
||||
<di:waypoint x="300" y="590"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="198" y="474.5" width="30" height="14"/>
|
||||
@@ -118,7 +120,8 @@
|
||||
<bpmndi:BPMNEdge id="Flow_4_di" bpmnElement="Flow_4">
|
||||
<di:waypoint x="215" y="445"/>
|
||||
<di:waypoint x="240" y="445"/>
|
||||
<di:waypoint x="240" y="590"/>
|
||||
<di:waypoint x="240" y="570"/>
|
||||
<di:waypoint x="130" y="570"/>
|
||||
<di:waypoint x="130" y="590"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="212.5" y="451" width="30" height="14"/>
|
||||
@@ -132,7 +135,8 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_6_di" bpmnElement="Flow_6">
|
||||
<di:waypoint x="300" y="670"/>
|
||||
<di:waypoint x="300" y="735"/>
|
||||
<di:waypoint x="300" y="690"/>
|
||||
<di:waypoint x="190" y="690"/>
|
||||
<di:waypoint x="190" y="735"/>
|
||||
<di:waypoint x="190" y="760"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
@@ -140,6 +144,7 @@
|
||||
<di:waypoint x="190" y="810"/>
|
||||
<di:waypoint x="190" y="835"/>
|
||||
<di:waypoint x="130" y="835"/>
|
||||
<di:waypoint x="130" y="910"/>
|
||||
<di:waypoint x="130" y="930"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="198" y="814.5" width="30" height="14"/>
|
||||
@@ -148,7 +153,8 @@
|
||||
<bpmndi:BPMNEdge id="Flow_8_di" bpmnElement="Flow_8">
|
||||
<di:waypoint x="215" y="785"/>
|
||||
<di:waypoint x="240" y="785"/>
|
||||
<di:waypoint x="240" y="930"/>
|
||||
<di:waypoint x="240" y="910"/>
|
||||
<di:waypoint x="300" y="910"/>
|
||||
<di:waypoint x="300" y="930"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="212.5" y="791" width="30" height="14"/>
|
||||
|
||||
@@ -473,14 +473,16 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_5_di" bpmnElement="Flow_5">
|
||||
<di:waypoint x="970" y="170"/>
|
||||
<di:waypoint x="970" y="120"/>
|
||||
<di:waypoint x="990" y="170"/>
|
||||
<di:waypoint x="990" y="120"/>
|
||||
<di:waypoint x="1085" y="120"/>
|
||||
<di:waypoint x="1085" y="145"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_6_di" bpmnElement="Flow_6">
|
||||
<di:waypoint x="1085" y="195"/>
|
||||
<di:waypoint x="1085" y="220"/>
|
||||
<di:waypoint x="1250" y="220"/>
|
||||
<di:waypoint x="1230" y="220"/>
|
||||
<di:waypoint x="1230" y="170"/>
|
||||
<di:waypoint x="1250" y="170"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="1093" y="199.5" width="30" height="14"/>
|
||||
@@ -510,18 +512,21 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_10_di" bpmnElement="Flow_10">
|
||||
<di:waypoint x="1730" y="170"/>
|
||||
<di:waypoint x="1750" y="170"/>
|
||||
<di:waypoint x="1795" y="170"/>
|
||||
<di:waypoint x="1820" y="170"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_11_di" bpmnElement="Flow_11">
|
||||
<di:waypoint x="1845" y="195"/>
|
||||
<di:waypoint x="1845" y="220"/>
|
||||
<di:waypoint x="2010" y="220"/>
|
||||
<di:waypoint x="1990" y="220"/>
|
||||
<di:waypoint x="1990" y="170"/>
|
||||
<di:waypoint x="2010" y="170"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_12_di" bpmnElement="Flow_12">
|
||||
<di:waypoint x="2110" y="170"/>
|
||||
<di:waypoint x="2110" y="120"/>
|
||||
<di:waypoint x="2130" y="170"/>
|
||||
<di:waypoint x="2130" y="120"/>
|
||||
<di:waypoint x="2225" y="120"/>
|
||||
<di:waypoint x="2225" y="145"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
@@ -540,6 +545,7 @@
|
||||
<di:waypoint x="2250" y="170"/>
|
||||
<di:waypoint x="2275" y="170"/>
|
||||
<di:waypoint x="2275" y="255"/>
|
||||
<di:waypoint x="2370" y="255"/>
|
||||
<di:waypoint x="2390" y="255"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2247.5" y="176" width="30" height="14"/>
|
||||
@@ -548,7 +554,8 @@
|
||||
<bpmndi:BPMNEdge id="Flow_15_di" bpmnElement="Flow_15">
|
||||
<di:waypoint x="2415" y="110"/>
|
||||
<di:waypoint x="2415" y="135"/>
|
||||
<di:waypoint x="2580" y="135"/>
|
||||
<di:waypoint x="2560" y="135"/>
|
||||
<di:waypoint x="2560" y="85"/>
|
||||
<di:waypoint x="2580" y="85"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2423" y="114.5" width="30" height="14"/>
|
||||
@@ -566,18 +573,21 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_17_di" bpmnElement="Flow_17">
|
||||
<di:waypoint x="2680" y="85"/>
|
||||
<di:waypoint x="2700" y="85"/>
|
||||
<di:waypoint x="2745" y="85"/>
|
||||
<di:waypoint x="2770" y="85"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_18_di" bpmnElement="Flow_18">
|
||||
<di:waypoint x="2795" y="110"/>
|
||||
<di:waypoint x="2795" y="135"/>
|
||||
<di:waypoint x="2960" y="135"/>
|
||||
<di:waypoint x="2940" y="135"/>
|
||||
<di:waypoint x="2940" y="85"/>
|
||||
<di:waypoint x="2960" y="85"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_19_di" bpmnElement="Flow_19">
|
||||
<di:waypoint x="3060" y="85"/>
|
||||
<di:waypoint x="3060" y="35"/>
|
||||
<di:waypoint x="3080" y="85"/>
|
||||
<di:waypoint x="3080" y="35"/>
|
||||
<di:waypoint x="3175" y="35"/>
|
||||
<di:waypoint x="3175" y="60"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
@@ -605,7 +615,8 @@
|
||||
<bpmndi:BPMNEdge id="Flow_22_di" bpmnElement="Flow_22">
|
||||
<di:waypoint x="3365" y="110"/>
|
||||
<di:waypoint x="3365" y="135"/>
|
||||
<di:waypoint x="3530" y="135"/>
|
||||
<di:waypoint x="3510" y="135"/>
|
||||
<di:waypoint x="3510" y="625"/>
|
||||
<di:waypoint x="3530" y="625"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3373" y="114.5" width="144" height="14"/>
|
||||
@@ -615,6 +626,7 @@
|
||||
<di:waypoint x="3390" y="85"/>
|
||||
<di:waypoint x="3415" y="85"/>
|
||||
<di:waypoint x="3415" y="805"/>
|
||||
<di:waypoint x="3510" y="805"/>
|
||||
<di:waypoint x="3530" y="805"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3387.5" y="91" width="30" height="14"/>
|
||||
@@ -628,13 +640,15 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_25_di" bpmnElement="Flow_25">
|
||||
<di:waypoint x="3820" y="85"/>
|
||||
<di:waypoint x="3820" y="120"/>
|
||||
<di:waypoint x="3840" y="85"/>
|
||||
<di:waypoint x="3840" y="120"/>
|
||||
<di:waypoint x="3935" y="120"/>
|
||||
<di:waypoint x="3935" y="145"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_26_di" bpmnElement="Flow_26">
|
||||
<di:waypoint x="3630" y="805"/>
|
||||
<di:waypoint x="3885" y="805"/>
|
||||
<di:waypoint x="3650" y="805"/>
|
||||
<di:waypoint x="3650" y="170"/>
|
||||
<di:waypoint x="3885" y="170"/>
|
||||
<di:waypoint x="3910" y="170"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
@@ -649,7 +663,8 @@
|
||||
<bpmndi:BPMNEdge id="Flow_28_di" bpmnElement="Flow_28">
|
||||
<di:waypoint x="3365" y="280"/>
|
||||
<di:waypoint x="3365" y="305"/>
|
||||
<di:waypoint x="3530" y="305"/>
|
||||
<di:waypoint x="3510" y="305"/>
|
||||
<di:waypoint x="3510" y="975"/>
|
||||
<di:waypoint x="3530" y="975"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3373" y="284.5" width="30" height="14"/>
|
||||
@@ -659,6 +674,7 @@
|
||||
<di:waypoint x="3390" y="255"/>
|
||||
<di:waypoint x="3415" y="255"/>
|
||||
<di:waypoint x="3415" y="170"/>
|
||||
<di:waypoint x="3510" y="170"/>
|
||||
<di:waypoint x="3530" y="170"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="3387.5" y="261" width="132" height="14"/>
|
||||
@@ -666,13 +682,15 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_30_di" bpmnElement="Flow_30">
|
||||
<di:waypoint x="3630" y="975"/>
|
||||
<di:waypoint x="3630" y="205"/>
|
||||
<di:waypoint x="3650" y="975"/>
|
||||
<di:waypoint x="3650" y="205"/>
|
||||
<di:waypoint x="3745" y="205"/>
|
||||
<di:waypoint x="3745" y="230"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_31_di" bpmnElement="Flow_31">
|
||||
<di:waypoint x="3630" y="170"/>
|
||||
<di:waypoint x="3695" y="170"/>
|
||||
<di:waypoint x="3650" y="170"/>
|
||||
<di:waypoint x="3650" y="255"/>
|
||||
<di:waypoint x="3695" y="255"/>
|
||||
<di:waypoint x="3720" y="255"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
@@ -697,7 +715,8 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_35_di" bpmnElement="Flow_35">
|
||||
<di:waypoint x="2680" y="255"/>
|
||||
<di:waypoint x="2680" y="205"/>
|
||||
<di:waypoint x="2700" y="255"/>
|
||||
<di:waypoint x="2700" y="205"/>
|
||||
<di:waypoint x="2795" y="205"/>
|
||||
<di:waypoint x="2795" y="230"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
@@ -715,6 +734,7 @@
|
||||
<bpmndi:BPMNEdge id="Flow_37_di" bpmnElement="Flow_37">
|
||||
<di:waypoint x="2820" y="255"/>
|
||||
<di:waypoint x="2845" y="255"/>
|
||||
<di:waypoint x="2940" y="255"/>
|
||||
<di:waypoint x="2960" y="255"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="2817.5" y="261" width="30" height="14"/>
|
||||
@@ -722,6 +742,7 @@
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_38_di" bpmnElement="Flow_38">
|
||||
<di:waypoint x="3060" y="255"/>
|
||||
<di:waypoint x="3080" y="255"/>
|
||||
<di:waypoint x="3125" y="255"/>
|
||||
<di:waypoint x="3150" y="255"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
@@ -735,7 +756,8 @@
|
||||
<bpmndi:BPMNEdge id="Flow_40_di" bpmnElement="Flow_40">
|
||||
<di:waypoint x="4315" y="195"/>
|
||||
<di:waypoint x="4315" y="220"/>
|
||||
<di:waypoint x="4480" y="220"/>
|
||||
<di:waypoint x="4460" y="220"/>
|
||||
<di:waypoint x="4460" y="170"/>
|
||||
<di:waypoint x="4480" y="170"/>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_41_di" bpmnElement="Flow_41">
|
||||
|
||||
@@ -615,6 +615,9 @@ GATEWAY_OUTWARD = {'N': (0, -1), 'S': (0, 1), 'E': (1, 0), 'W': (-1, 0)}
|
||||
# Halbe Kantenlaenge der Raute (Standardgroesse 50x50) reicht, um deren Kontur
|
||||
# in jedem Fall sicher zu verlassen, bevor die Kante abbiegt.
|
||||
GATEWAY_STUB = 25
|
||||
# Gerades Stück an einer Aufgabe, wenn das andere Ende eine Raute ist (#23); passt
|
||||
# in den kleinsten Gang (70) zwischen zwei Spalten bzw. Zeilen, auch von beiden Seiten.
|
||||
RECHTECK_STUB = 20
|
||||
|
||||
|
||||
def _dedupe_consecutive(points):
|
||||
@@ -688,35 +691,30 @@ def orthogonal_points(ox, oy, src_side, tx, ty, tgt_side,
|
||||
dieses Schritts (die einzige garantiert freie Koordinate) beim nächsten
|
||||
Abbiegen zunächst unverändert bleibt. Sonst würde die Kante beim
|
||||
Umbiegen zum Ziel auf demselben Weg zurück durch die Raute laufen, den
|
||||
sie gerade erst verlassen hat."""
|
||||
sie gerade erst verlassen hat.
|
||||
|
||||
Ist nur ein Ende eine Raute, bekommt auch das andere Ende (Aufgabe) einen
|
||||
kurzen geraden Schritt senkrecht zu seiner Seite (RECHTECK_STUB); danach
|
||||
wird wie zwischen zwei Rauten von Stub zu Stub geführt. Sonst liefe die
|
||||
Kante am Aufgaben-Ende parallel zu dessen Kante, wenn die Anschlusspunkte
|
||||
auf verschiedenen Achsen liegen (#23)."""
|
||||
gemischt = src_is_gateway != tgt_is_gateway
|
||||
sx, sy = ox, oy
|
||||
if src_is_gateway:
|
||||
if src_is_gateway or gemischt:
|
||||
dx, dy = GATEWAY_OUTWARD[src_side]
|
||||
sx, sy = ox + dx * GATEWAY_STUB, oy + dy * GATEWAY_STUB
|
||||
stub = GATEWAY_STUB if src_is_gateway else RECHTECK_STUB
|
||||
sx, sy = ox + dx * stub, oy + dy * stub
|
||||
ex, ey = tx, ty
|
||||
if tgt_is_gateway:
|
||||
if tgt_is_gateway or gemischt:
|
||||
dx, dy = GATEWAY_OUTWARD[tgt_side]
|
||||
ex, ey = tx + dx * GATEWAY_STUB, ty + dy * GATEWAY_STUB
|
||||
stub = GATEWAY_STUB if tgt_is_gateway else RECHTECK_STUB
|
||||
ex, ey = tx + dx * stub, ty + dy * stub
|
||||
|
||||
src_axis = 'v' if src_side in ('N', 'S') else 'h'
|
||||
tgt_axis = 'v' if tgt_side in ('N', 'S') else 'h'
|
||||
|
||||
if not src_is_gateway and not tgt_is_gateway:
|
||||
points = _orthogonal_middle(ox, oy, src_side, tx, ty, tgt_side)
|
||||
elif src_is_gateway and not tgt_is_gateway:
|
||||
# sy/sx (die Stub-Achse) fixieren, solange die andere Koordinate zum
|
||||
# Ziel hin schwenkt - erst danach auf der Stub-Achse zum Ziel.
|
||||
if src_axis == 'v':
|
||||
points = [(ox, oy), (sx, sy), (tx, sy), (tx, ty)]
|
||||
else:
|
||||
points = [(ox, oy), (sx, sy), (sx, ty), (tx, ty)]
|
||||
elif tgt_is_gateway and not src_is_gateway:
|
||||
# Spiegelbildlich: erst auf der Stub-Achse des Ziels ankommen, erst
|
||||
# danach dorthin schwenken.
|
||||
if tgt_axis == 'v':
|
||||
points = [(ox, oy), (ox, ey), (ex, ey), (tx, ty)]
|
||||
else:
|
||||
points = [(ox, oy), (ex, oy), (ex, ey), (tx, ty)]
|
||||
else:
|
||||
# Beide Enden sind Rauten: von der Stub-Achse des Starts direkt auf
|
||||
# die Stub-Achse des Ziels schwenken, jede nur einmal verlassen.
|
||||
|
||||
@@ -655,6 +655,25 @@ def test_anmerkung_waechst_mit_ihrem_text():
|
||||
assert kurz_w == lang_w and lang_h > kurz_h
|
||||
|
||||
|
||||
@pytest.mark.parametrize("src_side, tgt_side, src_gw, tgt_gw", [
|
||||
('E', 'N', False, True), # Aufgabe (Ostseite) -> Gateway (Nordpunkt), Lanes-Layout
|
||||
('S', 'W', True, False), # Gateway (Südpunkt) -> Aufgabe (Westseite), Lanes-Layout
|
||||
('S', 'E', False, True), # Aufgabe (Südseite) -> Gateway (Ostpunkt), Layout ohne Lanes
|
||||
('W', 'N', True, False), # Gateway (Westpunkt) -> Aufgabe (Nordseite), Layout ohne Lanes
|
||||
])
|
||||
def test_kante_zwischen_aufgabe_und_gateway_setzt_an_beiden_enden_rechtwinklig_an(src_side, tgt_side, src_gw, tgt_gw):
|
||||
"""Prüft, dass eine Kante zwischen Aufgabe und Gateway an beiden Enden rechtwinklig zur jeweiligen Seite ansetzt,
|
||||
auch wenn die Seiten auf verschiedenen Achsen liegen (Issue #23)."""
|
||||
punkte = m.orthogonal_points(100, 100, src_side, 400, 300, tgt_side, src_is_gateway=src_gw, tgt_is_gateway=tgt_gw)
|
||||
def achse(seite):
|
||||
return 'waagrecht' if seite in ('E', 'W') else 'senkrecht'
|
||||
def verlauf(a, b):
|
||||
return 'waagrecht' if abs(a[1] - b[1]) < 1e-6 else 'senkrecht'
|
||||
assert verlauf(punkte[0], punkte[1]) == achse(src_side)
|
||||
assert verlauf(punkte[-2], punkte[-1]) == achse(tgt_side)
|
||||
assert all(verlauf(a, b) and (a[0] == b[0] or a[1] == b[1]) for a, b in zip(punkte, punkte[1:]))
|
||||
|
||||
|
||||
def test_ausgabe_ist_wohlgeformtes_xml(tmp_path):
|
||||
"""Prüft, dass die erzeugte .bpmn-Datei sich als valides XML parsen laesst."""
|
||||
src = tmp_path / 'prozess.md'
|
||||
|
||||
@@ -31,6 +31,9 @@ Skills sind übernommen, alles QGIS- und Plugin-Spezifische ist entfernt. Kern i
|
||||
|
||||
## Erledigt
|
||||
|
||||
- 25.09.2026 **Kanten setzen an Aufgaben rechtwinklig an** (#23): Stub am Aufgaben-Ende auch bei Kanten zu
|
||||
bzw. von Gateways; 17 Kantenenden in den Beispielen korrigiert.
|
||||
|
||||
- 25.09.2026 **Beschriftungen überlappen nicht mehr** (#14): Gateway-Beschriftungen mit fester Position (45°,
|
||||
sonst nächste freie Stelle), Anmerkungen rechts neben dem Pool in passender Größe, Kantenbeschriftungen
|
||||
waagrecht aus Gateways unter der Linie.
|
||||
|
||||
@@ -5,6 +5,13 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
|
||||
|
||||
## BPMN und PICTURE (Skill sn-bpmn, md_to_bpmn.py)
|
||||
|
||||
- **Auch das Aufgaben-Ende einer Gateway-Kante braucht ein gerades Stück.** Das Gateway-Ende bekommt einen festen
|
||||
Anschlusspunkt (N/O/S/W), das Aufgaben-Ende die Layout-Seite (mit Lanes O/W, ohne Lanes N/S). Liegen beide auf
|
||||
verschiedenen Achsen, lief die Kante früher direkt an der Aufgabenkante entlang. Jetzt bekommt die Aufgabe einen
|
||||
Stub (`RECHTECK_STUB = 20`) senkrecht zu ihrer Seite, danach geht es wie zwischen zwei Rauten von Stub zu Stub.
|
||||
Geprüft mit `test_kanten_setzen_an_aufgaben_rechtwinklig_an` über alle Beispiele. Im Belastungstest aus #12 sind
|
||||
378 schräge Anschlüsse verschwunden. Dafür laufen in 4 künstlichen Fällen mit Querverbindung zwischen Zweigen
|
||||
Kanten durch eine Aufgabe, in 2 anderen nicht mehr (Art von #22). (#23, 25.09.2026)
|
||||
- **PICTURE übernimmt Beschriftungspositionen und Kästchengrößen aus der Datei.** Ohne `BPMNLabel` setzt es die
|
||||
Gateway-Beschriftung auf 180° (unter die Raute), wo der südliche Pfad und dessen Beschriftung sie schneiden. Im
|
||||
PICTURE-Editor steht sie auf 45°. Der Konverter setzt die Position deshalb selbst: 45°, sonst 45° rechts vom
|
||||
|
||||
@@ -236,6 +236,45 @@ def test_anmerkung_ist_gross_genug_fuer_ihren_text(md):
|
||||
assert h >= zeilen * 16, f"{anm.get('id')}: {len(text)} Zeichen brauchen etwa {zeilen} Zeilen, Höhe nur {h:g}"
|
||||
|
||||
|
||||
RECHTECKE = HINDERNISSE[len(GATEWAYS + EREIGNISSE):] # Aufgaben, Teilprozesse, Datenelemente
|
||||
|
||||
|
||||
def schraege_anschluesse(defs):
|
||||
"""Alle Kantenenden an Rechteck-Elementen, deren erstes bzw. letztes Stück nicht rechtwinklig zu der Elementkante
|
||||
verläuft, auf der der Anschlusspunkt liegt (Ost/West verlangt waagrecht, Nord/Süd senkrecht)."""
|
||||
art = {el.get("id"): el.tag.rpartition("}")[2] for el in defs.iter() if el.get("id")}
|
||||
enden = {el.get("id"): (el.get("sourceRef"), el.get("targetRef"))
|
||||
for el in defs.iter() if art.get(el.get("id")) in KANTEN}
|
||||
formen = {s.get("bpmnElement"): _box(s) for s in defs.iter(f"{{{m.NS['bpmndi']}}}BPMNShape")}
|
||||
funde = []
|
||||
for edge in defs.iter(f"{{{m.NS['bpmndi']}}}BPMNEdge"):
|
||||
kante = edge.get("bpmnElement")
|
||||
punkte = [(float(wp.get("x")), float(wp.get("y"))) for wp in edge.findall(f"{{{m.NS['di']}}}waypoint")]
|
||||
if kante not in enden or len(punkte) < 2:
|
||||
continue
|
||||
for element, (a, b) in ((enden[kante][0], punkte[:2]), (enden[kante][1], punkte[:-3:-1])):
|
||||
if art.get(element) not in RECHTECKE:
|
||||
continue
|
||||
x, y, w, h = formen[element]
|
||||
if abs(a[0] - x) < 0.5 or abs(a[0] - x - w) < 0.5:
|
||||
rechtwinklig = abs(a[1] - b[1]) < 1e-6 # Ost/West: waagrecht
|
||||
elif abs(a[1] - y) < 0.5 or abs(a[1] - y - h) < 0.5:
|
||||
rechtwinklig = abs(a[0] - b[0]) < 1e-6 # Nord/Süd: senkrecht
|
||||
else:
|
||||
rechtwinklig = False # Anschluss nicht auf dem Rand
|
||||
if not rechtwinklig:
|
||||
funde.append((kante, element))
|
||||
return sorted(funde)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("md", MODELLE, ids=lambda p: p.stem)
|
||||
def test_kanten_setzen_an_aufgaben_rechtwinklig_an(md):
|
||||
"""Prüft, dass jede Kante an einer Aufgabe (bzw. einem anderen Rechteck-Element) rechtwinklig zu der Kante ansetzt,
|
||||
auf der ihr Anschlusspunkt liegt, statt an ihr entlangzulaufen (Issue #23)."""
|
||||
funde = schraege_anschluesse(baue(md))
|
||||
assert funde == [], "nicht rechtwinklig (Kante, Element): " + ", ".join(f"{k} → {e}" for k, e in funde)
|
||||
|
||||
|
||||
def test_kreuzungspruefung_findet_eine_kante_durch_eine_aufgabe():
|
||||
"""Prüft die Prüfung selbst: eine absichtlich durch eine Aufgabe gelegte Kante wird gefunden, eine daneben nicht."""
|
||||
defs = baue(SKILL / "beispiele" / "beispiel_prozess.md")
|
||||
|
||||
Reference in New Issue
Block a user