|
1 |
| -const IGNORE_PERCENT_THRESHOLD = 0.8; // Ignore elements that cover at least this % of the region |
| 1 | +const APPEARANCE_THRESHOLD = 0.75; // How much of a line needs to appear in that element to consider it |
| 2 | + |
| 3 | +const IGNORE_PERCENT_THRESHOLD = 0.5; // Ignore elements that cover at least this % of the region |
2 | 4 | const SPLIT_SIZE = 10; // Split into 2 sub regions (vertically, horizontal shouldn't be common) when one contains this many elements
|
3 | 5 |
|
| 6 | +/** |
| 7 | + * Gets the area of the element |
| 8 | + * @param {Element} elem |
| 9 | + * @returns {number} |
| 10 | + */ |
| 11 | +function getArea(elem) { |
| 12 | + return ( |
| 13 | + elem.getBoundingClientRect().width * elem.getBoundingClientRect().height |
| 14 | + ); |
| 15 | +} |
| 16 | + |
4 | 17 | /**
|
5 | 18 | * Gets the top of the element, in pixels relative to the page.
|
6 | 19 | * @param {Element} elem
|
@@ -55,22 +68,19 @@ class PointResolver {
|
55 | 68 | if (minorElems.length < SPLIT_SIZE) {
|
56 | 69 | this.elements = this.elements.concat(minorElems);
|
57 | 70 | } else {
|
58 |
| - // Need to go to sub resolvers for minor elements. Just sort + take half in each |
59 |
| - const sorted = minorElems.sort( |
60 |
| - (a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top |
| 71 | + // Need to go to sub resolvers for minor elements. |
| 72 | + const midpoint = (top + bottom) / 2; |
| 73 | + const firstHalf = elements.filter(e => elemTop(e) < midpoint); |
| 74 | + const secondHalf = elements.filter( |
| 75 | + e => |
| 76 | + e.getBoundingClientRect().bottom + |
| 77 | + document.documentElement.scrollTop >= |
| 78 | + midpoint |
61 | 79 | );
|
62 |
| - const midway = Math.floor(sorted.length / 2); |
| 80 | + |
63 | 81 | this.subResolvers = [
|
64 |
| - new PointResolver( |
65 |
| - sorted.slice(0, midway), |
66 |
| - top, |
67 |
| - elemTop(sorted[midway]) |
68 |
| - ), |
69 |
| - new PointResolver( |
70 |
| - sorted.slice(midway, sorted.length), |
71 |
| - elemTop(sorted[midway]) + 1, |
72 |
| - bottom |
73 |
| - ), |
| 82 | + new PointResolver(firstHalf, top, midpoint), |
| 83 | + new PointResolver(secondHalf, midpoint + 1, bottom), |
74 | 84 | ];
|
75 | 85 | }
|
76 | 86 | }
|
@@ -147,16 +157,33 @@ export class DoodleElementFinder {
|
147 | 157 | /**
|
148 | 158 | * Returns all Elements that the line overlaps
|
149 | 159 | * @param {import("../types/api").DoodleLine} line
|
150 |
| - * @returns {Element[]} |
| 160 | + * @returns {Element} |
151 | 161 | */
|
152 | 162 | resolveLine(line) {
|
153 |
| - // Resolve for each point in the line, removing duplicates. |
154 |
| - // In a later sprint we can revisit this and potentially ignore some elements that aren't common |
155 |
| - // but for now, this is good enough. |
156 |
| - let elems = new Set(); |
| 163 | + // Map from element to count of times that element has been doodled over |
| 164 | + /** @type {Map<Element, number>} */ |
| 165 | + let elems = new Map(); |
157 | 166 | line.points.forEach(point => {
|
158 |
| - this.resolvePoint(point[0], point[1]).forEach(elems.add); |
| 167 | + this.resolvePoint(point[0], point[1]).forEach(elem => { |
| 168 | + if (!elems.has(elem)) { |
| 169 | + elems.set(elem, 0); |
| 170 | + } |
| 171 | + // @ts-ignore .get() can't be undefined, as we just set it. |
| 172 | + elems.set(elem, elems.get(elem) + 1); |
| 173 | + }); |
159 | 174 | });
|
160 |
| - return Array.from(elems); |
| 175 | + // Sort elements by appearance count, largest to smallest |
| 176 | + const sortedElems = Array.from(elems.entries()).sort((a, b) => b[1] - a[1]); |
| 177 | + // Get ones that appear enough times, and map back down to only element, removing count |
| 178 | + const overAppearanceThreshold = sortedElems |
| 179 | + .filter(entry => entry[1] >= APPEARANCE_THRESHOLD * line.points.length) |
| 180 | + .map(elem => elem[0]); |
| 181 | + if (overAppearanceThreshold.length === 0) { |
| 182 | + return sortedElems[0][0]; |
| 183 | + } |
| 184 | + |
| 185 | + return overAppearanceThreshold.reduce((prev, curr) => { |
| 186 | + return getArea(prev) <= getArea(curr) ? prev : curr; |
| 187 | + }, overAppearanceThreshold[0]); |
161 | 188 | }
|
162 | 189 | }
|
0 commit comments