Search code examples
javascriptreactjsd3.jsvisualizationvoronoi

problem with width of a voronoi diagram d3.js


I'm trying to create a voronoi diagram using d3 js and react js. The problem I encounter after implementing the required code, is that the voronoi diagram takes more width than what I've specified for the svg to take.

Here is the code I've implemented:

import React, { useState, useEffect, useRef, useCallback } from "react";
import * as d3 from "d3";
import "../App.css";




 const mySVGRef = useRef(null);
  const width = 600;
  const height = 500;
    // Create a Voronoi generator
    const voronoi = d3.Delaunay.from(data).voronoi();

const svg = d3.select(mySVGRef.current);
svg.attr("width", width).attr("height", height);
const xScale = d3
  .scaleLinear() // scale is used for linear
  .domain([0, d3.max(data, (d) => d[0])])
  .range([0, width]);
// Draw the Voronoi cells
svg
  .selectAll("path")
  .data(data)
  .enter()
  .append("path")
  .attr("d", (d, i) => voronoi.renderCell(i))
  .attr("fill", "none")
  .attr("stroke", "black");

// Draw the data points
svg
  .selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("cx", (d) => xScale(d[0]))
  //same must be done for the y scale
  .attr("cy", (d) => d[1])
  .attr("r", 3)
  .attr("fill", "red");
//draw the x and y axis

var x_axis = d3
  .axisBottom()
  .scale(xScale);
svg
  .append("g") // 
  .attr("transform", "translate(0, " + height + ")")
  .call(x_axis);


 

Solution

  • The voronoi method takes an optional bounds argument:

    When rendering, the diagram will be clipped to the specified bounds = [xmin, ymin, xmax, ymax]. If bounds is not specified, it defaults to [0, 0, 960, 500]. See To Infinity and Back Again for an interactive explanation of Voronoi cell clipping.

    You can update your code here:

    const voronoi = d3.Delaunay.from(data).voronoi(); // <-- no bounds
    

    To this:

    const voronoi = d3.Delaunay.from(data).voronoi([1, 1, width-1, height-1]);
    

    See below:

    // put your React items back later
    //import React, { useState, useEffect, useRef, useCallback } from "react";
    //import * as d3 from "d3";
    //import "../App.css";
    // const mySVGRef = useRef(null);
    const mySVGRef = { current: "#ref" }
    
    const width = 600;
    const height = 500;
    
    // fake data
    const data = Array.from({length: 200}, () => [Math.random() * width, Math.random() * height]) 
    
    // Create a Voronoi generator
    //const voronoi = d3.Delaunay.from(data).voronoi(); <-- no bounds
    // with bounds
    const voronoi = d3.Delaunay.from(data).voronoi([1, 1, width-1, height-1]);
    
    const svg = d3.select(mySVGRef.current);
    svg.attr("width", width).attr("height", height);
    const xScale = d3
      .scaleLinear() // scale is used for linear
      .domain([0, d3.max(data, (d) => d[0])])
      .range([0, width]);
    // Draw the Voronoi cells
    svg
      .selectAll("path")
      .data(data)
      .enter()
      .append("path")
      .attr("d", (d, i) => voronoi.renderCell(i))
      .attr("fill", "none")
      .attr("stroke", "black");
    
    // Draw the data points
    svg
      .selectAll("circle")
      .data(data)
      .enter()
      .append("circle")
      .attr("cx", (d) => xScale(d[0]))
      //same must be done for the y scale
      .attr("cy", (d) => d[1])
      .attr("r", 3)
      .attr("fill", "red");
    //draw the x and y axis
    
    var x_axis = d3
      .axisBottom()
      .scale(xScale);
    svg
      .append("g") // 
      .attr("transform", "translate(0, " + height + ")")
      .call(x_axis);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.0/d3.min.js"></script>
    <svg id="ref"></svg>