Skip to content

Commit ff03801

Browse files
authored
Merge pull request #39 from michox/master
implemented waypoints feature for draw.io
2 parents d9c9d9b + e334d31 commit ff03801

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

N2G/plugins/diagrams/N2G_DrawIO.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ def add_link(
307307
src_label_style="",
308308
trgt_label_style="",
309309
link_id=None,
310+
waypoints=None, # Add waypoints parameter
310311
**kwargs,
311312
):
312313
"""
@@ -315,7 +316,7 @@ def add_link(
315316
**Parameters**
316317
317318
* ``source`` (str) mandatory, source node id
318-
* ``source`` (str) mandatory, target node id
319+
* ``target`` (str) mandatory, target node id
319320
* ``label`` (str) link label to display at the center of the link
320321
* ``data`` (dict) dictionary of key value pairs to add as link data
321322
* ``url`` (str) url string to save as link ``url`` attribute
@@ -325,6 +326,7 @@ def add_link(
325326
* ``src_label_style`` (str) source label style string
326327
* ``trgt_label_style`` (str) target label style string
327328
* ``link_id`` (str or int) optional link id value, must be unique across all links
329+
* ``waypoints`` (list) optional list of (x, y) tuples defining intermediate points for the link path. Example: [(100, 150), (200, 150)]
328330
329331
Sample DrawIO style string for the link::
330332
@@ -402,11 +404,29 @@ def add_link(
402404
)
403405
self.current_root.append(trgt_label_obj)
404406
kwargs["trgt_label"] = trgt_label
405-
# add links data and url
407+
# add links data and url (do NOT include waypoints in link_data)
406408
link_data.update(data)
407409
link_data.update(kwargs)
408410
link_data.update({"source": source, "target": target})
411+
link_data.pop("waypoints", None) # Remove waypoints from attributes if present
409412
link = self._add_data_or_url(link, link_data, url)
413+
414+
# add or update waypoints if provided
415+
if waypoints and isinstance(waypoints, list):
416+
mxCell = link.find("./mxCell")
417+
mxGeometry = mxCell.find("./mxGeometry")
418+
if mxGeometry is None:
419+
mxGeometry = ET.SubElement(mxCell, "mxGeometry", {"relative": "1", "as": "geometry"})
420+
# Remove any existing Array as="points"
421+
for arr in mxGeometry.findall("./Array[@as='points']"):
422+
mxGeometry.remove(arr)
423+
array_elem = ET.SubElement(mxGeometry, "Array", {"as": "points"})
424+
for x, y in waypoints:
425+
try:
426+
ET.SubElement(array_elem, "mxPoint", {"x": str(float(x)), "y": str(float(y))})
427+
except Exception:
428+
continue
429+
410430
# save link to graph
411431
self.current_root.append(link)
412432

@@ -779,6 +799,7 @@ def update_link(
779799
new_trgt_label=None,
780800
src_label_style="",
781801
trgt_label_style="",
802+
waypoints=None, # Add waypoints parameter
782803
):
783804
"""
784805
Method to update edge/link details.
@@ -800,6 +821,8 @@ def update_link(
800821
* ``new_trgt_label`` (str) - new edge target label
801822
* ``src_label_style`` (str) - string with style to apply to source label
802823
* ``trgt_label_style`` (str) - strung with style to apply to target label
824+
* ``waypoints`` (list or None) - optional list of (x, y) tuples defining new intermediate points.
825+
If provided, replaces any existing waypoints. If None, existing waypoints are unchanged.
803826
804827
Either of these must be provided to find link element to update:
805828
@@ -922,6 +945,20 @@ def update_link(
922945
style = style_file.read()
923946
mxCell_elem = edge.find("./mxCell")
924947
mxCell_elem.attrib["style"] = style
948+
# Add or update waypoints if provided
949+
if waypoints is not None:
950+
mxGeometry = edge.find("./mxCell/mxGeometry")
951+
# Remove existing Array as="points" if present
952+
array_elem = mxGeometry.find("./Array[@as='points']")
953+
if array_elem is not None:
954+
mxGeometry.remove(array_elem)
955+
if waypoints:
956+
array_elem = ET.SubElement(mxGeometry, "Array", {"as": "points"})
957+
for x, y in waypoints:
958+
try:
959+
ET.SubElement(array_elem, "mxPoint", {"x": str(float(x)), "y": str(float(y))})
960+
except Exception:
961+
continue
925962

926963
def compare(
927964
self, data, diagram_name=None, missing_colour="#C0C0C0", new_colour="#00FF00"

tests/test_drawio_module.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_1_add_elements_one_by_one():
3434
with open("./Output/should_be_test_1_add_elements_one_by_one.drawio") as should_be:
3535
assert normalize_xml(produced.read()) == normalize_xml(should_be.read())
3636

37-
test_1_add_elements_one_by_one()
37+
# test_1_add_elements_one_by_one()
3838

3939
def test_2_from_dict():
4040
###########################################
@@ -483,4 +483,41 @@ def test_19_node_delete_mxcell_tag():
483483
with open("./Output/should_be_test_19_node_delete_mxcell_tag.drawio") as should_be:
484484
assert normalize_xml(produced.read()) == normalize_xml(should_be.read())
485485

486-
# test_19_node_delete_mxcell_tag()
486+
# test_19_node_delete_mxcell_tag()
487+
488+
def test_20_link_waypoints():
489+
"""
490+
Test creating and updating links with waypoints (mxPoint geometry).
491+
"""
492+
print("test_20_link_waypoints")
493+
drawio_drawing = create_drawio_diagram()
494+
drawio_drawing.add_diagram("Page-1")
495+
drawio_drawing.add_node(id="A", label="Node A", x_pos=100, y_pos=100)
496+
drawio_drawing.add_node(id="B", label="Node B", x_pos=400, y_pos=300)
497+
# Add link with waypoints
498+
waypoints1 = [(200, 120), (300, 280), (350, 200)]
499+
drawio_drawing.add_link(source="A", target="B", label="A to B", waypoints=waypoints1)
500+
drawio_drawing.dump_file(filename="test_20_link_waypoints_1.drawio", folder="./Output/")
501+
# Check waypoints in output
502+
with open("./Output/test_20_link_waypoints_1.drawio") as f:
503+
xml = f.read()
504+
assert '<Array as="points">' in xml
505+
assert '<mxPoint x="200.0" y="120.0" />' in xml
506+
assert '<mxPoint x="300.0" y="280.0" />' in xml
507+
assert '<mxPoint x="350.0" y="200.0" />' in xml
508+
# Update link with new waypoints
509+
waypoints2 = [(150, 150), (250, 250)]
510+
# Find the link id (should be only one link)
511+
link_id = drawio_drawing.edges_ids[drawio_drawing.current_diagram_id][0]
512+
drawio_drawing.update_link(edge_id=link_id, waypoints=waypoints2)
513+
drawio_drawing.dump_file(filename="test_20_link_waypoints_2.drawio", folder="./Output/")
514+
# Check updated waypoints in output
515+
with open("./Output/test_20_link_waypoints_2.drawio") as f:
516+
xml = f.read()
517+
assert '<Array as="points">' in xml
518+
assert '<mxPoint x="150.0" y="150.0" />' in xml
519+
assert '<mxPoint x="250.0" y="250.0" />' in xml
520+
assert '<mxPoint x="200.0" y="120.0" />' not in xml # old point should be gone
521+
assert '<mxPoint x="350.0" y="200.0" />' not in xml
522+
523+
test_20_link_waypoints()

0 commit comments

Comments
 (0)