D3 plugin which computes a Voronoi tesselation where each cell defines a region inside a given distance.
Because a picture is worth a thousand words:
This page (as the all master branch) concerns the plugin's version compatible with d3v4. Switch to the d3v3 branch for the version compatible with d3v3.
As stated in the first sentence of the README file of the d3-voronoi repository:
Voronoi layouts are particularly useful for invisible interactive regions, as demonstrated in Nate Vack’s Voronoi picking example
But this cited example also shows that interactive regions should be close to each point/subjectOfMatter. In other words, if the interactive region is far away from the subject of matter, interaction becomes confusing.
In its example, Nate Vack uses SVG's clipPath technique to cut off Voronoï-based interactive regions. This plugin mimic the final result by computing the adequate distance-limited region around each subject of matter. The adequate region is the intersection area between the Voronoï cell and a max-distance circle.
Finally, I highly encourage everyone to also take a look at Using a D3 Voronoi grid to improve a chart's interactive experience, from Nadieh Bremer (visualcinnamon.com), where everyone will find a step-by-step use of this technique on a concrete use case.
- This block is an update of Nate Vack’s Voronoi picking example, using the d3-distanceLimitedVoronoi plugin
- This block uses the d3-distanceLimitedVoronoi plugin in a real case study
- Using a D3 Voronoi grid to improve a chart's interactive experience from Nadieh Bremer (visualcinnamon.com)
In your HTML file, load the plugin after loading D3. The result may look like:
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://rawgit.com/Kcnarf/d3-distanceLimitedVoronoi/d3v4/distance-limited-voronoi.js"></script>
In your javascript, in order to define the layout:
var limitedVoronoi = d3.distanceLimitedVoronoi()
.x(...) // set the x accessor (as in d3.voronoi)
.y(...) // set the y accessor (as in d3.voronoi)
.limit(20) // set the maximum distance
var limitedCells = limitedVoronoi(data) // compute the layout; return an array of {path: , datum: }
// where 'path' is the adequate region around the datum
// and 'datum' is the datum
Later, in your javascript, in order to draw the (interactive) regions on an SVG:
d3.selectAll(".interactive-region")
.data(limitedCells)
.enter()
.append("path")
.attr("d", function(d) { return d.path; })
.on('mouseenter', ...)
.on('mouseout', ...)
, or in order to draw the regions on a Canvas:
var canvas = document.querySelector("#my-canvas");
var context = canvas.getContext("2d");
limitedVoronoi.context(context); //set the context to render to
context.strokeStyle = 'lightblue';
context.beginPath();
limitedVoronoi(data);
context.stroke();
- d3-voronoi: https://github.com/d3/d3-voronoi/
# d3.distanceLimitedVoronoi()
Creates a new distanceLimitedVoronoi diagram with the default settings:
voronoi = d3.voronoi().extent([[-1e6,-1e6], [1e6,1e6]]);
limit = 20;
context = null; // set it to render to a canvas' 2D context
# distanceLimitedVoronoi(data)
Computes the distanceLimitedVoronoi tesselation for the specified data points.
If the context of the layout is null, returns an array of {path: , datum: }
, where path
is the adequate region around the datum and datum
is the datum.
If the context of the layout is defined, the layout is supposed to be drawn on a Canvas. Hence the layout defines a new path, composed of each adequate regions, and return true
. Note that the layout doesn't stroke()
(or fill()
, or anything else ...) on its own in the case you don't want to fill regions. The use of the produced path remains at your charge.
# distanceLimitedVoronoi.limit([radius])
If radius is specified, set the limit (ie. maximum distance) of each cell and returns this distanceLimitedVoronoi. If radius is not specified, return the current limit, which defaults to 20
.
# distanceLimitedVoronoi.voronoi([voronoi])
If voronoi is specified, set the voronoi layout used by the distanceLimitedVoronoi and returns it. If voronoi is not specified, return the currently used voronoi, which defaults to d3.voronoi().extent([[-1e6,-1e6], [1e6,1e6]])
.
# distanceLimitedVoronoi.context([context])
If context is specified, set the context used by the distanceLimitedVoronoi to draw each distance-limited cell, and returns it. The context must be a 2D canvas context (for canvas rendering), or null
(for SVG rendering). If context is not specified, return the currently used context, which defaults to null
.
# distanceLimitedVoronoi.x([callback])
Exposes distanceLimitedVoronoi.voronoi().x(...)
If callback is specified, set the x-coordinate accessor and returns this distanceLimitedVoronoi. If callback is not specified, return the current x-coordinate accessor, which defaults to function(d) { return d[0]; }
.
# distanceLimitedVoronoi.y([callback])
Exposes distanceLimitedVoronoi.voronoi().y(...)
If callback is specified, set the y-coordinate accessor and returns this distanceLimitedVoronoi. If callback is not specified, return the current y-coordinate accessor, which defaults to function(d) { return d[1]; }
.
# distanceLimitedVoronoi.extent([extent])
Exposes distanceLimitedVoronoi.voronoi().extent(...)
If extent is specified, set the clip extent of the layout to the specified bounds and returns this distanceLimitedVoronoi. The extent bounds are specified as an array [[x0, y0], [x1, y1]], where x0 is the left side of the extent, y0 is the top, x1 is the right and y1 is the bottom. If extent is not specified, return the current clip extent, which defaults to [[-1e6, -1e6], [1e6,1e6]]
.