fabanc / pyvoronoi

Python wrapper for Boost voronoi diagram implementation.
27 stars 7 forks source link

pyvoronoi

A wrapper for Boost's Voronoi diagram library. The full documentation of the Boost Voronoi API is available here.

Install

The installation have been tested on Windows and Linux Ubuntu. If you notice any issue on Mac, reach out to us, we are interested in making sure it works for you.

Windows users will need Microsoft Visual C++ installed on their machine. You can find information about the version needed on this link. Python version from 3.5 to 3.12 rely on Visual C++ 14.x.

Dependencies

Cython dependency is optional. Cpp sources generated with Cython are available in releases.

Note on using the setup.py:

setup.py operates in 2 modes that are based on the presence of the dev file in the root of the project.

This way the package can be used without or with an incompatible version of Cython.

The idea comes from Matt Shannon's bandmat library.

From PyPI

Cython not required.

pip install pyvoronoi

From source

Cython required.

Clone the repository:

git clone https://github.com/fabanc/pyvoronoi.git

Install:

python setup.py install

After every modification of .pyx files compile with Cython:

python setup.py build_ext --inplace

Note in order to build the wheels, you will need to also install wheel

pip install wheel

Using

Create a new instance, passing the scaling factor into the constructor:

import pyvoronoi
pv = pyvoronoi.Pyvoronoi(10)

Since the voronoi library uses integer representation for points, the scaling factor chosen must be high enough to avoid roundoff error when converting from point coordinates to integers.

Add points and segments:

pv.AddPoint([0, 0])
pv.AddSegment([[1,5],[2,2]])

Call Construct() and get the edges and vertices:

pv.Construct()
edges = pv.GetEdges()
vertices = pv.GetVertices()
cells = pv.GetCells()

Note that vertices, edges, and cells, can be accessed individually. The methods above are just convenience wrappers around the following functions:

def GetVertices(self):
    count = self.CountVertices()
    output = []
    for index in  range(count):
        output.append(self.GetVertex(index))
    return output
def GetEdges(self):
    count = self.CountEdges()
    output = []
    for index in range(count):
        output.append(self.GetEdge(index))
    return output
def GetCells(self):
    count = self.CountCells()
    output = []
    for index in range(count):
        output.append(self.GetCell(index))
    return output

If you are running python 2.x, you might want to write your own wrappers using xrange. This will be more efficient.

Edges have the following properties:

Cells have the following properties:

pv = pyvoronoi.Pyvoronoi(100)
pv.AddSegment([[0.1,0.8],[0.3,0.6]])
pv.AddSegment([[0.3,0.6],[0.4,0.6]])
pv.AddSegment([[0.4,0.6],[0.4,0.5]])
pv.AddSegment([[0.4,0.6],[0.4,0.7]])
pv.AddSegment([[0.4,0.7],[0.5,0.8]])
pv.AddSegment([[0.4,0.7],[0.5,0.6]])
pv.AddSegment([[0.5,0.6],[0.7,0.7]])

pv.Construct()
edges = pv.GetEdges()
vertices = pv.GetVertices()
cells = pv.GetCells()
print("Cell Count: {0}".format(len(cells)))
for c in cells:
    print("Cell contains point: {0}. Contains segment: {1}. Is open: {2}, Site Index: {3}".format(c.contains_point, c.contains_segment, c.is_open, c.site))
    print(",".join(map(str,c.vertices)))
    for sIndex in c.edges:
        print("Start Index: {0}, End Index = {1}".format(edges[sIndex].start, edges[sIndex].end))

Some output edges returned by the boost voronoi API are suposed to be curved. In the C++ API, it is up to you to code it. Luckily, you can do it in python using the following the function DiscretizeCurvedEdge. The sample below shows you how to do that:

for cIndex in range(len(cells)):
    cell = cells[cIndex]
    if cell.is_open == False:
        for i in range(len(cell.edges)):
            e = edges[cell.edges[i]]
            startVertex = vertices[e.start]
            endVertex = vertices[e.end]

            max_distance  = distance([startVertex.X, startVertex.Y], [endVertex.X, endVertex.Y]) / 10
            if startVertex != -1 and endVertex != -1:
                if(e.is_linear == True):
                    array = [[startVertex.X, startVertex.Y],[endVertex.X, endVertex.Y]]
                else:
                    points = pv.DiscretizeCurvedEdge(i, max_distance)
                    for p in points:
                        print "{0},{1}".format(p[0], p[1])

The curve interpolation code can return 2 exceptions.

License

Development

Build tools

This project uses cibuildwheel to build wheels on multiple platforms.