@joint/core 4.0.1 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -88,8 +88,14 @@ function resolveForTopSourceSide(source, target, nextInLine) {
88
88
  const { x0: tx, y0: ty } = target;
89
89
 
90
90
  if (tx === ax && ty < sy0) return Directions.BOTTOM;
91
- if (tx < ax && ty < smy0) return Directions.RIGHT;
92
- if (tx > ax && ty < smy0) return Directions.LEFT;
91
+ if (tx < ax && ty < smy0) {
92
+ if (nextInLine.point.x === ax) return Directions.BOTTOM;
93
+ return Directions.RIGHT;
94
+ }
95
+ if (tx > ax && ty < smy0) {
96
+ if (nextInLine.point.x === ax) return Directions.BOTTOM;
97
+ return Directions.LEFT;
98
+ }
93
99
  if (tx < smx0 && ty >= sy0) return Directions.TOP;
94
100
  if (tx > smx1 && ty >= sy0) return Directions.TOP;
95
101
  if (tx >= smx0 && tx <= ax && ty > sy1) {
@@ -122,8 +128,14 @@ function resolveForBottomSourceSide(source, target, nextInLine) {
122
128
  const { x0: tx, y0: ty } = target;
123
129
 
124
130
  if (tx === ax && ty > sy1) return Directions.TOP;
125
- if (tx < ax && ty > smy1) return Directions.RIGHT;
126
- if (tx > ax && ty > smy1) return Directions.LEFT;
131
+ if (tx < ax && ty > smy1) {
132
+ if (nextInLine.point.x === ax) return Directions.TOP;
133
+ return Directions.RIGHT;
134
+ }
135
+ if (tx > ax && ty > smy1) {
136
+ if (nextInLine.point.x === ax) return Directions.TOP;
137
+ return Directions.LEFT;
138
+ }
127
139
  if (tx < smx0 && ty <= sy1) return Directions.BOTTOM;
128
140
  if (tx > smx1 && ty <= sy1) return Directions.BOTTOM;
129
141
  if (tx >= smx0 && tx <= ax && ty < sy0) {
@@ -314,17 +326,16 @@ function getOutsidePoint(side, pointData, margin) {
314
326
  return outsidePoint;
315
327
  }
316
328
 
317
- function routeBetweenPoints(source, target) {
318
- const { point: sourcePoint, x0: sx0, y0: sy0, view: sourceView, width: sourceWidth, height: sourceHeight, margin: sourceMargin } = source;
329
+ function routeBetweenPoints(source, target, opt = {}) {
330
+ const { point: sourcePoint, x0: sx0, y0: sy0, width: sourceWidth, height: sourceHeight, margin: sourceMargin } = source;
319
331
  const { point: targetPoint, x0: tx0, y0: ty0, width: targetWidth, height: targetHeight, margin: targetMargin } = target;
332
+ const { targetInSourceBBox = false } = opt;
320
333
 
321
334
  const tx1 = tx0 + targetWidth;
322
335
  const ty1 = ty0 + targetHeight;
323
336
  const sx1 = sx0 + sourceWidth;
324
337
  const sy1 = sy0 + sourceHeight;
325
338
 
326
- const isSourceEl = sourceView && sourceView.model.isElement();
327
-
328
339
  // Key coordinates including the margin
329
340
  const smx0 = sx0 - sourceMargin;
330
341
  const smx1 = sx1 + sourceMargin;
@@ -350,10 +361,15 @@ function routeBetweenPoints(source, target) {
350
361
  const middleOfVerticalSides = (scx < tcx ? (sx1 + tx0) : (tx1 + sx0)) / 2;
351
362
  const middleOfHorizontalSides = (scy < tcy ? (sy1 + ty0) : (ty1 + sy0)) / 2;
352
363
 
364
+ const sourceBBox = new g.Rect(sx0, sy0, sourceWidth, sourceHeight);
365
+ const targetBBox = new g.Rect(tx0, ty0, targetWidth, targetHeight);
366
+ const inflatedSourceBBox = sourceBBox.clone().inflate(sourceMargin);
367
+ const inflatedTargetBBox = targetBBox.clone().inflate(targetMargin);
368
+
353
369
  if (sourceSide === 'left' && targetSide === 'right') {
354
370
  if (smx0 <= tmx1) {
355
371
  let y = middleOfHorizontalSides;
356
- if (sx1 <= tx0) {
372
+ if (sox <= tmx0) {
357
373
  if (ty1 >= smy0 && toy < soy) {
358
374
  y = Math.min(tmy0, smy0);
359
375
  } else if (ty0 <= smy1 && toy >= soy) {
@@ -374,7 +390,7 @@ function routeBetweenPoints(source, target) {
374
390
  { x, y: toy }
375
391
  ];
376
392
  } else if (sourceSide === 'right' && targetSide === 'left') {
377
- if (smx1 >= tmx0) {
393
+ if (sox >= tmx0) {
378
394
  let y = middleOfHorizontalSides;
379
395
  if (sox > tx1) {
380
396
  if (ty1 >= smy0 && toy < soy) {
@@ -398,16 +414,20 @@ function routeBetweenPoints(source, target) {
398
414
  { x, y: toy }
399
415
  ];
400
416
  } else if (sourceSide === 'top' && targetSide === 'bottom') {
417
+ const isPointInsideSource = g.intersection.rectWithRect(inflatedSourceBBox, targetBBox);
418
+
401
419
  if (soy < toy) {
402
420
  let x = middleOfVerticalSides;
403
421
  let y = soy;
404
422
 
405
- if (soy < ty0) {
406
- if (tx1 >= smx0 && tox < sox) {
407
- x = Math.min(tmx0, smx0);
408
- } else if (tx0 <= smx1 && tox >= sox) {
409
- x = Math.max(tmx1, smx1);
410
- }
423
+ if (isPointInsideSource) {
424
+ y = Math.min(y, tmy0);
425
+ }
426
+
427
+ if (tx1 >= smx0 && tox < sox) {
428
+ x = Math.min(tmx0, smx0);
429
+ } else if (tx0 <= smx1 && tox >= sox) {
430
+ x = Math.max(tmx1, smx1);
411
431
  }
412
432
 
413
433
  return [
@@ -423,16 +443,20 @@ function routeBetweenPoints(source, target) {
423
443
  { x: tox, y }
424
444
  ];
425
445
  } else if (sourceSide === 'bottom' && targetSide === 'top') {
426
- if (soy - sourceMargin > toy) {
446
+ const isPointInsideSource = g.intersection.rectWithRect(inflatedSourceBBox, targetBBox);
447
+
448
+ if (soy > toy) {
427
449
  let x = middleOfVerticalSides;
428
450
  let y = soy;
429
451
 
430
- if (soy > ty1) {
431
- if (tx1 >= smx0 && tox < sox) {
432
- x = Math.min(tmx0, smx0);
433
- } else if (tx0 <= smx1 && tox >= sox) {
434
- x = Math.max(tmx1, smx1);
435
- }
452
+ if (isPointInsideSource) {
453
+ y = Math.max(y, tmy1);
454
+ }
455
+
456
+ if (tx1 >= smx0 && tox < sox) {
457
+ x = Math.min(tmx0, smx0);
458
+ } else if (tx0 <= smx1 && tox >= sox) {
459
+ x = Math.max(tmx1, smx1);
436
460
  }
437
461
 
438
462
  return [
@@ -448,28 +472,31 @@ function routeBetweenPoints(source, target) {
448
472
  { x: tox, y }
449
473
  ];
450
474
  } else if (sourceSide === 'top' && targetSide === 'top') {
475
+ const useUShapeConnection =
476
+ targetInSourceBBox ||
477
+ g.intersection.rectWithRect(inflatedSourceBBox, targetBBox) ||
478
+ (soy <= ty0 && (inflatedSourceBBox.bottomRight().x <= tox || inflatedSourceBBox.bottomLeft().x >= tox)) ||
479
+ (soy >= ty0 && (inflatedTargetBBox.bottomRight().x <= sox || inflatedTargetBBox.bottomLeft().x >= sox));
480
+
481
+ if (useUShapeConnection) {
482
+ return [
483
+ { x: sox, y: Math.min(soy, toy) },
484
+ { x: tox, y: Math.min(soy, toy) }
485
+ ];
486
+ }
487
+
451
488
  let x;
452
489
  let y1 = Math.min((sy1 + ty0) / 2, toy);
453
490
  let y2 = Math.min((sy0 + ty1) / 2, soy);
454
491
 
455
492
  if (toy < soy) {
456
- if (sox >= tmx1 || sox <= tmx0) {
457
- return [
458
- { x: sox, y: Math.min(soy, toy) },
459
- { x: tox, y: Math.min(soy, toy) }
460
- ];
461
- } else if (tox > sox) {
493
+ if (tox > sox) {
462
494
  x = Math.min(sox, tmx0);
463
495
  } else {
464
496
  x = Math.max(sox, tmx1);
465
497
  }
466
498
  } else {
467
- if (tox >= smx1 || tox <= smx0) {
468
- return [
469
- { x: sox, y: Math.min(soy, toy) },
470
- { x: tox, y: Math.min(soy, toy) }
471
- ];
472
- } else if (tox >= sox) {
499
+ if (tox >= sox) {
473
500
  x = Math.max(tox, smx1);
474
501
  } else {
475
502
  x = Math.min(tox, smx0);
@@ -483,28 +510,31 @@ function routeBetweenPoints(source, target) {
483
510
  { x: tox, y: y1 }
484
511
  ];
485
512
  } else if (sourceSide === 'bottom' && targetSide === 'bottom') {
513
+ const useUShapeConnection =
514
+ targetInSourceBBox ||
515
+ g.intersection.rectWithRect(inflatedSourceBBox, targetBBox) ||
516
+ (soy >= toy && (inflatedSourceBBox.topRight().x <= tox || inflatedSourceBBox.topLeft().x >= tox)) ||
517
+ (soy <= toy && (inflatedTargetBBox.topRight().x <= sox || inflatedTargetBBox.topLeft().x >= sox));
518
+
519
+ if (useUShapeConnection) {
520
+ return [
521
+ { x: sox, y: Math.max(soy, toy) },
522
+ { x: tox, y: Math.max(soy, toy) }
523
+ ];
524
+ }
525
+
486
526
  let x;
487
527
  let y1 = Math.max((sy0 + ty1) / 2, toy);
488
528
  let y2 = Math.max((sy1 + ty0) / 2, soy);
489
529
 
490
530
  if (toy > soy) {
491
- if (sox >= tmx1 || sox <= tmx0) {
492
- return [
493
- { x: sox, y: Math.max(soy, toy) },
494
- { x: tox, y: Math.max(soy, toy) }
495
- ];
496
- } else if (tox > sox) {
531
+ if (tox > sox) {
497
532
  x = Math.min(sox, tmx0);
498
533
  } else {
499
534
  x = Math.max(sox, tmx1);
500
535
  }
501
536
  } else {
502
- if (tox >= smx1 || tox <= smx0) {
503
- return [
504
- { x: sox, y: Math.max(soy, toy) },
505
- { x: tox, y: Math.max(soy, toy) }
506
- ];
507
- } else if (tox >= sox) {
537
+ if (tox >= sox) {
508
538
  x = Math.max(tox, smx1);
509
539
  } else {
510
540
  x = Math.min(tox, smx0);
@@ -518,6 +548,19 @@ function routeBetweenPoints(source, target) {
518
548
  { x: tox, y: y1 }
519
549
  ];
520
550
  } else if (sourceSide === 'left' && targetSide === 'left') {
551
+ const useUShapeConnection =
552
+ targetInSourceBBox ||
553
+ g.intersection.rectWithRect(inflatedSourceBBox, targetBBox) ||
554
+ (sox <= tox && (inflatedSourceBBox.bottomRight().y <= toy || inflatedSourceBBox.topRight().y >= toy)) ||
555
+ (sox >= tox && (inflatedTargetBBox.bottomRight().y <= soy || inflatedTargetBBox.topRight().y >= soy));
556
+
557
+ if (useUShapeConnection) {
558
+ return [
559
+ { x: Math.min(sox, tox), y: soy },
560
+ { x: Math.min(sox, tox), y: toy }
561
+ ];
562
+ }
563
+
521
564
  let y;
522
565
  let x1 = Math.min((sx1 + tx0) / 2, tox);
523
566
  let x2 = Math.min((sx0 + tx1) / 2, sox);
@@ -543,11 +586,24 @@ function routeBetweenPoints(source, target) {
543
586
  { x: x1, y: toy }
544
587
  ];
545
588
  } else if (sourceSide === 'right' && targetSide === 'right') {
589
+ const useUShapeConnection =
590
+ targetInSourceBBox ||
591
+ g.intersection.rectWithRect(inflatedSourceBBox, targetBBox) ||
592
+ (sox >= tox && (inflatedSourceBBox.bottomLeft().y <= toy || inflatedSourceBBox.topLeft().y >= toy)) ||
593
+ (sox <= tox && (inflatedTargetBBox.bottomLeft().y <= soy || inflatedTargetBBox.topLeft().y >= soy));
594
+
595
+ if (useUShapeConnection) {
596
+ return [
597
+ { x: Math.max(sox, tox), y: soy },
598
+ { x: Math.max(sox, tox), y: toy }
599
+ ];
600
+ }
601
+
546
602
  let y;
547
603
  let x1 = Math.max((sx0 + tx1) / 2, tox);
548
604
  let x2 = Math.max((sx1 + tx0) / 2, sox);
549
605
 
550
- if (tox < sox) {
606
+ if (tox <= sox) {
551
607
  if (toy <= soy) {
552
608
  y = Math.min(smy0, toy);
553
609
  } else {
@@ -568,13 +624,41 @@ function routeBetweenPoints(source, target) {
568
624
  { x: x1, y: toy }
569
625
  ];
570
626
  } else if (sourceSide === 'top' && targetSide === 'right') {
571
- if (soy > toy) {
627
+ const isPointInsideSource = inflatedSourceBBox.containsPoint(targetPoint);
628
+
629
+ // The target point is inside the source element
630
+ if (isPointInsideSource) {
631
+ if (sox <= tmx1) {
632
+ const x = Math.max(sox + sourceMargin, tox);
633
+ const y = Math.min(smy0, tmy0);
634
+
635
+ // Target anchor is on the right side of the source anchor
636
+ return [
637
+ { x: sox, y },
638
+ { x: x, y },
639
+ { x: x, y: toy }
640
+ ];
641
+ }
642
+
643
+ // Target anchor is on the left side of the source anchor
644
+ // Subtract the `sourceMargin` since the source anchor is on the right side of the target anchor
645
+ const anchorMiddleX = (sox - sourceMargin + tox) / 2;
646
+
647
+ return [
648
+ { x: sox, y: soy },
649
+ { x: anchorMiddleX, y: soy },
650
+ { x: anchorMiddleX, y: toy }
651
+ ];
652
+ }
653
+
654
+ if (smy0 > toy) {
572
655
  if (sox < tox) {
573
- let y = middleOfHorizontalSides;
656
+ let y = tmy0;
574
657
 
575
- if ((y > tcy || !isSourceEl) && y < tmy1 && sox < tx0) {
576
- y = tmy0;
658
+ if (tmy1 <= smy0 && tmx1 >= sox) {
659
+ y = middleOfHorizontalSides;
577
660
  }
661
+
578
662
  return [
579
663
  { x: sox, y },
580
664
  { x: tox, y },
@@ -587,17 +671,17 @@ function routeBetweenPoints(source, target) {
587
671
 
588
672
  const x = Math.max(middleOfVerticalSides, tmx1);
589
673
 
590
- if (tox < sox && toy > sy0 && toy < sy1) {
674
+ if (sox > tox && sy1 >= toy) {
591
675
  return [
592
676
  { x: sox, y: soy },
593
- { x: x, y: soy },
594
- { x: x, y: toy }
677
+ { x, y: soy },
678
+ { x, y: toy }
595
679
  ];
596
680
  }
597
681
 
598
- if ((x > smx0 && toy > sy0) || tx0 > sx1) {
599
- const y = Math.min(sy0 - sourceMargin, ty0 - targetMargin);
600
- const x = Math.max(sx1 + sourceMargin, tx1 + targetMargin);
682
+ if (x > smx0 && soy < ty1) {
683
+ const y = Math.min(smy0, tmy0);
684
+ const x = Math.max(smx1, tmx1);
601
685
  return [
602
686
  { x: sox, y },
603
687
  { x, y },
@@ -607,23 +691,52 @@ function routeBetweenPoints(source, target) {
607
691
 
608
692
  return [
609
693
  { x: sox, y: soy },
610
- { x: Math.max(x, tox), y: soy },
611
- { x: Math.max(x, tox), y: toy }
694
+ { x, y: soy },
695
+ { x, y: toy }
612
696
  ];
613
697
  } else if (sourceSide === 'top' && targetSide === 'left') {
614
- if (soy > toy) {
698
+ const isPointInsideSource = inflatedSourceBBox.containsPoint(targetPoint);
699
+
700
+ // The target point is inside the source element
701
+ if (isPointInsideSource) {
702
+ if (sox >= tmx0) {
703
+ const x = Math.min(sox - sourceMargin, tox);
704
+ const y = Math.min(smy0, tmy0);
705
+
706
+ // Target anchor is on the left side of the source anchor
707
+ return [
708
+ { x: sox, y },
709
+ { x: x, y },
710
+ { x: x, y: toy }
711
+ ];
712
+ }
713
+
714
+ // Target anchor is on the right side of the source anchor
715
+ // Add the `sourceMargin` since the source anchor is on the left side of the target anchor
716
+ const anchorMiddleX = (sox + sourceMargin + tox) / 2;
717
+
718
+ return [
719
+ { x: sox, y: soy },
720
+ { x: anchorMiddleX, y: soy },
721
+ { x: anchorMiddleX, y: toy }
722
+ ];
723
+ }
724
+
725
+ if (smy0 > toy) {
615
726
  if (sox > tox) {
616
- let y = middleOfHorizontalSides;
727
+ let y = tmy0;
617
728
 
618
- if ((y > tcy || !isSourceEl) && y < tmy1 && sox > tx1) {
619
- y = tmy0;
729
+ if (tmy1 <= smy0 && tmx0 <= sox) {
730
+ y = middleOfHorizontalSides;
620
731
  }
732
+
621
733
  return [
622
734
  { x: sox, y },
623
735
  { x: tox, y },
624
736
  { x: tox, y: toy }
625
737
  ];
626
738
  }
739
+
627
740
  return [{ x: sox, y: toy }];
628
741
  }
629
742
 
@@ -645,39 +758,77 @@ function routeBetweenPoints(source, target) {
645
758
  { x, y: toy }
646
759
  ];
647
760
  }
761
+
648
762
  return [
649
763
  { x: sox, y: soy },
650
764
  { x, y: soy },
651
765
  { x, y: toy }
652
766
  ];
653
767
  } else if (sourceSide === 'bottom' && targetSide === 'right') {
654
- if (soy < toy) {
768
+ const isPointInsideSource = inflatedSourceBBox.containsPoint(targetPoint);
769
+
770
+ // The target point is inside the source element
771
+ if (isPointInsideSource) {
772
+ if (sox <= tmx1) {
773
+ const x = Math.max(sox + sourceMargin, tox);
774
+ const y = Math.max(smy1, tmy1);
775
+
776
+ // Target anchor is on the right side of the source anchor
777
+ return [
778
+ { x: sox, y },
779
+ { x, y },
780
+ { x, y: toy }
781
+ ];
782
+ }
783
+
784
+ // Target anchor is on the left side of the source anchor
785
+ // Subtract the `sourceMargin` since the source anchor is on the right side of the target anchor
786
+ const anchorMiddleX = (sox - sourceMargin + tox) / 2;
787
+
788
+ return [
789
+ { x: sox, y: soy },
790
+ { x: anchorMiddleX, y: soy },
791
+ { x: anchorMiddleX, y: toy }
792
+ ];
793
+ }
794
+
795
+ if (smy1 < toy) {
655
796
  if (sox < tox) {
656
- let y = middleOfHorizontalSides;
797
+ let y = tmy1;
657
798
 
658
- if ((y < tcy || !isSourceEl) && y > tmy0 && sox < tx0) {
659
- y = tmy1;
799
+ if (tmy0 >= smy1 && tmx1 >= sox) {
800
+ y = middleOfHorizontalSides;
660
801
  }
802
+
661
803
  return [
662
804
  { x: sox, y },
663
805
  { x: tox, y },
664
806
  { x: tox, y: toy }
665
807
  ];
666
808
  }
809
+
667
810
  return [{ x: sox, y: toy }];
668
- } else {
669
- if (sx0 < tox) {
670
- const y = Math.max(smy1, tmy1);
671
- const x = Math.max(smx1, tmx1);
672
- return [
673
- { x: sox, y },
674
- { x, y },
675
- { x, y: toy }
676
- ];
677
- }
678
811
  }
679
812
 
680
- const x = middleOfVerticalSides;
813
+ const x = Math.max(middleOfVerticalSides, tmx1);
814
+
815
+ if (sox > tox && sy0 <= toy) {
816
+ return [
817
+ { x: sox, y: soy },
818
+ { x, y: soy },
819
+ { x, y: toy }
820
+ ];
821
+ }
822
+
823
+ if (x > smx0 && soy > ty0) {
824
+ const y = Math.max(smy1, tmy1);
825
+ const x = Math.max(smx1, tmx1);
826
+ return [
827
+ { x: sox, y },
828
+ { x, y },
829
+ { x, y: toy }
830
+ ];
831
+ }
681
832
 
682
833
  return [
683
834
  { x: sox, y: soy },
@@ -685,58 +836,123 @@ function routeBetweenPoints(source, target) {
685
836
  { x, y: toy }
686
837
  ];
687
838
  } else if (sourceSide === 'bottom' && targetSide === 'left') {
688
- if (soy < toy) {
839
+ const isPointInsideSource = inflatedSourceBBox.containsPoint(targetPoint);
840
+
841
+ // The target point is inside the source element
842
+ if (isPointInsideSource) {
843
+ if (sox >= tmx0) {
844
+ const x = Math.min(sox - sourceMargin, tox);
845
+ const y = Math.max(smy1, tmy1);
846
+
847
+ // Target anchor is on the left side of the source anchor
848
+ return [
849
+ { x: sox, y },
850
+ { x, y },
851
+ { x, y: toy }
852
+ ];
853
+ }
854
+
855
+ // Target anchor is on the right side of the source anchor
856
+ // Add the `sourceMargin` since the source anchor is on the left side of the target anchor
857
+ const anchorMiddleX = (sox + sourceMargin + tox) / 2;
858
+
859
+ return [
860
+ { x: sox, y: soy },
861
+ { x: anchorMiddleX, y: soy },
862
+ { x: anchorMiddleX, y: toy }
863
+ ];
864
+ }
865
+
866
+ if (smy1 < toy) {
689
867
  if (sox > tox) {
690
- let y = middleOfHorizontalSides;
868
+ let y = tmy1;
691
869
 
692
- if ((y < tcy || !isSourceEl) && y > tmy0 && sox > tx1) {
693
- y = tmy1;
870
+ if (tmy0 >= smy1 && tmx0 <= sox) {
871
+ y = middleOfHorizontalSides;
694
872
  }
873
+
695
874
  return [
696
875
  { x: sox, y },
697
876
  { x: tox, y },
698
877
  { x: tox, y: toy }
699
878
  ];
700
879
  }
880
+
701
881
  return [{ x: sox, y: toy }];
702
- } else {
703
- if (sx1 > tox) {
704
- const y = Math.max(smy1, tmy1);
705
- const x = Math.min(smx0, tmx0);
706
- return [
707
- { x: sox, y },
708
- { x, y },
709
- { x, y: toy }
710
- ];
711
- }
712
882
  }
713
883
 
714
- const x = middleOfVerticalSides;
884
+ const x = Math.min(tmx0, middleOfVerticalSides);
885
+
886
+ if (sox < tox && sy0 <= toy) {
887
+ return [
888
+ { x: sox, y: soy },
889
+ { x, y: soy },
890
+ { x, y: toy }
891
+ ];
892
+ }
893
+
894
+ if (x < smx1 && soy > ty0) {
895
+ const y = Math.max(smy1, tmy1);
896
+ const x = Math.min(smx0, tmx0);
897
+ return [
898
+ { x: sox, y },
899
+ { x, y },
900
+ { x, y: toy }
901
+ ];
902
+ }
715
903
 
716
904
  return [
717
905
  { x: sox, y: soy },
718
906
  { x, y: soy },
719
907
  { x, y: toy }
720
908
  ];
721
- }
722
- else if (sourceSide === 'left' && targetSide === 'bottom') {
723
- if (sox >= tox && soy >= tmy1) {
724
- return [{ x: tox, y: soy }];
725
- }
909
+ } else if (sourceSide === 'left' && targetSide === 'bottom') {
910
+ const isPointInsideSource = inflatedSourceBBox.containsPoint(targetPoint);
911
+
912
+ // The target point is inside the source element
913
+ if (isPointInsideSource) {
914
+ if (soy <= tmy1) {
915
+ const x = Math.min(smx0, tmx0);
916
+ const y = Math.max(soy + sourceMargin, toy);
917
+
918
+ return [
919
+ { x, y: soy },
920
+ { x, y },
921
+ { x: tox, y }
922
+ ];
923
+ }
726
924
 
727
- if (sox >= tx1 && soy < toy) {
728
- const x = middleOfVerticalSides;
925
+ // Target anchor is above the source anchor
926
+ const anchorMiddleY = (soy - sourceMargin + toy) / 2;
729
927
 
730
928
  return [
731
- { x, y: soy },
732
- { x, y: toy },
733
- { x: tox, y: toy }
929
+ { x: sox, y: soy },
930
+ { x: sox, y: anchorMiddleY },
931
+ { x: tox, y: anchorMiddleY }
734
932
  ];
735
933
  }
736
934
 
737
- if (tox < sx1 && ty1 <= sy0) {
738
- const y = middleOfHorizontalSides;
935
+ if (smx0 > tox) {
936
+ if (soy < toy) {
937
+ let x = tmx0;
938
+
939
+ if (tmx1 <= smx0 && tmy1 >= soy) {
940
+ x = middleOfVerticalSides;
941
+ }
942
+
943
+ return [
944
+ { x, y: soy },
945
+ { x, y: toy },
946
+ { x: tox, y: toy }
947
+ ];
948
+ }
949
+
950
+ return [{ x: tox, y: soy }];
951
+ }
952
+
953
+ const y = Math.max(tmy1, middleOfHorizontalSides);
739
954
 
955
+ if (soy > toy && sx1 >= tox) {
740
956
  return [
741
957
  { x: sox, y: soy },
742
958
  { x: sox, y },
@@ -744,22 +960,57 @@ function routeBetweenPoints(source, target) {
744
960
  ];
745
961
  }
746
962
 
747
- const x = Math.min(tmx0, sox);
748
- const y = Math.max(smy1, tmy1);
963
+ if (y > smy0 && sox < tx1) {
964
+ const x = Math.min(smx0, tmx0);
965
+ const y = Math.max(smy1, tmy1);
966
+
967
+ return [
968
+ { x, y: soy },
969
+ { x, y },
970
+ { x: tox, y }
971
+ ];
972
+ }
749
973
 
750
974
  return [
751
- { x, y: soy },
752
- { x, y },
975
+ { x: sox, y: soy },
976
+ { x: sox, y },
753
977
  { x: tox, y }
754
978
  ];
755
979
  } else if (sourceSide === 'left' && targetSide === 'top') {
756
- if (sox > tox && soy < tmy0) {
757
- return [{ x: tox, y: soy }];
980
+ const isPointInsideSource = inflatedSourceBBox.containsPoint(targetPoint);
981
+
982
+ // The target point is inside the source element
983
+ if (isPointInsideSource) {
984
+ if (soy >= tmy0) {
985
+ const y = Math.min(soy - sourceMargin, toy);
986
+ const x = Math.min(smx0, tmx0);
987
+
988
+ // Target anchor is on the top side of the source anchor
989
+ return [
990
+ { x, y: soy },
991
+ { x, y },
992
+ { x: tox, y }
993
+ ];
994
+ }
995
+
996
+ // Target anchor is below the source anchor
997
+ // Add the `sourceMargin` since the source anchor is above the target anchor
998
+ const anchorMiddleY = (soy + sourceMargin + toy) / 2;
999
+
1000
+ return [
1001
+ { x: sox, y: soy },
1002
+ { x: sox, y: anchorMiddleY },
1003
+ { x: tox, y: anchorMiddleY }
1004
+ ];
758
1005
  }
759
1006
 
760
- if (sox >= tx1) {
1007
+ if (smx0 > tox) {
761
1008
  if (soy > toy) {
762
- const x = middleOfVerticalSides;
1009
+ let x = tmx0;
1010
+
1011
+ if (tmx1 <= smx0 && tmy0 <= soy) {
1012
+ x = middleOfVerticalSides;
1013
+ }
763
1014
 
764
1015
  return [
765
1016
  { x, y: soy },
@@ -767,78 +1018,152 @@ function routeBetweenPoints(source, target) {
767
1018
  { x: tox, y: toy }
768
1019
  ];
769
1020
  }
1021
+
1022
+ return [{ x: tox, y: soy }];
770
1023
  }
771
1024
 
772
- if (tox <= sx1 && toy > soy) {
773
- const y = middleOfHorizontalSides;
1025
+ const y = Math.min(tmy0, middleOfHorizontalSides);
774
1026
 
1027
+ if (soy < toy && sx1 >= tox) {
775
1028
  return [
776
1029
  { x: sox, y: soy },
777
1030
  { x: sox, y },
778
- { x: tox, y },
779
- ];
1031
+ { x: tox, y }];
780
1032
  }
781
1033
 
782
- const x = toy < soy ? Math.min(smx0, tmx0) : smx0;
783
- const y = Math.min(smy0, tmy0);
1034
+ if (y < smy1 && sox < tx1) {
1035
+ const x = Math.min(smx0, tmx0);
1036
+ const y = Math.min(smy0, tmy0);
1037
+ return [
1038
+ { x, y: soy },
1039
+ { x, y },
1040
+ { x: tox, y }
1041
+ ];
1042
+ }
784
1043
 
785
1044
  return [
786
- { x, y: soy },
787
- { x, y },
1045
+ { x: sox, y: soy },
1046
+ { x: sox, y },
788
1047
  { x: tox, y }
789
1048
  ];
790
-
791
1049
  } else if (sourceSide === 'right' && targetSide === 'top') {
792
- if (sox <= tox && soy < tmy0) {
793
- return [{ x: tox, y: soy }];
794
- }
1050
+ const isPointInsideSource = inflatedSourceBBox.containsPoint(targetPoint);
795
1051
 
796
- if (sx1 < tx0 && soy > toy) {
797
- let x = middleOfVerticalSides;
1052
+ // The target point is inside the source element
1053
+ if (isPointInsideSource) {
1054
+ if (soy >= tmy0) {
1055
+ const x = Math.max(smx1, tmx1);
1056
+ const y = Math.min(soy - sourceMargin, toy);
1057
+
1058
+ // Target anchor is on the top side of the source anchor
1059
+ return [
1060
+ { x, y: soy },
1061
+ { x, y }, // Path adjustment for right side start
1062
+ { x: tox, y }
1063
+ ];
1064
+ }
1065
+
1066
+ // Target anchor is below the source anchor
1067
+ // Adjust sourceMargin calculation since the source anchor is now on the right
1068
+ const anchorMiddleY = (soy + sourceMargin + toy) / 2;
798
1069
 
799
1070
  return [
800
- { x, y: soy },
801
- { x, y: toy },
802
- { x: tox, y: toy }
1071
+ { x: sox, y: soy },
1072
+ { x: sox, y: anchorMiddleY },
1073
+ { x: tox, y: anchorMiddleY }
803
1074
  ];
804
1075
  }
805
1076
 
806
- if (tox < sox && ty0 > sy1) {
807
- const y = middleOfHorizontalSides;
1077
+ if (smx1 < tox) {
1078
+ if (soy > toy) {
1079
+ let x = tmx1;
1080
+
1081
+ if (tmx0 >= smx1 && tmy0 <= soy) {
1082
+ x = middleOfVerticalSides;
1083
+ }
1084
+
1085
+ return [
1086
+ { x, y: soy },
1087
+ { x, y: toy },
1088
+ { x: tox, y: toy }
1089
+ ];
1090
+ }
1091
+
1092
+ return [{ x: tox, y: soy }];
1093
+ }
1094
+
1095
+ const y = Math.min(tmy0, middleOfHorizontalSides);
808
1096
 
1097
+ if (soy < toy && sx0 <= tox) {
809
1098
  return [
810
1099
  { x: sox, y: soy },
811
1100
  { x: sox, y },
1101
+ { x: tox, y }];
1102
+ }
1103
+
1104
+ if (y < smy1 && sox > tx0) {
1105
+ const x = Math.max(smx1, tmx1);
1106
+ const y = Math.min(smy0, tmy0);
1107
+
1108
+ return [
1109
+ { x, y: soy },
1110
+ { x, y },
812
1111
  { x: tox, y }
813
1112
  ];
814
1113
  }
815
1114
 
816
- const x = Math.max(smx1, tmx1);
817
- const y = Math.min(smy0, tmy0);
818
-
819
1115
  return [
820
- { x, y: soy },
821
- { x, y },
1116
+ { x: sox, y: soy },
1117
+ { x: sox, y },
822
1118
  { x: tox, y }
823
1119
  ];
824
1120
  } else if (sourceSide === 'right' && targetSide === 'bottom') {
825
- if (sox <= tox && soy >= tmy1) {
826
- return [{ x: tox, y: soy }];
827
- }
1121
+ const isPointInsideSource = inflatedSourceBBox.containsPoint(targetPoint);
1122
+
1123
+ // The target point is inside the source element
1124
+ if (isPointInsideSource) {
1125
+ if (soy <= tmy1) {
1126
+ const x = Math.max(smx1, tmx1);
1127
+ const y = Math.max(soy + sourceMargin, toy);
1128
+
1129
+ return [
1130
+ { x, y: soy },
1131
+ { x, y },
1132
+ { x: tox, y }
1133
+ ];
1134
+ }
828
1135
 
829
- if (sox <= tmx0 && soy < toy) {
830
- const x = middleOfVerticalSides;
1136
+ // Target anchor is above the source anchor
1137
+ const anchorMiddleY = (soy - sourceMargin + toy) / 2;
831
1138
 
832
1139
  return [
833
- { x, y: soy },
834
- { x, y: toy },
835
- { x: tox, y: toy }
1140
+ { x: sox, y: soy },
1141
+ { x: sox, y: anchorMiddleY },
1142
+ { x: tox, y: anchorMiddleY }
836
1143
  ];
837
1144
  }
838
1145
 
839
- if (tox > sx0 && ty1 < sy0) {
840
- const y = middleOfHorizontalSides;
1146
+ if (smx1 < tox) {
1147
+ if (soy < toy) {
1148
+ let x = tmx1;
1149
+
1150
+ if (tmx0 >= smx1 && tmy1 >= soy) {
1151
+ x = middleOfVerticalSides;
1152
+ }
841
1153
 
1154
+ return [
1155
+ { x, y: soy },
1156
+ { x, y: toy },
1157
+ { x: tox, y: toy }
1158
+ ];
1159
+ }
1160
+
1161
+ return [{ x: tox, y: soy }];
1162
+ }
1163
+
1164
+ const y = Math.max(tmy1, middleOfHorizontalSides);
1165
+
1166
+ if (soy > toy && sx0 <= tox) {
842
1167
  return [
843
1168
  { x: sox, y: soy },
844
1169
  { x: sox, y },
@@ -846,12 +1171,20 @@ function routeBetweenPoints(source, target) {
846
1171
  ];
847
1172
  }
848
1173
 
849
- const x = Math.max(tmx1, sox);
850
- const y = Math.max(smy1, tmy1);
1174
+ if (y > smy0 && sox > tx0) {
1175
+ const x = Math.max(smx1, tmx1);
1176
+ const y = Math.max(smy1, tmy1);
1177
+
1178
+ return [
1179
+ { x, y: soy },
1180
+ { x, y },
1181
+ { x: tox, y }
1182
+ ];
1183
+ }
851
1184
 
852
1185
  return [
853
- { x, y: soy },
854
- { x, y },
1186
+ { x: sox, y: soy },
1187
+ { x: sox, y },
855
1188
  { x: tox, y }
856
1189
  ];
857
1190
  }
@@ -886,7 +1219,7 @@ function rightAngleRouter(vertices, opt, linkView) {
886
1219
  dummySource.direction = fromDirection;
887
1220
  firstVertex.direction = toDirection;
888
1221
 
889
- resultVertices.push(...routeBetweenPoints(dummySource, firstVertex), firstVertex.point);
1222
+ resultVertices.push(...routeBetweenPoints(dummySource, firstVertex, { targetInSourceBBox: true }), firstVertex.point);
890
1223
  } else {
891
1224
  // The first point responsible for the initial direction of the route
892
1225
  const next = verticesData[1] || targetPoint;