join.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import hashset from "./hash/hashset.js";
  2. import hashmap from "./hash/hashmap.js";
  3. import equalPoint from "./hash/point-equal.js";
  4. import hashPoint from "./hash/point-hash.js";
  5. // Given an extracted (pre-)topology, identifies all of the junctions. These are
  6. // the points at which arcs (lines or rings) will need to be cut so that each
  7. // arc is represented uniquely.
  8. //
  9. // A junction is a point where at least one arc deviates from another arc going
  10. // through the same point. For example, consider the point B. If there is a arc
  11. // through ABC and another arc through CBA, then B is not a junction because in
  12. // both cases the adjacent point pairs are {A,C}. However, if there is an
  13. // additional arc ABD, then {A,D} != {A,C}, and thus B becomes a junction.
  14. //
  15. // For a closed ring ABCA, the first point A’s adjacent points are the second
  16. // and last point {B,C}. For a line, the first and last point are always
  17. // considered junctions, even if the line is closed; this ensures that a closed
  18. // line is never rotated.
  19. export default function(topology) {
  20. var coordinates = topology.coordinates,
  21. lines = topology.lines,
  22. rings = topology.rings,
  23. indexes = index(),
  24. visitedByIndex = new Int32Array(coordinates.length),
  25. leftByIndex = new Int32Array(coordinates.length),
  26. rightByIndex = new Int32Array(coordinates.length),
  27. junctionByIndex = new Int8Array(coordinates.length),
  28. junctionCount = 0, // upper bound on number of junctions
  29. i, n,
  30. previousIndex,
  31. currentIndex,
  32. nextIndex;
  33. for (i = 0, n = coordinates.length; i < n; ++i) {
  34. visitedByIndex[i] = leftByIndex[i] = rightByIndex[i] = -1;
  35. }
  36. for (i = 0, n = lines.length; i < n; ++i) {
  37. var line = lines[i],
  38. lineStart = line[0],
  39. lineEnd = line[1];
  40. currentIndex = indexes[lineStart];
  41. nextIndex = indexes[++lineStart];
  42. ++junctionCount, junctionByIndex[currentIndex] = 1; // start
  43. while (++lineStart <= lineEnd) {
  44. sequence(i, previousIndex = currentIndex, currentIndex = nextIndex, nextIndex = indexes[lineStart]);
  45. }
  46. ++junctionCount, junctionByIndex[nextIndex] = 1; // end
  47. }
  48. for (i = 0, n = coordinates.length; i < n; ++i) {
  49. visitedByIndex[i] = -1;
  50. }
  51. for (i = 0, n = rings.length; i < n; ++i) {
  52. var ring = rings[i],
  53. ringStart = ring[0] + 1,
  54. ringEnd = ring[1];
  55. previousIndex = indexes[ringEnd - 1];
  56. currentIndex = indexes[ringStart - 1];
  57. nextIndex = indexes[ringStart];
  58. sequence(i, previousIndex, currentIndex, nextIndex);
  59. while (++ringStart <= ringEnd) {
  60. sequence(i, previousIndex = currentIndex, currentIndex = nextIndex, nextIndex = indexes[ringStart]);
  61. }
  62. }
  63. function sequence(i, previousIndex, currentIndex, nextIndex) {
  64. if (visitedByIndex[currentIndex] === i) return; // ignore self-intersection
  65. visitedByIndex[currentIndex] = i;
  66. var leftIndex = leftByIndex[currentIndex];
  67. if (leftIndex >= 0) {
  68. var rightIndex = rightByIndex[currentIndex];
  69. if ((leftIndex !== previousIndex || rightIndex !== nextIndex)
  70. && (leftIndex !== nextIndex || rightIndex !== previousIndex)) {
  71. ++junctionCount, junctionByIndex[currentIndex] = 1;
  72. }
  73. } else {
  74. leftByIndex[currentIndex] = previousIndex;
  75. rightByIndex[currentIndex] = nextIndex;
  76. }
  77. }
  78. function index() {
  79. var indexByPoint = hashmap(coordinates.length * 1.4, hashIndex, equalIndex, Int32Array, -1, Int32Array),
  80. indexes = new Int32Array(coordinates.length);
  81. for (var i = 0, n = coordinates.length; i < n; ++i) {
  82. indexes[i] = indexByPoint.maybeSet(i, i);
  83. }
  84. return indexes;
  85. }
  86. function hashIndex(i) {
  87. return hashPoint(coordinates[i]);
  88. }
  89. function equalIndex(i, j) {
  90. return equalPoint(coordinates[i], coordinates[j]);
  91. }
  92. visitedByIndex = leftByIndex = rightByIndex = null;
  93. var junctionByPoint = hashset(junctionCount * 1.4, hashPoint, equalPoint), j;
  94. // Convert back to a standard hashset by point for caller convenience.
  95. for (i = 0, n = coordinates.length; i < n; ++i) {
  96. if (junctionByIndex[j = indexes[i]]) {
  97. junctionByPoint.add(coordinates[j]);
  98. }
  99. }
  100. return junctionByPoint;
  101. }