-
Notifications
You must be signed in to change notification settings - Fork 743
/
split-rects.js
76 lines (68 loc) · 2.23 KB
/
split-rects.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* Given an outer rect, and a list of rects that overlap with it, find any rectangular
* space that does not overlap.
* @method getOffset
* @memberof axe.commons.math
* @param {DOMRect} outerRect
* @param {DOMRect[]} overlapRects
* @returns {DOMRect[]} Unique array of rects
*/
export default function splitRects(outerRect, overlapRects) {
let uniqueRects = [outerRect];
for (const overlapRect of overlapRects) {
uniqueRects = uniqueRects.reduce((rects, inputRect) => {
return rects.concat(splitRect(inputRect, overlapRect));
}, []);
// exit early if we get too many rects that it starts causing
// a performance bottleneck
// @see https://github.com/dequelabs/axe-core/issues/4359
if (uniqueRects.length > 4000) {
return [];
}
}
return uniqueRects;
}
// Cut the input rect along any intersecting edge of the clip rect.
function splitRect(inputRect, clipRect) {
const { top, left, bottom, right } = inputRect;
const yAligned = top < clipRect.bottom && bottom > clipRect.top;
const xAligned = left < clipRect.right && right > clipRect.left;
const rects = [];
if (between(clipRect.top, top, bottom) && xAligned) {
rects.push({ top, left, bottom: clipRect.top, right });
}
if (between(clipRect.right, left, right) && yAligned) {
rects.push({ top, left: clipRect.right, bottom, right });
}
if (between(clipRect.bottom, top, bottom) && xAligned) {
rects.push({ top: clipRect.bottom, right, bottom, left });
}
if (between(clipRect.left, left, right) && yAligned) {
rects.push({ top, left, bottom, right: clipRect.left });
}
if (rects.length === 0) {
// Fully overlapping
if (isEnclosedRect(inputRect, clipRect)) {
return [];
}
rects.push(inputRect); // No intersection
}
return rects.map(computeRect); // add x / y / width / height
}
const between = (num, min, max) => num > min && num < max;
function computeRect(baseRect) {
return new window.DOMRect(
baseRect.left,
baseRect.top,
baseRect.right - baseRect.left,
baseRect.bottom - baseRect.top
);
}
function isEnclosedRect(rectA, rectB) {
return (
rectA.top >= rectB.top &&
rectA.left >= rectB.left &&
rectA.bottom <= rectB.bottom &&
rectA.right <= rectB.right
);
}