graffle.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. /**
  2. * Originally grabbed from the official RaphaelJS Documentation
  3. * http://raphaeljs.com/graffle.html
  4. * Changes and comments by Philipp Strathausen http://blog.ameisenbar.de
  5. * Licenced under the MIT licence.
  6. */
  7. /**
  8. * Usage:
  9. * connect two shapes
  10. * parameters:
  11. * source shape [or connection for redrawing],
  12. * target shape,
  13. * style with { fg : linecolor, bg : background color, directed: boolean }
  14. * returns:
  15. * connection { draw = function() }
  16. */
  17. Raphael.fn.connection = function (obj1, obj2, style) {
  18. var selfRef = this;
  19. /* create and return new connection */
  20. var color = style.fg || "#000";
  21. var edge = {/*
  22. from : obj1,
  23. to : obj2,
  24. style : style,*/
  25. draw : function() {
  26. /* get bounding boxes of target and source */
  27. var bb1 = obj1.getBBox();
  28. var bb2 = obj2.getBBox();
  29. bb1.height = bb1.height + 15; /* respect the label TODO do it in a cleaner way */
  30. bb2.height = bb2.height + 15;
  31. var off1 = 1;
  32. var off2 = 2;
  33. /* coordinates for potential connection coordinates from/to the objects */
  34. var p = [
  35. {x: bb1.x + bb1.width / 2, y: bb1.y - off1}, /* NORTH 1 */
  36. {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + off1}, /* SOUTH 1 */
  37. {x: bb1.x - off1, y: bb1.y + bb1.height / 2}, /* WEST 1 */
  38. {x: bb1.x + bb1.width + off1, y: bb1.y + bb1.height / 2}, /* EAST 1 */
  39. {x: bb2.x + bb2.width / 2, y: bb2.y - off2}, /* NORTH 2 */
  40. {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + off2}, /* SOUTH 2 */
  41. {x: bb2.x - off2, y: bb2.y + bb2.height / 2}, /* WEST 2 */
  42. {x: bb2.x + bb2.width + off2, y: bb2.y + bb2.height / 2} /* EAST 2 */
  43. ];
  44. /* distances between objects and according coordinates connection */
  45. var d = {}, dis = [];
  46. /*
  47. * find out the best connection coordinates by trying all possible ways
  48. */
  49. /* loop the first object's connection coordinates */
  50. for (var i = 0; i < 4; i++) {
  51. /* loop the seond object's connection coordinates */
  52. for (var j = 4; j < 8; j++) {
  53. var dx = Math.abs(p[i].x - p[j].x),
  54. dy = Math.abs(p[i].y - p[j].y);
  55. if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
  56. dis.push(dx + dy);
  57. d[dis[dis.length - 1]] = [i, j];
  58. }
  59. }
  60. }
  61. var res = dis.length == 0 ? res = [0, 4] : d[Math.min.apply(Math, dis)];
  62. /* bezier path */
  63. var x1 = p[res[0]].x,
  64. y1 = p[res[0]].y,
  65. x4 = p[res[1]].x,
  66. y4 = p[res[1]].y,
  67. dx = Math.max(Math.abs(x1 - x4) / 2, 10),
  68. dy = Math.max(Math.abs(y1 - y4) / 2, 10),
  69. x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3),
  70. y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3),
  71. x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3),
  72. y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3);
  73. /* assemble path and arrow */
  74. var path = ["M", x1.toFixed(3), y1.toFixed(3), "C", x2, y2, x3, y3, x4.toFixed(3), y4.toFixed(3)].join(",");
  75. /* arrow */
  76. if(style && style.directed) {
  77. /* magnitude, length of the last path vector */
  78. var mag = Math.sqrt((y4 - y3) * (y4 - y3) + (x4 - x3) * (x4 - x3));
  79. /* vector normalisation to specified length */
  80. var norm = function(x,l){return (-x*(l||5)/mag);};
  81. /* calculate array coordinates (two lines orthogonal to the path vector) */
  82. var arr = [
  83. {x:(norm(x4-x3)+norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)+norm(x4-x3)+y4).toFixed(3)},
  84. {x:(norm(x4-x3)-norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)-norm(x4-x3)+y4).toFixed(3)}
  85. ];
  86. path = path + ",M"+arr[0].x+","+arr[0].y+",L"+x4+","+y4+",L"+arr[1].x+","+arr[1].y;
  87. }
  88. // applying path
  89. edge.fg && edge.fg.attr({path:path})
  90. || (edge.fg = selfRef.path(path).attr({stroke: color, fill: "none"}).toBack());
  91. edge.bg && edge.bg.attr({path:path})
  92. || style && style.bg && (edge.bg = style.bg.split && selfRef.path(path).attr({stroke: style.bg.split("|")[0], fill: "none", "stroke-width": style.bg.split("|")[1] || 3}).toBack());
  93. }
  94. }
  95. edge.draw();
  96. return edge;
  97. };