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);
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>