Skip to content

Commit 4823187

Browse files
Filmbostock
authored andcommitted
when the pointer is outside the frame, filter out the winning values that are further than the euclidian distance
this solves the weirdness of pointer selection detailed in #1777 (comment)
1 parent e8ab373 commit 4823187

File tree

3 files changed

+235
-3
lines changed

3 files changed

+235
-3
lines changed

src/interactions/pointer.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,17 @@ function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, render, ...op
132132

133133
// Select the closest point to the mouse in the current facet; for
134134
// pointerX or pointerY, the orthogonal component of the distance is
135-
// squashed, selecting primarily on the dominant dimension. Return the
136-
// usual euclidian distance to determine the winner across facets.
135+
// squashed, selecting primarily on the dominant dimension. Across facets,
136+
// use the euclidian distance to determine the winner. The current facet’s
137+
// surface includes the frame, margins and pointer radius.
137138
function pointermove(event) {
138139
if (state.sticky || (event.pointerType === "mouse" && event.buttons === 1)) return; // dragging
139140
let [xp, yp] = pointof(event);
141+
const hw = dimensions.width / 2;
142+
const hh = dimensions.height / 2;
143+
const offFrame =
144+
(kx !== 1 && fx && Math.abs(xp - (fx(index.fx) + hw)) > hw + maxRadius) ||
145+
(ky !== 1 && fy && Math.abs(yp - (fy(index.fy) + hh)) > hh + maxRadius);
140146
(xp -= tx), (yp -= ty); // correct for facets and band scales
141147
let ii = null;
142148
let ri = maxRadius * maxRadius;
@@ -146,7 +152,13 @@ function pointerK(kx, ky, {x, y, px, py, maxRadius = 40, channels, render, ...op
146152
const rj = dx * dx + dy * dy;
147153
if (rj <= ri) (ii = j), (ri = rj);
148154
}
149-
update(ii, ii != null && Math.hypot(px(ii) - xp, py(ii) - yp));
155+
if (ii != null) {
156+
const dx = px(ii) - xp;
157+
const dy = py(ii) - yp;
158+
ri = dx * dx + dy * dy;
159+
if (offFrame && ri > maxRadius * maxRadius) ii = null;
160+
}
161+
update(ii, ri);
150162
}
151163

152164
function pointerdown(event) {

test/output/tipFacetX.svg

Lines changed: 201 additions & 0 deletions
Loading

test/plots/tip.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,22 @@ export async function tipTransform() {
237237
marks: [Plot.dotX([0, 0.1, 0.3, 1], {fill: Plot.identity, r: 10, frameAnchor: "middle", tip: true})]
238238
});
239239
}
240+
241+
export async function tipFacetX() {
242+
const data = d3.range(100).map((i) => ({f: i > 60 || i % 2 ? "b" : "a", x: i, y: i / 10}));
243+
return Plot.plot({
244+
inset: 10,
245+
y: {domain: [0, 7]},
246+
marks: [
247+
Plot.frame(),
248+
Plot.dot(data, {fy: "f", x: "x", y: "y", tip: "x", fill: "f"}),
249+
Plot.dot(
250+
[
251+
{f: "a", y: 3},
252+
{f: "b", y: 1}
253+
],
254+
{fy: "f", x: 90, y: "y", r: 30, fill: "f", fillOpacity: 0.1, stroke: "currentColor", strokeDasharray: 4}
255+
)
256+
]
257+
});
258+
}

0 commit comments

Comments
 (0)