lineclip.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. // Cohen-Sutherland line clipping algorithm, adapted to efficiently
  2. // handle polylines rather than just segments
  3. export function lineclip(points, bbox, result) {
  4. var len = points.length, codeA = bitCode(points[0], bbox), part = [], i, codeB, lastCode;
  5. var a;
  6. var b;
  7. if (!result)
  8. result = [];
  9. for (i = 1; i < len; i++) {
  10. a = points[i - 1];
  11. b = points[i];
  12. codeB = lastCode = bitCode(b, bbox);
  13. while (true) {
  14. if (!(codeA | codeB)) {
  15. // accept
  16. part.push(a);
  17. if (codeB !== lastCode) {
  18. // segment went outside
  19. part.push(b);
  20. if (i < len - 1) {
  21. // start a new line
  22. result.push(part);
  23. part = [];
  24. }
  25. }
  26. else if (i === len - 1) {
  27. part.push(b);
  28. }
  29. break;
  30. }
  31. else if (codeA & codeB) {
  32. // trivial reject
  33. break;
  34. }
  35. else if (codeA) {
  36. // a outside, intersect with clip edge
  37. a = intersect(a, b, codeA, bbox);
  38. codeA = bitCode(a, bbox);
  39. }
  40. else {
  41. // b outside
  42. b = intersect(a, b, codeB, bbox);
  43. codeB = bitCode(b, bbox);
  44. }
  45. }
  46. codeA = lastCode;
  47. }
  48. if (part.length)
  49. result.push(part);
  50. return result;
  51. }
  52. // Sutherland-Hodgeman polygon clipping algorithm
  53. export function polygonclip(points, bbox) {
  54. var result, edge, prev, prevInside, i, p, inside;
  55. // clip against each side of the clip rectangle
  56. for (edge = 1; edge <= 8; edge *= 2) {
  57. result = [];
  58. prev = points[points.length - 1];
  59. prevInside = !(bitCode(prev, bbox) & edge);
  60. for (i = 0; i < points.length; i++) {
  61. p = points[i];
  62. inside = !(bitCode(p, bbox) & edge);
  63. // if segment goes through the clip window, add an intersection
  64. if (inside !== prevInside)
  65. result.push(intersect(prev, p, edge, bbox));
  66. if (inside)
  67. result.push(p); // add a point if it's inside
  68. prev = p;
  69. prevInside = inside;
  70. }
  71. points = result;
  72. if (!points.length)
  73. break;
  74. }
  75. return result;
  76. }
  77. // intersect a segment against one of the 4 lines that make up the bbox
  78. function intersect(a, b, edge, bbox) {
  79. return edge & 8
  80. ? [a[0] + ((b[0] - a[0]) * (bbox[3] - a[1])) / (b[1] - a[1]), bbox[3]] // top
  81. : edge & 4
  82. ? [a[0] + ((b[0] - a[0]) * (bbox[1] - a[1])) / (b[1] - a[1]), bbox[1]] // bottom
  83. : edge & 2
  84. ? [bbox[2], a[1] + ((b[1] - a[1]) * (bbox[2] - a[0])) / (b[0] - a[0])] // right
  85. : edge & 1
  86. ? [bbox[0], a[1] + ((b[1] - a[1]) * (bbox[0] - a[0])) / (b[0] - a[0])] // left
  87. : null;
  88. }
  89. // bit code reflects the point position relative to the bbox:
  90. // left mid right
  91. // top 1001 1000 1010
  92. // mid 0001 0000 0010
  93. // bottom 0101 0100 0110
  94. function bitCode(p, bbox) {
  95. var code = 0;
  96. if (p[0] < bbox[0])
  97. code |= 1;
  98. // left
  99. else if (p[0] > bbox[2])
  100. code |= 2; // right
  101. if (p[1] < bbox[1])
  102. code |= 4;
  103. // bottom
  104. else if (p[1] > bbox[3])
  105. code |= 8; // top
  106. return code;
  107. }