diff --git a/src/interactions/pointer.js b/src/interactions/pointer.js
index 72f1b2d7a8..ef3f97b294 100644
--- a/src/interactions/pointer.js
+++ b/src/interactions/pointer.js
@@ -179,10 +179,18 @@ export function pointerY(options) {
return pointerK(0.01, 1, options);
}
-export function anchorX({x1: X1, x2: X2, x: X = X1}, cx) {
- return X1 && X2 ? (i) => (X1[i] + X2[i]) / 2 : X ? (i) => X[i] : () => cx;
+function anchorK(k, values, c) {
+ let {[`${k}1`]: V1, [`${k}2`]: V2, [k]: V, channels} = values;
+ if (V1 && channels[`${k}1`].hint?.singleton) V1 = null;
+ if (V2 && channels[`${k}2`].hint?.singleton) V2 = null;
+ if (V === undefined) V = V1;
+ return V1 && V2 ? (i) => (V1[i] + V2[i]) / 2 : V ? (i) => V[i] : () => c;
}
-export function anchorY({y1: Y1, y2: Y2, y: Y = Y1}, cy) {
- return Y1 && Y2 ? (i) => (Y1[i] + Y2[i]) / 2 : Y ? (i) => Y[i] : () => cy;
+export function anchorX(values, cx) {
+ return anchorK("x", values, cx);
+}
+
+export function anchorY(values, cy) {
+ return anchorK("y", values, cy);
}
diff --git a/src/marks/raster.js b/src/marks/raster.js
index fd601e3ced..f8f90c06c6 100644
--- a/src/marks/raster.js
+++ b/src/marks/raster.js
@@ -7,6 +7,9 @@ import {Mark} from "../mark.js";
import {applyAttr, applyDirectStyles, applyIndirectStyles, applyTransform, impliedString} from "../style.js";
import {initializer} from "../transforms/basic.js";
+// A hint for the pointer interaction…
+const singleton = {singleton: true};
+
const defaults = {
ariaLabel: "raster",
stroke: null,
@@ -65,10 +68,10 @@ export class AbstractRaster extends Mark {
{
x: {value: x, scale: "x", optional: true},
y: {value: y, scale: "y", optional: true},
- x1: {value: x1 == null ? null : [x1], scale: "x", optional: true, filter: null},
- y1: {value: y1 == null ? null : [y1], scale: "y", optional: true, filter: null},
- x2: {value: x2 == null ? null : [x2], scale: "x", optional: true, filter: null},
- y2: {value: y2 == null ? null : [y2], scale: "y", optional: true, filter: null},
+ x1: {value: x1 == null ? null : [x1], scale: "x", optional: true, filter: null, hint: singleton},
+ y1: {value: y1 == null ? null : [y1], scale: "y", optional: true, filter: null, hint: singleton},
+ x2: {value: x2 == null ? null : [x2], scale: "x", optional: true, filter: null, hint: singleton},
+ y2: {value: y2 == null ? null : [y2], scale: "y", optional: true, filter: null, hint: singleton},
...channels
},
options,
diff --git a/test/output/tipRasterDense.svg b/test/output/tipRasterDense.svg
new file mode 100644
index 0000000000..8e8f919f3b
--- /dev/null
+++ b/test/output/tipRasterDense.svg
@@ -0,0 +1,73 @@
+
\ No newline at end of file
diff --git a/test/plots/tip.ts b/test/plots/tip.ts
index 3e645005af..07de450112 100644
--- a/test/plots/tip.ts
+++ b/test/plots/tip.ts
@@ -179,6 +179,13 @@ export async function tipRaster() {
});
}
+export async function tipRasterDense() {
+ const volcano = await d3.json("data/volcano.json");
+ return Plot.plot({
+ marks: [Plot.raster(volcano.values, {width: volcano.width, height: volcano.height, tip: true}), Plot.frame()]
+ });
+}
+
export async function tipRule() {
const penguins = await d3.csv("data/penguins.csv", d3.autoType);
return Plot.ruleX(penguins, {x: "body_mass_g", tip: true}).plot();