Skip to content

Commit 0d8a300

Browse files
authored
Merge pull request #20353 from calixteman/improve_intersector
[Annotation] Improve the performance of the code for getting glyphs which belongs to annotations bounding boxes (bug 1987914)
2 parents c8d8f9f + c4d4367 commit 0d8a300

1 file changed

Lines changed: 69 additions & 48 deletions

File tree

src/core/intersector.js

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
class SingleIntersector {
1717
#annotation;
1818

19-
#minX = Infinity;
19+
minX = Infinity;
2020

21-
#minY = Infinity;
21+
minY = Infinity;
2222

23-
#maxX = -Infinity;
23+
maxX = -Infinity;
2424

25-
#maxY = -Infinity;
25+
maxY = -Infinity;
2626

2727
#quadPoints = null;
2828

@@ -40,30 +40,21 @@ class SingleIntersector {
4040
if (!quadPoints) {
4141
// If there are no quad points, we use the rectangle to determine the
4242
// bounds of the annotation.
43-
[this.#minX, this.#minY, this.#maxX, this.#maxY] = annotation.data.rect;
43+
[this.minX, this.minY, this.maxX, this.maxY] = annotation.data.rect;
4444
return;
4545
}
4646

4747
for (let i = 0, ii = quadPoints.length; i < ii; i += 8) {
48-
this.#minX = Math.min(this.#minX, quadPoints[i]);
49-
this.#maxX = Math.max(this.#maxX, quadPoints[i + 2]);
50-
this.#minY = Math.min(this.#minY, quadPoints[i + 5]);
51-
this.#maxY = Math.max(this.#maxY, quadPoints[i + 1]);
48+
this.minX = Math.min(this.minX, quadPoints[i]);
49+
this.maxX = Math.max(this.maxX, quadPoints[i + 2]);
50+
this.minY = Math.min(this.minY, quadPoints[i + 5]);
51+
this.maxY = Math.max(this.maxY, quadPoints[i + 1]);
5252
}
5353
if (quadPoints.length > 8) {
5454
this.#quadPoints = quadPoints;
5555
}
5656
}
5757

58-
overlaps(other) {
59-
return !(
60-
this.#minX >= other.#maxX ||
61-
this.#maxX <= other.#minX ||
62-
this.#minY >= other.#maxY ||
63-
this.#maxY <= other.#minY
64-
);
65-
}
66-
6758
/**
6859
* Check if the given point intersects with the annotation's quad points.
6960
* The point (x, y) is supposed to be the center of the glyph.
@@ -72,12 +63,7 @@ class SingleIntersector {
7263
* @returns {boolean}
7364
*/
7465
#intersects(x, y) {
75-
if (
76-
this.#minX >= x ||
77-
this.#maxX <= x ||
78-
this.#minY >= y ||
79-
this.#maxY <= y
80-
) {
66+
if (this.minX >= x || this.maxX <= x || this.minY >= y || this.maxY <= y) {
8167
return false;
8268
}
8369

@@ -154,56 +140,91 @@ class SingleIntersector {
154140
}
155141
}
156142

143+
// The grid is STEPS x STEPS.
144+
const STEPS = 64;
145+
157146
class Intersector {
158-
#intersectors = new Map();
147+
#intersectors = [];
148+
149+
#grid = [];
150+
151+
#minX;
152+
153+
#minY;
154+
155+
#invXRatio;
156+
157+
#invYRatio;
159158

160159
constructor(annotations) {
160+
let minX = Infinity;
161+
let minY = Infinity;
162+
let maxX = -Infinity;
163+
let maxY = -Infinity;
164+
const intersectors = this.#intersectors;
161165
for (const annotation of annotations) {
162166
if (!annotation.data.quadPoints && !annotation.data.rect) {
163167
continue;
164168
}
165169
const intersector = new SingleIntersector(annotation);
166-
for (const [otherIntersector, overlapping] of this.#intersectors) {
167-
if (otherIntersector.overlaps(intersector)) {
168-
if (!overlapping) {
169-
this.#intersectors.set(otherIntersector, new Set([intersector]));
170-
} else {
171-
overlapping.add(intersector);
170+
intersectors.push(intersector);
171+
minX = Math.min(minX, intersector.minX);
172+
minY = Math.min(minY, intersector.minY);
173+
maxX = Math.max(maxX, intersector.maxX);
174+
maxY = Math.max(maxY, intersector.maxY);
175+
}
176+
this.#minX = minX;
177+
this.#minY = minY;
178+
this.#invXRatio = (STEPS - 1) / (maxX - minX);
179+
this.#invYRatio = (STEPS - 1) / (maxY - minY);
180+
for (const intersector of intersectors) {
181+
const iMin = this.#getGridIndex(intersector.minX, intersector.minY);
182+
const iMax = this.#getGridIndex(intersector.maxX, intersector.maxY);
183+
const w = (iMax - iMin) % STEPS;
184+
const h = Math.floor((iMax - iMin) / STEPS);
185+
for (let i = iMin; i <= iMin + h * STEPS; i += STEPS) {
186+
for (let j = 0; j <= w; j++) {
187+
let existing = this.#grid[i + j];
188+
if (!existing) {
189+
this.#grid[i + j] = existing = [];
172190
}
191+
existing.push(intersector);
173192
}
174193
}
175-
this.#intersectors.set(intersector, null);
176194
}
177195
}
178196

197+
#getGridIndex(x, y) {
198+
const i = Math.floor((x - this.#minX) * this.#invXRatio);
199+
const j = Math.floor((y - this.#minY) * this.#invYRatio);
200+
return i >= 0 && i < STEPS && j >= 0 && j < STEPS ? i + j * STEPS : -1;
201+
}
202+
179203
addGlyph(transform, width, height, glyph) {
180204
const x = transform[4] + width / 2;
181205
const y = transform[5] + height / 2;
182-
let overlappingIntersectors;
183-
for (const [intersector, overlapping] of this.#intersectors) {
184-
if (overlappingIntersectors) {
185-
if (overlappingIntersectors.has(intersector)) {
186-
intersector.addGlyph(x, y, glyph);
187-
} else {
188-
intersector.disableExtraChars();
189-
}
190-
continue;
191-
}
192-
if (!intersector.addGlyph(x, y, glyph)) {
193-
continue;
194-
}
195-
overlappingIntersectors = overlapping;
206+
const index = this.#getGridIndex(x, y);
207+
if (index < 0) {
208+
return;
209+
}
210+
const intersectors = this.#grid[index];
211+
if (!intersectors) {
212+
return;
213+
}
214+
215+
for (const intersector of intersectors) {
216+
intersector.addGlyph(x, y, glyph);
196217
}
197218
}
198219

199220
addExtraChar(char) {
200-
for (const intersector of this.#intersectors.keys()) {
221+
for (const intersector of this.#intersectors) {
201222
intersector.addExtraChar(char);
202223
}
203224
}
204225

205226
setText() {
206-
for (const intersector of this.#intersectors.keys()) {
227+
for (const intersector of this.#intersectors) {
207228
intersector.setText();
208229
}
209230
}

0 commit comments

Comments
 (0)