obstacle-map.js 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { ArrayExt } from '@antv/x6-common';
  2. import { Point } from '@antv/x6-geometry';
  3. /**
  4. * Helper structure to identify whether a point lies inside an obstacle.
  5. */
  6. export class ObstacleMap {
  7. constructor(options) {
  8. this.options = options;
  9. this.mapGridSize = 100;
  10. this.map = {};
  11. }
  12. /**
  13. * Builds a map of all nodes for quicker obstacle queries i.e. is a point
  14. * contained in any obstacle?
  15. *
  16. * A simplified grid search.
  17. */
  18. build(model, edge) {
  19. const options = this.options;
  20. // source or target node could be excluded from set of obstacles
  21. const excludedTerminals = options.excludeTerminals.reduce((memo, type) => {
  22. const terminal = edge[type];
  23. if (terminal) {
  24. const cell = model.getCell(terminal.cell);
  25. if (cell) {
  26. memo.push(cell);
  27. }
  28. }
  29. return memo;
  30. }, []);
  31. let excludedAncestors = [];
  32. const source = model.getCell(edge.getSourceCellId());
  33. if (source) {
  34. excludedAncestors = ArrayExt.union(excludedAncestors, source.getAncestors().map((cell) => cell.id));
  35. }
  36. const target = model.getCell(edge.getTargetCellId());
  37. if (target) {
  38. excludedAncestors = ArrayExt.union(excludedAncestors, target.getAncestors().map((cell) => cell.id));
  39. }
  40. // The graph is divided into smaller cells, where each holds information
  41. // about which node belong to it. When we query whether a point lies
  42. // inside an obstacle we don't need to go through all obstacles, we check
  43. // only those in a particular cell.
  44. const mapGridSize = this.mapGridSize;
  45. model.getNodes().reduce((map, node) => {
  46. const excludedTerminal = excludedTerminals.some((cell) => cell.id === node.id);
  47. const excludedShape = node.shape
  48. ? options.excludeShapes.includes(node.shape)
  49. : false;
  50. const excludedNode = options.excludeNodes.some((item) => {
  51. if (typeof item === 'string') {
  52. return node.id === item;
  53. }
  54. return item === node;
  55. });
  56. const excludedAncestor = excludedAncestors.includes(node.id);
  57. const excluded = excludedShape || excludedTerminal || excludedNode || excludedAncestor;
  58. if (node.isVisible() && !excluded) {
  59. const bbox = node.getBBox().moveAndExpand(options.paddingBox);
  60. const origin = bbox.getOrigin().snapToGrid(mapGridSize);
  61. const corner = bbox.getCorner().snapToGrid(mapGridSize);
  62. for (let x = origin.x; x <= corner.x; x += mapGridSize) {
  63. for (let y = origin.y; y <= corner.y; y += mapGridSize) {
  64. const key = new Point(x, y).toString();
  65. if (map[key] == null) {
  66. map[key] = [];
  67. }
  68. map[key].push(bbox);
  69. }
  70. }
  71. }
  72. return map;
  73. }, this.map);
  74. return this;
  75. }
  76. isAccessible(point) {
  77. const key = point.clone().snapToGrid(this.mapGridSize).toString();
  78. const rects = this.map[key];
  79. return rects ? rects.every((rect) => !rect.containsPoint(point)) : true;
  80. }
  81. }
  82. //# sourceMappingURL=obstacle-map.js.map