From 31e90d7fe8627d4285da37367ee7d6737fac461b Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Sat, 27 May 2023 17:03:38 -0700 Subject: [PATCH] tip for dense raster --- src/interactions/pointer.js | 16 ++++++-- src/marks/raster.js | 11 +++-- test/output/tipRasterDense.svg | 73 ++++++++++++++++++++++++++++++++++ test/plots/tip.ts | 7 ++++ 4 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 test/output/tipRasterDense.svg 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 @@ + + + + + + + + + + + + + + + + + + + 0 + 5 + 10 + 15 + 20 + 25 + 30 + 35 + 40 + 45 + 50 + 55 + 60 + + + + + + + + + + + + + + 0 + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + + + + + + + \ 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();