Skip to content

Commit 4841caa

Browse files
authored
fix: two issues related to point filtering (#181)
1 parent 1130eb4 commit 4841caa

3 files changed

Lines changed: 103 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 1.9.4
2+
3+
- Fix: `scatterplot.draw(newPoints, { preventFilterReset })` should preserves the filter ([Jupyter-Scatter#134](https://github.com/flekschas/jupyter-scatter/issues/134))
4+
- Fix: `scatterplot.hover(filteredOutPoint)` should not trigger a hover event
5+
16
## 1.9.3
27

38
- Fix: point connection initial color map

src/index.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,9 @@ const createScatterplot = (
772772
) => {
773773
let needsRedraw = false;
774774

775-
if (point >= 0 && point < numPoints) {
775+
const isFilteredOut = isPointsFiltered && !filteredPointsSet.has(point);
776+
777+
if (!isFilteredOut && point >= 0 && point < numPoints) {
776778
needsRedraw = true;
777779
const oldHoveredPoint = hoveredPoint;
778780
const newHoveredPoint = point !== hoveredPoint;
@@ -1796,6 +1798,9 @@ const createScatterplot = (
17961798
new Promise((resolve) => {
17971799
isPointsDrawn = false;
17981800

1801+
const preventFilterReset =
1802+
options?.preventFilterReset && newPoints.length === numPoints;
1803+
17991804
numPoints = newPoints.length;
18001805
numPointsInView = numPoints;
18011806

@@ -1805,11 +1810,13 @@ const createScatterplot = (
18051810
w: options.wDataType,
18061811
});
18071812

1808-
normalPointsIndexBuffer({
1809-
usage: 'static',
1810-
type: 'float',
1811-
data: createPointIndex(numPoints),
1812-
});
1813+
if (!preventFilterReset) {
1814+
normalPointsIndexBuffer({
1815+
usage: 'static',
1816+
type: 'float',
1817+
data: createPointIndex(numPoints),
1818+
});
1819+
}
18131820

18141821
createKdbush(options.spatialIndex || newPoints, {
18151822
useWorker: spatialIndexUseWorker,
@@ -2275,6 +2282,7 @@ const createScatterplot = (
22752282
setPoints(newPointsArray, {
22762283
zDataType,
22772284
wDataType,
2285+
preventFilterReset: options.preventFilterReset,
22782286
spatialIndex: options.spatialIndex,
22792287
}).then(() => {
22802288
if (options.hover !== undefined) {

tests/index.js

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2257,10 +2257,10 @@ test('other methods', async (t2) => {
22572257

22582258
const points = [
22592259
[0, 0],
2260-
[1, 1],
2261-
[1, -1],
2262-
[-1, -1],
2263-
[-1, 1],
2260+
[0.9, 0.9],
2261+
[0.9, -0.9],
2262+
[-0.9, -0.9],
2263+
[-0.9, 0.9],
22642264
];
22652265

22662266
await scatterplot.draw(points);
@@ -2273,9 +2273,36 @@ test('other methods', async (t2) => {
22732273
`only points ${filteredPoints} should be filtered in`
22742274
);
22752275

2276-
const updatedPoints = points.map(([x, y]) => [x + 1, y + 1]);
2276+
const getPixelSum = (img, xStart, xEnd, yStart, yEnd) => {
2277+
let pixelSum = 0;
2278+
for (let i = yStart; i < yEnd; i++) {
2279+
for (let j = xStart; j < xEnd; j++) {
2280+
const idx = (i * img.width + j) * 4;
2281+
pixelSum +=
2282+
img.data[idx] +
2283+
img.data[idx + 1] +
2284+
img.data[idx + 2] +
2285+
img.data[idx + 3];
2286+
}
2287+
}
2288+
return pixelSum;
2289+
};
22772290

2278-
await scatterplot.draw(updatedPoints, { preventFilterReset: true });
2291+
let img = scatterplot.export();
2292+
2293+
t.equal(
2294+
getPixelSum(img, 0, Math.ceil(img.width / 3), 0, img.height),
2295+
0,
2296+
'The left side of the image should be empty as points #3 and #4 are filtered out'
2297+
);
2298+
2299+
t.ok(
2300+
getPixelSum(img, Math.ceil(img.width / 3), img.width, 0, img.height) >
2301+
0,
2302+
'The right side of the image should *not* be empty as points #0 to #2 are filtered in'
2303+
);
2304+
2305+
await scatterplot.draw([...points], { preventFilterReset: true });
22792306

22802307
t.equal(
22812308
scatterplot.get('isPointsFiltered'),
@@ -2288,7 +2315,21 @@ test('other methods', async (t2) => {
22882315
'the filtered points should be the same as before'
22892316
);
22902317

2291-
await scatterplot.draw([...updatedPoints, [0.5, 0.5]], {
2318+
img = scatterplot.export();
2319+
2320+
t.equal(
2321+
getPixelSum(img, 0, Math.ceil(img.width / 3), 0, img.height),
2322+
0,
2323+
'The left side of the image should be empty as points #3 and #4 are filtered out'
2324+
);
2325+
2326+
t.ok(
2327+
getPixelSum(img, Math.ceil(img.width / 3), img.width, 0, img.height) >
2328+
0,
2329+
'The right side of the image should *not* be empty as points #0 to #2 are filtered in'
2330+
);
2331+
2332+
await scatterplot.draw([...points, [0.5, 0.5]], {
22922333
preventFilterReset: true,
22932334
});
22942335

@@ -2300,10 +2341,17 @@ test('other methods', async (t2) => {
23002341

23012342
t.equal(
23022343
scatterplot.get('filteredPoints').length,
2303-
updatedPoints.length + 1,
2344+
points.length + 1,
23042345
'the filtered points should be reset as draw has been invoked with different number of points'
23052346
);
23062347

2348+
img = scatterplot.export();
2349+
2350+
t.ok(
2351+
getPixelSum(img, 0, Math.ceil(img.width / 3), 0, img.height) > 0,
2352+
'The left side of the image should *not* be empty as the filter was reset'
2353+
);
2354+
23072355
scatterplot.destroy();
23082356
})
23092357
);
@@ -2532,6 +2580,16 @@ test('other methods', async (t2) => {
25322580
scatterplot.subscribe('filter', filterHandler);
25332581
scatterplot.subscribe('unfilter', unfilterHandler);
25342582

2583+
let hoveredPoint;
2584+
const pointOverHandler = (pointIdx) => {
2585+
hoveredPoint = pointIdx;
2586+
};
2587+
const pointOutHandler = () => {
2588+
hoveredPoint = undefined;
2589+
};
2590+
scatterplot.subscribe('pointover', pointOverHandler);
2591+
scatterplot.subscribe('pointout', pointOutHandler);
2592+
25352593
await scatterplot.filter([1, 3]);
25362594
await wait(0);
25372595

@@ -2556,6 +2614,24 @@ test('other methods', async (t2) => {
25562614
'should be able to retrieve the filtered points'
25572615
);
25582616

2617+
scatterplot.hover(1);
2618+
await wait(0);
2619+
2620+
t.equal(
2621+
hoveredPoint,
2622+
1,
2623+
'should be able to hover a point that is filtered in'
2624+
);
2625+
2626+
scatterplot.hover(2);
2627+
await wait(0);
2628+
2629+
t.equal(
2630+
hoveredPoint,
2631+
undefined,
2632+
'should not be able to hover a point that is filtered out'
2633+
);
2634+
25592635
let selectedPoints = [];
25602636
const selectHandler = ({ points: newSelectedPoints }) => {
25612637
selectedPoints = [...newSelectedPoints];

0 commit comments

Comments
 (0)