@@ -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"
0 commit comments