Closed sebpiq closed 11 years ago
Seems like there might be a bug in d3_geom_voronoiCloseCells
.
What's with all the precision? Does it work when you remove some of the digits after the decimal?
Didn't try. That's just the output of my algorithm which gives me random-ish points. Would that make a difference?
end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; I've honestly never seen syntax like this. Why is there a comma instead of a semicolon? If the end = ...end() returns a null does it still try to fill x3 and y3?
This thing with the coma is just multiple assignment, nothing special.
I believe the problem here is that the clipExtent in use does not contain all of the input points. Probably you’re using a standard clipExtent of [[0, 0], [960, 500]] or similar, but you have points with negative x and y coordinates. The clipping logic currently assumes that all of the points are contained within the clip extent.
A simple fix to this would be to ignore any points outside the clip extent on input, but that wouldn’t produce the same output since a site outside the clip extent can still affect the cells within the clip extent. I think probably the d3_geom_voronoiCloseCells would need to be changed to handle this case.
Indeed, I don't even know about the 'clipExtent' :)
On the other hand, this works in ~ 98% of my randomized points, and it works even if I place some points completely outside the range [[0, 0], [960, 500]] for example [-10000, 10000].
I’m seeing the error even with the default clipExtent of [[-1e6, -1e6], [1e6, 1e6]]
.
Hmm. I tested it with [[-width, -height], [width, height]], but yeah. I wonder if the bug more generally is negative coordinates. I did catch a bug related to that when I was porting the code, so it's possible another bug remains.
That bug is actually unrelated to this bug, but good to fix anyway. Here’s a reduction of the original test case. The correct output should look like this:
Instead it crashes (as reported). It seems like what’s happening here is that the four cells are meeting almost exactly at a point. If you change any of the points slightly, it computes the tessellation correctly.
All fixed!
Nice work!
Nice!!! That was quick :) Thanks!
Thanks, but it was @gorhill who figured it out! Also, Jason, he mentioned catastrophic cancellation which you might find interesting. :)
I get the same error as this one, the problem seems to come from the same method @jasondavies mentioned as well. I'm using d3 3.4.1. I don't really know where to look from here so I dumped the whole data in a gist in the hope someone would spot what I don't.
The most likely explanation is that you have coincident points in your input data. Did you check?
The input data is unique, but the rounding introduced in 6fd4625 changes this, 4023 -> 621 unique values.
Well, then by our definition you have coincident points, so you’ll need to resolve those before computing the Voronoi.
So you're saying I should pre-round it at 1e-6 and make it unique from there ? If the points are too close to each other I don't really see how I could achieve that, other than removing the (almost) duplicates from the dataset, any suggestion ?
@mbostock > most likely explanation is that you have coincident points
Isn't d3_geom_voronoi
supposed to work fine with duplicate points?
if (site.x !== x0 || site.y !== y0)
Note that I had to fix yet another loss of precision problem occurring in the Liang-Barsky algorithm, could it be the same problem here?
My fix was to prune more while attempting to connect dangling lines in connectEdge
: https://github.com/gorhill/Javascript-Voronoi/commit/41a3cae6178385836187fdacd760b6abc96cc6d9
Anyway maybe this has nothing to do, just FYI in case.
d3.geom.voronoi does handle with coincident points (in that it doesn’t throw an error); however it returns a null cell for each point that is a duplicate of an earlier point, so code that doesn’t explicitly check for null cells is likely to throw a TypeError.
And yes, you’d need to either remove the points that are coincident after rounding (say using d3.nest as in the Multi-Line Voronoi example — although this example doesn’t round), or represent coincident points as a cluster.
Well I've tried, the gist is updated with another file containing only the 621 non-duplicated x values, I've confirmed the uniqueness before and after the sites
call, and I still get a Cannot read property 'x' of null
.
The data is used to shape line charts paths, the Voronoi being the invisible part that helps find the nearest point from the mouse. In this context, 1px might be enough as long as I do not miss anything visible on the screen. Should I round it to integer pixels and take only the 1st of duplicates or would I miss visible points this way ?
Yes, that sounds like a reasonable approach.
In my attempts to reduce the number of points, I noticed that voronoi sometimes brings back an array with gaps, some indexes just don't exist, is this normal ?
@mbostock Any idea ?
The only known cause of the array containing null elements is coincident input points.
Oh, I expected an actual null value, not a missing index, thanks for the explanation :)
Yep; when skipping coincident points, the implementation simply doesn’t assign anything to that index in the returned array.
I am using voronoi to make simple radnomized drawings. It works great most of the time, but once in a while throws an error
'TypeError: Cannot read property 'x' of null'
. Is it a know error? Is there some points for which there is no Voronoi tesselation that can be calculated?For example, this happened with the following data set :