Data Point interaction with a Voronoi diagram

While a major advantage of SVG is that event handlers can be attached to any element, in some cases it may be simpler (and more performant) to attach handlers to the parent Chart element, and map back to the nearest data point using a Voronoi diagram.

Hypocube provides a hook for this purpose. When supplied with a set of data series and a callback, it remaps the data in the callback to the data for the nearest data point (and meta data) in the set of series. It returns an event handler to be attached to the Chart (not the series). This works even when the Chart is rendered in canvas mode.

To demonstrate how this works, we'll rebuild the Tooltip example from the previous article to use the Voronoi hook instead. It should work in a similar way, except that every part of the chart area will map the nearest data point.

It's good practice to construct the data above the component that will render the Chart, to avoid re-processing it on each render.

const myData = [
{
data: [
[1, 3],
[2, 2],
[3, 5],
],
meta: {
color: 'green',
},
},
{
data: [
[1, 1],
[2, 3],
[3, 4],
],
meta: {
color: 'blue',
},
},
];

In our chart component, we accept the data as a prop to use it construct the Voronoi and draw our data series.

const MyChart = (data) => {
const [tooltipData, setTooltipData, handleCloseTooltip] = useTooltip();
const handleSelectPoint = useVoronoi(data, setTooltipData);
return (
<Chart
height={300}
view={[0, 0, 4, 5]}
gutter={[5, 5, 5, 5]}
chartStyle={{
dataPointSymbol: 'circle',
dataPointMinTargetRadius: 20,
}}
htmlLayer={
tooltipData
? {
position: tooltipData.position,
render: (
<SimpleTooltip
seriesColor={tooltipData.meta.seriesColor}
position={tooltipData.position}
onRequestClose={handleCloseTooltip}
/>
),
}
: null
}
onPointerDown={handleSelectPoint}
>
<XAxis />
<YAxis />
{data.map((series) => (
<LineSeries
data={series.data}
chartStyle={{
dataLineStroke: series.meta.color,
dataPointFill: series.meta.color,
}}
/>
))}
</Chart>
);
};

Deferred calculation of the diagram

To speed up chart rendering, useVoronoi delays setting the needed diagram based on window.requestIdleCallback. If the diagram has not yet been set, the event will still fire, but elementPosition will be undefined.

If desired, a timeout for setting the Voronoi diagram can be supplied as a third argument.

const handler = useVoronoi(myData, myHandler, 1000);

The Voronoi diagram will only be recalculated (and the handler will only update) when the data changes.