Day 17: Voronoi Diagram

Today I learned a new trick from fullstack D3 course. It’s called Voronoi diagram, and it will help the hovering event to show the tooltip for the scatter plot. Users do not need to hover exactly on the dot. To show what I mean, see the gif below. As long as the mouse is over the nearby area, the tooltip is shown.

So I made some improvements to my old scatter plot from Day 10. 🙂

Maybe you are like me to be the first time to hear about the Voronoi diagram. Here is the explanation I got from chatGPT.

A Voronoi diagram is a partitioning of a plane into regions based on the distance to points in a specific subset of the plane. Given a set of points, called sites, each location in the plane is associated with the nearest site.

It’s worth noting that Voronoi diagrams are closely related to Delaunay triangulations. In a Delaunay triangulation, triangles are formed such that no point is inside the circumcircle of any triangle. If you connect the circumcenters of these triangles, you get the Voronoi diagram.

chatGPT

To achieve this function, we need to do the following steps:

  • Create a delaunay object
  • Create a Voronoi diagram from delaunay
  • Bound the mouseover and mouseout event to our Voronoi diagram instead of the dots
  • Create some visual cue to show which dot is the tooltip linked to since our mouse might not be on the dot when the tooltip is shown

Create Voronoi Diagram

One thing to note is that the width and height of the Voronoi diagram are 960 px * 500 px. Therefore we need to set it as the same size as our chart.

const voronoi = delaunay.voronoi()
voronoi.xmax = chartWidth
voronoi.ymax = chartHeight

chart.selectAll(".voronoi")
    .data(dataset)
    .join("path")
    .attr("class", "voronoi")
    .attr("d", (d,i) => voronoi.renderCell(i))
    //.attr("stroke", "salmon")
    //comment this out so the end users don't see it
If we don’t set the .xmax & .ymax attribute

mouseover & mouseout

The bold lines are the codes added this time. In “mouseover”, we added the black stroke to indicate which dot is associated with the tooltip. In “mouseout”, we set the stroke to none. Of course, the “mouseover” and “mouseout “events are bounded to the Voronoi diagram.

 chart.selectAll(".voronoi")
       //here used to be ".circle"
       .on('mouseover', mouseOver)
       .on('mouseout',  mouseOut)

function mouseOver(event,d){
        chart.selectAll("circle")
        .filter(datum => datum == d)
        .style("stroke", "black")
        .style("stroke-width",1)

        tooltip
        .style("opacity", 1)

        tooltip
        .html("Petal Length: " + yAccessor(d)+
        "<br>" + "Petal Width: " + xAccessor(d))
        .style("left", xScale(xAccessor(d)) + margin.left + window.innerWidth * 0.2 + "px")
        .style("top",  yScale(yAccessor(d)) + margin.top+ "px")
    }


    function mouseOut(event,d) { 
        tooltip
        .style("opacity", 0)
        
        chart.selectAll("circle")
        .filter(datum => datum == d)
        .style("stroke","none")
    }

Today’s codes are here.

Leave a comment