GithubHelp home page GithubHelp logo

voronoicells.jl's Introduction

VoronoiCells

Build Status codecov.io

VoronoiCells use the VoronoiDelaunay package to compute the vertices and areas of the Voronoi cells in a tessellation. Furthermore, VoronoiCells handles interaction with the specified observation rectangle.

Installation

Switch to Pkg mode in Julia with ] and run

add VoronoiCells

Usage

For specifying 2D points I use the GeometryBasics package. Using the Plots package we can easily visualize Voronoi tesselations. To make this document reproducible, I also used a random point pattern with a fixed seed.

using VoronoiCells
using GeometryBasics
using Plots
using Random

First make a vector of points and a rectangle that contains the points:

rng = Random.MersenneTwister(1337)
rect = Rectangle(Point2(0, 0), Point2(1, 1))
points = [Point2(rand(rng), rand(rng)) for _ in 1:10]
10-element Vector{Point2{Float64}}:
 [0.22658190197881312, 0.5046291972412908]
 [0.9333724636943255, 0.5221721267193593]
 [0.5052080505550971, 0.09978246027514359]
 [0.04432218813798294, 0.7229058081423172]
 [0.8128138585478044, 0.24545709827626805]
 [0.11201971329803984, 0.0003419958128361156]
 [0.3800005018641015, 0.5052774551949404]
 [0.8411766784932724, 0.3265612016334474]
 [0.8108569785026885, 0.8504559154148246]
 [0.47805314311674496, 0.17906582198336407]

The main function of VoronoiCells is voronoicells that computes the cell of each generator point.

tess = voronoicells(points, rect);

The output tess is a struct. The corners of the Voronoi cells of the n'th generator is available as tess.Cells[n]. The corners are sorted counter-clockwise.

tess.Cells[1]
5-element Vector{Point2{Float64}}:
 [0.0, 0.5006658246439761]
 [0.0, 0.2909467571782577]
 [0.2213254892928772, 0.24066681274894675]
 [0.3041376415938776, 0.3046324682958502]
 [0.30224294454977085, 0.7530369438246255]

There is a convenience function for plotting the edges of the Voronoi cells. The generators are not added, but here I add them separately.

scatter(points, markersize = 6, label = "generators")
annotate!([(points[n][1] + 0.02, points[n][2] + 0.03, Plots.text(n)) for n in 1:10])
plot!(tess, legend = :topleft)

The function voronoiarea computes the area of each Voronoi cell:

voronoiarea(tess)
10-element Vector{Float64}:
 0.10905486942527855
 0.07490860886924389
 0.05461848195251296
 0.12952276262421533
 0.0943320987129441
 0.07231514118679612
 0.14583818637774282
 0.06295556177551635
 0.1745766432353954
 0.08187764584035451

Technical notes

My main interest is the area of the Voronoi cells and not the cells per se. The current representation of a cell as its corners in a vector is by no means set in stone, so reach out if you think another representation is more suitable.

Corners

For technical reasons the VoronoiDelaunay package only works with points in the rectangle [1, 2] x [1, 2] -- here referred to as the VoronoiDelaunay rectangle. Furthermore, VoronoiDelaunay includes the corner points of the rectangle in the set of generators. We can emulate the behavior in VoronoiCells by explicitly including the corners:

extended_points = vcat(points, VoronoiCells.corners(rect))
extended_tess = voronoicells(extended_points, rect);

Plotting this tesselation we see that the cells neighboring the corners are affected, namely the cells of points 1, 4, 5, 6, 9.

scatter(points, markersize = 6, label = "generators")
annotate!([(points[n][1] + 0.02, points[n][2] + 0.03, Plots.text(n)) for n in 1:10])
plot!(extended_tess, legend = :none)

VoronoiCells circumvents this in the following manner: The set of transformed generators are augmented with the corners of the VoronoiDelaunay rectangle. All points in the augmented generators are mapped to a rectangle called the computational rectangle with the following properties:

  • It is a (non-empty) subset of the VoronoiDelaunay rectangle
  • The Voronoi cells of the augmented generators belonging to the corners of the VoronoiDelaunay rectangle do not overlap with the computational rectangle.

This does not uniquely define a computational rectangle, but in theory any candidate will suffice. The intersection of the computational rectangle and the Voronoi cells of the transformed generators are transformed versions of their Voronoi cells in the original rectangle. Transforming these cells back to the original rectangle give the desired Voronoi tesselation.

Note that in order to consider point patterns in general rectangles such a mapping has to be applied anyway, so we are not introducing unnecessary computational cost.

The closer the generators are to the edges/corners of the original rectangle, the larger the computational rectangle can be. In order to avoid additional preprocessing I use a conservative minimal rectangle with corners (1.5 + x, 15 + x), (1.5 - x, 1.5 + x), (1.5 - x, 1.5 - x), (1.5 + x, 1.5 - x) where x = 1/6. If we can assume that all quadrants of the original rectangle contains points we can set x = 1/4.

Reach out if this small rectangle is causing trouble.

One extra step that is necessary is to figure out which Voronoi cell(s) the corners of the rectangle belongs to. This is determined by finding the point(s) with the smallest distance to each of the corners.

Weave

This README is generated with the Weave package using the command

weave("README.jmd", doctype = "github", fig_path = "doc")

voronoicells.jl's People

Contributors

dgleich avatar femtocleaner[bot] avatar philbit avatar robertdj avatar simondanisch avatar skygering avatar staticfloat avatar tkelman avatar

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

Watchers

 avatar  avatar  avatar

voronoicells.jl's Issues

Strict typing

voronoicells(points, rect) seems to be pretty strict on the type of points. A Point2f0 vector fails with

MethodError: no method matching map_to_computation_rectangle(::Vector{Point{2, Float32}}, ::Rectangle{Point2{Float64}}, ::Rectangle{GeometricalPredicates.Point2D})
Closest candidates are:
  map_to_computation_rectangle(::Vector{T}, ::Rectangle{T}, ::Rectangle) where T at ~/.julia/packages/VoronoiCells/PjjQG/src/Rectangle.jl:90

Stacktrace:
 [1] VoronoiCells.PointCollection(points::Vector{Point{2, Float32}}, rect::Rectangle{Point2{Float64}})
   @ VoronoiCells ~/.julia/packages/VoronoiCells/PjjQG/src/Cells.jl:18
 [2] voronoicells(points::Vector{Point{2, Float32}}, rect::Rectangle{Point2{Float64}})
   @ VoronoiCells ~/.julia/packages/VoronoiCells/PjjQG/src/Cells.jl:98

Same with Point2{Int64}.

I'm also wondering why this package implements its own Rectangle. Is the Rect in GeometryBasics missing functionality?

Outer rect/corner problem

First, thanks for the great work!

Sometimes, edges that go to the outer observation rectangle do not properly line up between neighboring Voronoi cells, like in this MWE:

using VoronoiCells
using GeometryBasics
using Plots
xcoord = [15.88214474527994
    18.863039659292394
    30.328176045427
    28.037431454174133
    40.31988094097913
    30.665194659310004
    44.11595650945559
    43.56366650514183]
ycoord = [24.68379930190996
    13.965766368581402
    18.398517333119784
    7.753520072641838
    26.694764409793734
    21.4580117453147
    25.27856333715974
    14.285077941536494]

rect = Rectangle(Point2(0, 0), Point2(50, 40))
points = [Point2(x, y) for (x,y) in zip(xcoord,ycoord)]
tess = voronoicells(points, rect);
Plots.scatter(points, markersize = 6, label = "center points")
plot!(tess, legend = :bottomleft, aspect_ratio=:equal)

This leads to the following tesselation:

result

As you can see, there are two vertices near the top-right corner that should be identical but aren't. Any idea what is going on here? Could it have anything to do with the conservative estimates for the transformation to the [1,2]x[1,2] rectangle for VoronoiDelaunay?

Construct tessellation from arbitrary shape, not just a Rectangle

In my application, it would be nice to calculate a tessellation inside an arbitrary bounding shape. What needs to be implemented for this to work? With some guidance I could implement it myself, but it seems that Rectangle is a key part of this as many of the Cells functions take rect as an argument, as such it may take some effort.

Not reproducible

If I run the example in the documentation twice, even using the exact same points, I get very very slightly different answers on some of the points.

For example, if I run:

using VoronoiCells
using GeometryBasics
using Random

rect = Rectangle(Point2(0, 0), Point2(1, 1))
rng = Random.MersenneTwister(1337)
points = [Point2(rand(rng), rand(rng)) for _ in 1:10]


cells1 = voronoicells(points, rect).Cells
cells2 = voronoicells(points, rect).Cells

same = true
for i in eachindex(cells1)
    same = same && all(cells1[i] .== cells2[i])
end

I consistently get different values on at least some of the points and same is false. Granted, these are small differences, e.g. 0.5006658246439755 vs. 0.5006658246439761, but given I have to scale the results quite a lot to get a tessellation of my needed size, these can become quite large. It also prevents my simulation from being reproducible.

Do you have any idea where this might come from? I am willing to take a look as well!

Thanks!

New version release

Would love to use the deterministic version as implemented in #35.
Can you release a new version of this package for it?

Example in readme does not work.

I tried running this code below but it errors at the rect definition, I tried replacing it with rect = Rect(0, 0, 1, 1) but this didn't fix the problem, now voronoicells breaks.

using VoronoiCells
using GeometryBasics
using Plots
using Random

Random.seed!(1337)
rect = Rectangle(Point2(0, 0), Point2(1, 1))
points = [Point2(rand(), rand()) for _ in 1:10]
tess = voronoicells(points, rect);
scatter(points, markersize = 6, label = "generators")
annotate!([(points[n][1] + 0.02, points[n][2] + 0.03, Plots.text(n)) for n in 1:10])
plot!(tess, legend = :topleft)
voronoiarea(tess)

ERROR: Unsatisfiable requirements detected for package VoronoiCells [e3e34ffb]

Cannot seem to install this package. Error is as follows:

ERROR: Unsatisfiable requirements detected for package VoronoiCells [e3e34ffb]:
VoronoiCells [e3e34ffb] log:
├─possible versions are: [0.0.1-0.0.2, 0.1.0-0.1.4] or uninstalled
├─restricted to versions * by an explicit requirement, leaving only versions [0.0.1-0.0.2, 0.1.0-0.1.4]
└─restricted by julia compatibility requirements to versions: uninstalled — no versions left

Readme code does not work

Hi,
I just tried the code that you offer in the Readme and it does not work:
Julia Version 1.5.2
VoronoiCells v0.1.5
GeometryBasics v0.3.11

using VoronoiCells
using GeometryBasics
using Plots
using Random

Random.seed!(1337)
rect = Rectangle(Point2(0, 0), Point2(1, 1))
points = [Point2(rand(), rand()) for _ in 1:10]
tess = voronoicells(points, rect);

Rectangle should be Rect2D
and voronoicells is defined over PointGenerators, no idea what's that.

Can you offer a minimal working example, I ll work from there.

Is it possible to extend to higher dimensions?

I found that VoronoiCells is fastest package to generate bounded Voronoi partition of a space, compared with MPT toolbox in matlab and 'voronoin' in matlab which called QHull. Is it possible to extend this package to higher dimensions? And what is the difficulty?

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

Plotting Voronoi cells filled-in by area

In some applications, such as mine, it is useful to be able to fill in the Voronoi cells when plotted and have the colour correspond to the area of the cell. This is simple enough to do using the code from the example:

using VoronoiCells
using GeometryBasics
using Plots
using Random

Random.seed!(42)
rect = Rectangle(Point2(0, 0), Point2(1, 1))
points = [Point2(rand(), rand()) for _ = 1:10]
tess = voronoicells(points, rect);

function cell_to_shape(cell)
    xs = first.(cell)
    ys = last.(cell)
    return Shape(xs, ys)
end


begin
    vareas = voronoiarea(tess)
    voronoi_cells_plot = plot(aspect_ratio = 1.0, lims = (0, 1), clims = (0, maximum(vareas)))
    for (i, cell) in enumerate(tess.Cells)
        plot!(cell_to_shape(cell), fill_z = vareas[i], label = "", alpha = 1)
    end
    scatter!(points, markersize = 6, label = "Generators", mc = :white)
    plot!(tess, legend = :topleft, lc = :black)
    voronoi_cells_plot
end

and this produces the following plot:
filled_cells_example

That said, this is quite hacky as it isn't done just using RecipesBase. Do you think you have time to implement this in RecipesBase?

Update compat for GeometryBasics v0.4

Hi, is it possible to have this package updated for GeometryBasics v0.4, and have the compat updated?

From what I can tell, Rectangle has been removed in the new minor version of GeometryBasics (as well as other changes), so the code in the README doesn't work (#26). Furthermore, adding compat VoronoiCells = "0.2" in a user's Project files forces GeometricBasics v0.3.13, which in turn may downgrade other packages a user is using (e.g. Makie.jl and more, which is causing me unresolvable issues).

Annoyingly, version v0.1.5 of VoronoiCells e1e0e28 didn't have a compat, so Pkg installs v0.1.5 by default when there are packages which require the latest version of GeometryBasics -- which is even more of a mess.

Thanks!

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.