Giter Club home page Giter Club logo

d3-voronoi's Introduction

d3-voronoi

Deprecation notice: Consider using the newer d3-delaunay instead of d3-voronoi. Based on Delaunator, d3-delaunay is 5-10× faster than d3-voronoi to construct the Delaunay triangulation or the Voronoi diagram, is more robust numerically, has Canvas rendering built-in, allows traversal of the Delaunay graph, and a variety of other improvements.


This module implements Steven J. Fortune’s algorithm for computing the Voronoi diagram or Delaunay triangulation of a set of two-dimensional points. This implementation is largely based on work by Raymond Hill.

Voronoi diagrams are not only visually attractive but practical tools for interaction, such as to increase the target area of points in a scatterplot. See “Strikeouts on the Rise” in The New York Times and this multi-line chart for examples; also see Tovi Grossman’s paper on bubble cursors for a related technique. Voronoi diagrams can also be used to automate label positioning, and Delaunay meshes are useful in computing adjacency or grouping of visual elements.

Installing

If you use NPM, npm install d3-voronoi. Otherwise, download the latest release. You can also load directly from d3js.org, either as a standalone library or as part of D3 4.0. AMD, CommonJS, and vanilla environments are supported. In vanilla, a d3 global is exported:

<script src="https://d3js.org/d3-voronoi.v1.min.js"></script>
<script>

var voronoi = d3.voronoi();

</script>

Try d3-voronoi in your browser.

API Reference

# d3.voronoi() <>

Creates a new Voronoi layout with default x- and y- accessors and a null extent.

# voronoi(data) <>

Computes the Voronoi diagram for the specified data points.

# voronoi.x([x]) <>

If x is specified, sets the x-coordinate accessor. If x is not specified, returns the current x-coordinate accessor, which defaults to:

function x(d) {
  return d[0];
}

# voronoi.y([y]) <>

If y is specified, sets the y-coordinate accessor. If y is not specified, returns the current y-coordinate accessor, which defaults to:

function y(d) {
  return d[1];
}

# voronoi.extent([extent]) <>

If extent is specified, sets the clip extent of the Voronoi layout to the specified bounds and returns the layout. 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, returns the current clip extent which defaults to null. A clip extent is required when using voronoi.polygons.

# voronoi.size([size]) <>

An alias for voronoi.extent where the minimum x and y of the extent are ⟨0,0⟩. Equivalent to:

voronoi.extent([[0, 0], size]);

# voronoi.polygons(data) <>

Returns a sparse array of polygons, one for each unique input point in the specified data points, corresponding to the cells in the computed Voronoi diagram. Equivalent to:

voronoi(data).polygons();

See diagram.polygons for more detail. Note: an extent is required.

# voronoi.triangles(data) <>

Returns the Delaunay triangulation of the specified data array as an array of triangles. Each triangle is a three-element array of elements from data. Equivalent to:

voronoi(data).triangles();

See diagram.triangles for more detail.

# voronoi.links(data) <>

Returns the Delaunay triangulation of the specified data array as an array of links. Each link has source and target attributes referring to elements in data. Equivalent to:

voronoi(data).links();

See diagram.links for more detail.

Voronoi Diagrams

# diagram <>

The computed Voronoi diagram returned by voronoi has the following properties:

  • edges - an array of edges.
  • cells - a sparse array of cells, one for each unique input point.

For each set of coincident input points, one of the points is chosen arbitrarily and assigned the associated cell; the other coincident input points’ entries are missing from the returned sparse array.

# diagram.polygons() <>

Returns a sparse array of polygons clipped to the extent, one for each cell (each unique input point) in the diagram. Each polygon is represented as an array of points [x, y] where x and y are the point coordinates, and a data field that refers to the corresponding element in data. Polygons are open: they do not contain a closing point that duplicates the first point; a triangle, for example, is an array of three points. Polygons are also counterclockwise, assuming the origin ⟨0,0⟩ is in the top-left corner.

For each set of coincident input points, one of the points is chosen arbitrarily and assigned the associated polygon; the other coincident input points’ entries are missing from the returned sparse array.

# diagram.triangles() <>

Returns the Delaunay triangulation of the specified data array as an array of triangles. Each triangle is a three-element array of elements from data. Since the triangulation is computed as the dual of the Voronoi diagram, and the Voronoi diagram is clipped by the extent, a subset of the Delaunay triangulation is returned.

# diagram.links() <>

Returns the Delaunay triangulation of the specified data array as an array of links, one for each edge in the mesh. Each link has the following attributes:

  • source - the source node, an element in data.
  • target - the target node, an element in data.

Since the triangulation is computed as the dual of the Voronoi diagram, and the Voronoi diagram is clipped by the extent, a subset of the Delaunay links is returned.

# diagram.find(x, y[, radius]) <>

Returns the nearest site to point [x, y]. If radius is specified, only sites within radius distance are considered.

See Philippe Rivière’s bl.ocks.org/1b7ddbcd71454d685d1259781968aefc for an example.

# cell

Each cell in the diagram is an object with the following properties:

  • site - the site of the cell’s associated input point.
  • halfedges - an array of indexes into diagram.edges representing the cell’s polygon.

# site

Each site in the diagram is an array [x, y] with two additional properties:

  • index - the site’s index, corresponding to the associated input point.
  • data - the input data corresponding to this site.

# edge

Each edge in the diagram is an array [[x0, y0], [x1, y1]] with two additional properties:

  • left - the site on the left side of the edge.
  • right - the site on the right side of the edge; null for a clipped border edge.

d3-voronoi's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d3-voronoi's Issues

Inverse Voronoi?

Hi!

I was wondering if, given the points used to make a line segments used to make up a closed shape, could you fine the voronoi point? if so what is the function that I would look at?

Cell.js:75 Uncaught TypeError: Cannot read property '0' of null

I've been getting this error randomly when loading datasets into a plot. I've spent a few days now trying to find the root cause of this. I haven't been able to find any null values that I'm sending into the voronoi calculating code. So right now my chart is crashing routinely and I don't know what to do.

Voronoi.find(x,y)

diagram.find(x,y) returns the closest site to point x,y — in other words a cell that contains said point, except if we are out of the extent.
http://bl.ocks.org/Fil/1b7ddbcd71454d685d1259781968aefc

The algorithm is quite simple (I invented it, but probably someone already did before me!). It seems to converge in about sqrt(n) steps, involving a few distance computations each time.

The API is similar to d3-force simulation.find() https://github.com/d3/d3-force/blob/master/src/simulation.js#L116

clipCells causes 'cannot read property 0 of null'

Repro: http://jsbin.com/cucerakofa/edit?js,console

Ran into this with certain datasets (most work fine). I tried debugging and don't have a clue what's going on, but

in the clipCells function, there's a chain of ternary operators ending with null. It's precisely this null (which is passed into createBorderEdge as the final argument creating an edge with a null point on the end) which is later being accessed for property 0 and is causing the TypeError.

polygons() returning [null, null, null]

I'm missing something, or there is a bug:

  const points = [[100, 100], [200, 200], [300, 300]]
  const voronoi = d3.voronoi()
    .extent([0, 0], [400, 400])
  const diagram = voronoi(points)
  const polygons = diagram.polygons()
  console.log(polygons)

gives

[
  null,
  null,
  null
]

without the extent:

  const points = [[100, 100], [200, 200], [300, 300]]
  const voronoi = d3.voronoi()
  const diagram = voronoi(points)
  const polygons = diagram.polygons()
  console.log(polygons)

gives

[
  [
    null
  ],
  [
    null,
    null
  ],
  [
    null
  ]
]

😕

Handle collinear (and cocircular) points?

So I stumbled upon this bug with d3.voronoi with this particular dataset:
http://jsbin.com/sogamotefo/edit?html,js,console

The bug also exists in the most recent version of d3v3, and if you swap the accessors for x & y it actually causes the browser to hang indefinitely with no error messages as though it it stuck in a loop:

// warning, replacing voronoiGen in the jsbin with this snippet will cause the browser to hang.
var voronoiGen = d3.voronoi()
  .x(function(d) {return y(d.y);})
  .y(function(d) {return x(d.x);})
  .extent([[0, 0], [500, 500]]);

.find() throws exception when data contains two elements on the same same position

d3-voronoi v1.1.0

const v = d3.voronoi()([[0,0], [0,0]]);
v.find(0, 0); // <- Uncaught TypeError: Cannot read property 'site' of undefined(…)

If you inspect v.cells you'll see that the first item in the array is undefined instead of a cell object.

v.triangles() also throws an exception, although a different message. And v.triangles() returns an array where the first element is undefined.

The documentation doesn't mention exceptions at all, which is why I didn't expect them.

Missing Triangle?

Discovered this while using triangles() to generate a mesh for interpolating some points. Noticed that some of my points were not inside any triangles. I was surprised to find that a triangle was missing from the mesh. Pruned my data down to 4 vertices, which should yield 2 triangles, but yields just 1. I've tried tweaking the vertices slightly, which results in the expect number of triangles.

v = require("d3-voronoi").voronoi();

vertices = [[47.307,105.33],[51.707,104.13],[52.717,110.74],[53.917,106.34]];

console.log(v.triangles(vertices)); // returns 1 triangle

vertices[3][1] += 0.001 // tweak one of the values slightly

console.log(v.triangles(vertices)); // returns 2 triangles (as expected)

Relation with d3-delaunay

Any plans (or wishes) to make this module use d3-delaunay internally, while exposing the same API? In that case, should this be done here, or should we on the contrary try and offer the old-style API in d3-delaunay?

TypeError: Cannot read property 'circle' of null

I encountered an issue that special input data can crash voronoi.polygons.

Reproduce Demo

var d3 = require("d3");

var width = 400;
var height = 400;

// change width and height to 100 works well
// var width = 100;
// var height = 100;

var x = d3.scaleLinear();
var y = d3.scaleLinear();

x.domain([0, 1]).range([0, width]).nice();
y.domain([0, 1]).range([height, 0]).nice();

var voronoi = d3.voronoi()
  .extent([[-1, -1], [width + 1, height + 1]])
  .x((pt) => x(pt.x))
  .y((pt) => y(pt.y));

// TypeError: Cannot read property 'circle' of null
voronoi.polygons([
  {x: 0.9999994485078688, y: 0},
  {x: 0.9999994512180275, y: 0}
]);

I thought it was my scale range problem, but the following code works:

voronoi.polygons([
  {x: 0.1, y: 0},
  {x: 0.2, y: 0}
]);

https://runkit.com/micooz/595cabe5640c440013382792

New examples for 1.0.1

It seems the API as described here is now very different than the examples given, can we get some updated examples?

voronoi.polygons will run out of memory or return incorrect polygons for certain data sets.

Extending on top of #28 and #16, voronoi.polygons will either crash, return incorrect data, or run out of memory on certain sets of linear data.

JSFiddle demonstrating the second case (incorrect polygons) here: https://jsfiddle.net/y3t9026e/
I've also generated a couple of other bad datasets for all 3 different cases here: https://pastebin.com/hHxWEvb1

Unfortunately, Delaunay also fails for linear data sets (since triangles cannot be generated within points in a line), so that is not an option for the time being.

voronoi.findAll(x,y,r)

The d3.voronoi module has the handy voronoi.find(x,y,r) (#17 ) method, which returns the closest site to a given point, which is very useful for a number of applications. But what if we wanted all the sites within a given radius?

I've been working on a few things where a built in method would be handy, but also saw a question question on StackOverflow yesterday on the same topic that got me thinking, if we have a method to find the closest site, why not a method to find all sites within a given radius?

I've mocked up a potential method here, it takes the same parameters as voronoi.find, but spits out an array of sites rather than a single one.

  findAll: function(x, y, r) {
    var diagram = this, center = diagram.find(x,y,r); // use the existing method to find central point
      if(!center) return [];
      var queue = [center], checked = [];      // queue holds cells within the boundary

      for(var i = 0; i < queue.length; i++) {
          checked.push(queue[i].index);           // don't check any cell twice
          
          // find neighbors of current item:
          var edges = diagram.cells[queue[i].index].halfedges;   
          var neighbors = edges.map(function(e) {
            return (diagram.edges[e].left == queue[i]) ?  diagram.edges[e].right : diagram.edges[e].left;
          })
         
          // for each neighbor, see if it is within the radius (and unchecked), if so, add to queue
          neighbors.forEach(function(n) {
             if(n && checked.indexOf(n.index) == -1) {
               var dx = n[0] - x, dy = n[1] - y;
               var d2 = dx*dx+dy*dy;
               (d2>r*r) ? checked.push(n.index) : queue.push(n);
             }
          })
      }
    return queue;  
  }

And here's a demonstration.

Granted, this implementation leads to some cells potentially being examined more than once (first through the use of voronoi.find() to get the initial cell); consequently, it could be re-worked to include a slightly modified voronoi.find() method to get started.

But, this might be getting ahead of myself. Is there value in including such a method in d3.voronoi?

voronoi.polygons crashes (runs out of memory)

Hey, thanks for d3 and all its various great packages!

We (see beancount/fava#731) recently in an out of memory issue with one of the charts of Fava - a line chart (looking like this) using a voronoi diagram to provide tooltips. A call to voronoi.polygons() seemed to be the culprit and the following snippet on runkit seems to reproduce the error (it crashes with error code 137, which denotes 'out of memory' afaict): https://runkit.com/yagebu/5ac88e13ef4e310012a3c15f

Feature request; polygon extent

I have a viz I want to make where I have a map segmented along district lines. I have points in each district and want to do a voronoi segmentation of that district along certain points. I would like to use the district polygon outline as extent, instead of a triangle.

d3.voronoi mutates data

Like some d3 layouts and functions. Voronoi seems to mutate data, but unlike some others (force for example adds properties but does not move them), all properties are dropped into a data node, and an array of 4 points is left at top level. As a result, it seems, I am unable to access the data properties to use a key function when joining when attempting to extend this example to apply animations with the general update pattern.
https://bl.ocks.org/mbostock/6526445e2b44303eebf21da3b6627320

Likewise, I am unable to to generate voronoi diagram without changing the data used elsewhere in my project. Which I think would be helpful as the voronoi is only being used in one state of my data display. Would it be preferable if it did not mutate, or am I missing something?

Cannot read property '0' of null in clipCells()

I'm using d3 4.2.2. I'm getting an exception in clipCells().

angular.js:13920 TypeError: Cannot read property '0' of null
    at clipCells (d3.js:11482)
    at new Diagram (d3.js:11849)
    at voronoi (d3.js:11918)
    at Function.voronoi.polygons (d3.js:11927)
    ...

Looking at the code it appears that the variable end is referenced end[0] before it is populated with a value.

https://github.com/d3/d3-voronoi/blob/master/src/Cell.js#L75

I don't have a simple test case right now since I'm seeing this exception buried under several other layers of 3rd party code that ends up calling d3.

Thanks for having a look.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.