vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
3.5 KiB

  1. // Utility functions for ordering and stacking of items
  2. var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors
  3. /**
  4. * Order items by their start data
  5. * @param {Item[]} items
  6. */
  7. exports.orderByStart = function(items) {
  8. items.sort(function (a, b) {
  9. return a.data.start - b.data.start;
  10. });
  11. };
  12. /**
  13. * Order items by their end date. If they have no end date, their start date
  14. * is used.
  15. * @param {Item[]} items
  16. */
  17. exports.orderByEnd = function(items) {
  18. items.sort(function (a, b) {
  19. var aTime = ('end' in a.data) ? a.data.end : a.data.start,
  20. bTime = ('end' in b.data) ? b.data.end : b.data.start;
  21. return aTime - bTime;
  22. });
  23. };
  24. /**
  25. * Adjust vertical positions of the items such that they don't overlap each
  26. * other.
  27. * @param {Item[]} items
  28. * All visible items
  29. * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
  30. * Margins between items and between items and the axis.
  31. * @param {boolean} [force=false]
  32. * If true, all items will be repositioned. If false (default), only
  33. * items having a top===null will be re-stacked
  34. */
  35. exports.stack = function(items, margin, force) {
  36. var i, iMax;
  37. if (force) {
  38. // reset top position of all items
  39. for (i = 0, iMax = items.length; i < iMax; i++) {
  40. items[i].top = null;
  41. }
  42. }
  43. // calculate new, non-overlapping positions
  44. for (i = 0, iMax = items.length; i < iMax; i++) {
  45. var item = items[i];
  46. if (item.top === null) {
  47. // initialize top position
  48. item.top = margin.axis;
  49. do {
  50. // TODO: optimize checking for overlap. when there is a gap without items,
  51. // you only need to check for items from the next item on, not from zero
  52. var collidingItem = null;
  53. for (var j = 0, jj = items.length; j < jj; j++) {
  54. var other = items[j];
  55. if (other.top !== null && other !== item && other.ignoreStacking == false && exports.collision(item, other, margin.item)) {
  56. collidingItem = other;
  57. break;
  58. }
  59. }
  60. if (collidingItem != null) {
  61. // There is a collision. Reposition the items above the colliding element
  62. item.top = collidingItem.top + collidingItem.height + margin.item.vertical;
  63. }
  64. } while (collidingItem);
  65. }
  66. }
  67. };
  68. /**
  69. * Adjust vertical positions of the items without stacking them
  70. * @param {Item[]} items
  71. * All visible items
  72. * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin
  73. * Margins between items and between items and the axis.
  74. */
  75. exports.nostack = function(items, margin) {
  76. var i, iMax;
  77. // reset top position of all items
  78. for (i = 0, iMax = items.length; i < iMax; i++) {
  79. items[i].top = margin.axis;
  80. }
  81. };
  82. /**
  83. * Test if the two provided items collide
  84. * The items must have parameters left, width, top, and height.
  85. * @param {Item} a The first item
  86. * @param {Item} b The second item
  87. * @param {{horizontal: number, vertical: number}} margin
  88. * An object containing a horizontal and vertical
  89. * minimum required margin.
  90. * @return {boolean} true if a and b collide, else false
  91. */
  92. exports.collision = function(a, b, margin) {
  93. return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) &&
  94. (a.left + a.width + margin.horizontal - EPSILON) > b.left &&
  95. (a.top - margin.vertical + EPSILON) < (b.top + b.height) &&
  96. (a.top + a.height + margin.vertical - EPSILON) > b.top);
  97. };