A C implementation for creating 2D voronoi diagrams

Related tags

Deep Learningvoronoi
Overview
Branch OSX/Linux Windows
master Build Status Build status
dev Build Status Build status

jc_voronoi

A fast C/C++ header only implementation for creating 2D Voronoi diagrams from a point set

Uses Fortune's sweep algorithm.

vanilla custom clipping

Brief

I was realizing that the previous 2D voronoi generator I was using, was taking up too much time in my app, and worse, sometimes it also produced errors.

So I started looking for other implementations.

Given the alternatives out there, they usually lack one aspect or the other. So this project set out to achieve a combination of the good things the other libs provide.

  • Easy to use
  • Robustness
  • Speed
  • Small memory footprint
  • Single/Double floating point implementation
  • Readable code
  • Small code (single source file)
  • No external dependencies
  • Cells have a list of edges (for easier/faster relaxation)
  • Edges should be clipped
  • A clear license

But mostly, I did it for fun :)

Disclaimer

This software is supplied "AS IS" without any warranties and support

License

LICENSE (The MIT license)

Feature comparisons

Feature vs Impl voronoi++ boost fastjet jcv
Language C++ C++ C C
Edge clip * * *
Generate Edges * * * *
Generate Cells * * *
Cell Edges Not Flipped * *
Cell Edges CCW * *
Easy Relaxation *
Custom Allocator *

Usage

The api contains these functions

void jcv_diagram_generate( int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, jcv_diagram* diagram );
void jcv_diagram_generate_useralloc( int num_points, const jcv_point* points, const jcv_rect* rect, const jcv_clipper* clipper, void* userallocctx, FJCVAllocFn allocfn, FJCVFreeFn freefn, jcv_diagram* diagram );
void jcv_diagram_free( jcv_diagram* diagram );

const jcv_site* jcv_diagram_get_sites( const jcv_diagram* diagram );
const jcv_edge* jcv_diagram_get_edges( const jcv_diagram* diagram );
const jcv_edge* jcv_diagram_get_next_edge( const jcv_edge* edge );

The input points are pruned if

* There are duplicates points
* The input points are outside of the bounding box
* The input points are rejected by the clipper's test function

The input bounding box is optional

The input clipper is optional, a default box clipper is used by default

Example

Example implementation (see main.c for actual code)

#define JC_VORONOI_IMPLEMENTATION
#include "jc_voronoi.h"

void draw_edges(const jcv_diagram* diagram);
void draw_cells(const jcv_diagram* diagram);

void generate_and_draw(int numpoints, const jcv_point* points, int imagewidth, int imageheight)
{
    jcv_diagram diagram;
    memset(&diagram, 0, sizeof(jcv_diagram));
    jcv_diagram_generate(count, points, 0, 0, &diagram );

    draw_edges(diagram);
    draw_cells(diagram);

    jcv_diagram_free( &diagram );
}

void draw_edges(const jcv_diagram* diagram)
{
    // If all you need are the edges
    const jcv_edge* edge = jcv_diagram_get_edges( diagram );
    while( edge )
    {
        draw_line(edge->pos[0], edge->pos[1]);
        edge = jcv_diagram_get_next_edge(edge);
    }
}

void draw_cells(const jcv_diagram* diagram)
{
    // If you want to draw triangles, or relax the diagram,
    // you can iterate over the sites and get all edges easily
    const jcv_site* sites = jcv_diagram_get_sites( diagram );
    for( int i = 0; i < diagram->numsites; ++i )
    {
        const jcv_site* site = &sites[i];

        const jcv_graphedge* e = site->edges;
        while( e )
        {
            draw_triangle( site->p, e->pos[0], e->pos[1]);
            e = e->next;
        }
    }
}

Relaxing the points

Here is an example of how to do the relaxations of the cells.

void relax_points(const jcv_diagram* diagram, jcv_point* points)
{
    const jcv_site* sites = jcv_diagram_get_sites(diagram);
    for( int i = 0; i < diagram->numsites; ++i )
    {
        const jcv_site* site = &sites[i];
        jcv_point sum = site->p;
        int count = 1;

        const jcv_graphedge* edge = site->edges;

        while( edge )
        {
            sum.x += edge->pos[0].x;
            sum.y += edge->pos[0].y;
            ++count;
            edge = edge->next;
        }

        points[site->index].x = sum.x / count;
        points[site->index].y = sum.y / count;
    }
}

Double floating point precision

If you wish to use doubles, you can override these defines:

#define JC_VORONOI_IMPLEMENTATION
#define JCV_REAL_TYPE double
#define JCV_ATAN2 atan2
#define JCV_SQRT sqrt
#define JCV_FLT_MAX DBL_MAX
#define JCV_PI 3.141592653589793115997963468544185161590576171875
//define JCV_EDGE_INTERSECT_THRESHOLD 1.0e-10F
#include "jc_voronoi.h"

Custom clipping

The library also comes with a second header, that contains code for custom clipping of edges against a convex polygon.

The polygon is defined by a set of

Again, see main.c for a practical example

    #define JC_VORONOI_CLIP_IMPLEMENTATION
    #include "jc_voronoi_clip.h"

    jcv_clipping_polygon polygon;
    // Triangle
    polygon.num_points = 3;
    polygon.points = (jcv_point*)malloc(sizeof(jcv_point)*(size_t)polygon.num_points);

    polygon.points[0].x = width/2;
    polygon.points[1].x = width - width/5;
    polygon.points[2].x = width/5;
    polygon.points[0].y = height/5;
    polygon.points[1].y = height - height/5;
    polygon.points[2].y = height - height/5;

    jcv_clipper polygonclipper;
    polygonclipper.test_fn = jcv_clip_polygon_test_point;
    polygonclipper.clip_fn = jcv_clip_polygon_clip_edge;
    polygonclipper.fill_fn = jcv_clip_polygon_fill_gaps;
    polygonclipper.ctx = &polygon;

    jcv_diagram diagram;
    memset(&diagram, 0, sizeof(jcv_diagram));
    jcv_diagram_generate(count, (const jcv_point*)points, 0, clipper, &diagram);

Some Numbers

Tests run on a Intel(R) Core(TM) i7-7567U CPU @ 3.50GHz MBP with 16 GB 2133 MHz LPDDR3 ram. Each test ran 20 times, and the minimum time is presented below

I removed the voronoi++ from the results, since it was consistently 10x-15x slower than the rest and consumed way more memory _
timings memory num_allocations

Same stats, as tables

General thoughts

Fastjet

The Fastjet version is built upon Steven Fortune's original C version, which Shane O'Sullivan improved upon. Given the robustness and speed improvements of the implementation done by Fastjet, that should be the base line to compare other implementations with.

Unfortunately, the code is not very readable, and the license is unclear (GPL?)

Also, if you want access to the actual cells, you have to recreate that yourself using the edges.

Boost

Using boost might be convenient for some, but the sheer amount of code is too great in many cases. I had to install 5 modules of boost to compile (config, core, mpl, preprocessor and polygon). If you install full boost, that's 650mb of source.

It is ~2x as slow as the fastest algorithms, and takes ~2.5x as much memory.

The boost implementation also puts the burden of clipping the final edges on the client.

The code consists of only templated headers, and it increases compile time a lot. For simply generating a 2D voronoi diagram using points as input, it is clearly overkill.

Voronoi++

The performance of it is very slow (~20x slower than fastjet) and And it uses ~2.5x-3x more memory than the fastest algorithms.

Using the same data sets as the other algorithms, it breaks under some conditions.

O'Sullivan

A C++ version of the original C version from Steven Fortune.

Although fast, it's not completely robust and will produce errors.

Gallery

I'd love to see what you're using this software for! If possible, please send me images and some brief explanation of your usage of this library!

Comments
  • Site vertices

    Site vertices

    If I want to create a mesh from the sites can I use edges and assume they are connected, i.e. iterate through edges and add each starting point to a list of vertices ?

    opened by kewp 14
  • Site-point collisions

    Site-point collisions

    Not sure where to put these comments / requests for info ...

    (Thanks for creating this library, btw ! Has saved me a lot of time and brain power)

    Is there an easy way to determine which site within the rectangle belongs to an x,y co-ordinate ? All I can think to do is loop through each one and do a polygonal collision check using each edge but I figured Voronoi is sort-of designed to know which points belong inside (that's basically the definition of a Voronoi diagram).

    opened by kewp 9
  • voronoi map:multiple polygons intersects

    voronoi map:multiple polygons intersects

    Hi, I'm using this code to generate voronoi with 1036 points in my project. but the result is image it't not a voronoi map and many polygons intersects. I'm using this function: jcv_diagram_generate(1036, points, &bounding_box, nullptr, &diagram) bounding_box is jcv_rect bounding_box = {{-45.8605, -653.969}, {746.861, 142.3}}; points is in the points.csv file. points.csv

    diagram is definited as this: jcv_diagram diagram; memset(&diagram, 0, sizeof(jcv_diagram)); as a result, I'm using OGRGeometry library to render that diagram as above and I'm pretty sure it's not a problem of ogrGeometry library. I also confirmed that all points are within the bounding_box and there is no abnormal point.

    can you help me to handle this problem? thank you very much @JCash @dgavedissian @williamleong

    opened by lxzmxl 8
  • Assertion internal->numsites == 1 occasionally triggers on valid input.

    Assertion internal->numsites == 1 occasionally triggers on valid input.

    There is a bug in this library that occasionally causes the assertion at line 1134 (internal->numsites == 1) to fail. I do not know what the issue is, but I have attached a test program and data file that faithfully reproduce the error. Apologies for the size of the data file—this is an extremely infrequent error, and I've only been able to trigger it with sets this large.

    Test file: mem.bin.zip

    Test code:

    #include <stdio.h>
    
    #define JC_VORONOI_IMPLEMENTATION
    #define JCV_REAL_TYPE double
    #define JCV_ATAN2 atan2
    #define JCV_FLT_MAX 1.7976931348623157E+308
    #include "jc_voronoi.h"
    
    int main() {
      jcv_point *points = (jcv_point *)malloc(9216 * sizeof(jcv_point));
    
      FILE *in = fopen("mem.bin", "rb");
      fread(points, sizeof(jcv_point), 9216, in);
      fclose(in);
    
      /* should start with
         (x = 40.232121213226684, y = 13.714460519854523)
         (x = 168.23212121322669, y = 13.714460519854523)
         (x = -87.767878786773309, y = 13.714460519854523)
         (x = 40.232121213226684, y = 29.714460519854523)
         (x = 40.232121213226684, y = -2.2855394801454771)
         (x = 168.23212121322669, y = 29.714460519854523)
         (x = -87.767878786773309, y = 29.714460519854523)
         (x = 168.23212121322669, y = -2.2855394801454771)
         (x = -87.767878786773309, y = -2.2855394801454771)
         (x = 123.81366674520085, y = 1.1403291016984274)
         */
      for (unsigned int i = 0; i < 10; i++) {
        printf("(x = %.14f, y = %.14f)\n", points[i].x, points[i].y);
      }
    
      jcv_diagram diagram;
      memset(&diagram, 0, sizeof(jcv_diagram));
    
      jcv_rect rect = {{-128.0, -16.0}, {256.0, 32.0}};
    
      jcv_diagram_generate(9216, points, &rect, &diagram);
    
      return 0;
    }
    

    Example use:

    > unzip mem.bin.zip
    Archive:  mem.bin.zip
      inflating: mem.bin 
    > clang -lm test.c
    > ./a.out
    (x = 40.23212121322668, y = 13.71446051985452)
    (x = 168.23212121322669, y = 13.71446051985452)
    (x = -87.76787878677331, y = 13.71446051985452)
    (x = 40.23212121322668, y = 29.71446051985452)
    (x = 40.23212121322668, y = -2.28553948014548)
    (x = 168.23212121322669, y = 29.71446051985452)
    (x = -87.76787878677331, y = 29.71446051985452)
    (x = 168.23212121322669, y = -2.28553948014548)
    (x = -87.76787878677331, y = -2.28553948014548)
    (x = 123.81366674520085, y = 1.14032910169843)
    a.out: ./jc_voronoi.h:1134: void jcv_fillgaps(jcv_diagram *): Assertion `internal->numsites == 1' failed.
    [1]    21138 abort (core dumped)  ./a.out
    
    opened by kentdobias 8
  • List of unique vertices

    List of unique vertices

    One more issue :/

    Because I want to create a mesh, I need a list of unique vertices. These are stored as x,y points. Any idea how to do this ?

    My naive algorithm would go as follows:

    • Loop through all sites and their edges and add them all together (i.e. 5 sites each with, say, 5 edges gives a maximum of 25 vertices)
    • Now we have a maximum. Allocate an integer for each (to be used as boolean). Set all to 1.
    • Loop through again, this time with an inner loop starting from the outer loop +1
    • In the inner loop, check if the outer loop position as the same as inner. If so, set the value of the integer to 0 (not unique).

    Now we have a list of unique vertices ...

    Not very elegant. Any better ideas ?

    Also - can I just use an equals check on the positions (x1==x2) even though they are floats ?

    opened by kewp 5
  • Assertion `pq->numitems < pq->maxnumitems' failed.

    Assertion `pq->numitems < pq->maxnumitems' failed.

    It is very rare, and saw it only once, but I was able to hit this assert:

    jc_voronoi.h:887: int jcv_pq_push(jcv_priorityqueue *, void *): Assertion `pq->numitems < pq->maxnumitems' failed.

    Unfortunately, I didn't get a core dump to investigate further.

    I feed it between 2 and 16 points, with no duplicates.

    From now on, I'll run my game in a debugger, so I can catch it and report back.

    opened by stolk 5
  • Bug: graph is missing edges

    Bug: graph is missing edges

    First of all, thank you very much for your great work!

    We're are using your implementation in a RoboCup soccer league and believe to have encountered a bug.

    The points are the player's positions {-4050,0}, {-3300,500}, {0,1000}, {0,-1000}, {2250,0}. The bounding box to clip against is the field's corners {-4500,-3000}, {4500,3000}.

    When iterating over the graph edges of the last point, the top right ({4500, 3000}) and bottom right ({4500,-3000}) corners of the field are missing entirely. Interestingly, changing the point {2250,0} to {2250,1} will fix the issue and the Voronoi diagram is constructed correctly.

    Please find my screenshot attached.

    Any help would be much appreciated. I'd be happy to submit a PR fixing the bug when found.

    121

    opened by JoHoop 3
  • Assertion on polygon bounds

    Assertion on polygon bounds

    Hey, congrats on the great library.

    I am trying to generate a voronoi diagram inside one of the cells of another voronoi diagram. I am converting the bounding polygon to a jcv_polygon and then making a clipper and setting it as a ctx.

    The generation asserts at line 218 of jc_voronoi_clip.h at this assertion assert(min_edge >= 0);

    Can you please help me understand what is happening? Thanks

    opened by Balgy 3
  • half edge neighbor is incorrect

    half edge neighbor is incorrect

    when i iterate through a site's halfedges and look at their neighbors, they do not correspond to actual neighbors. and even if i look at the half edge's corresponding edge, its two sites are not neighboring in the diagram. untitled

    opened by godpenguin7 3
  • Incorrect number of edges in specific case

    Incorrect number of edges in specific case

    When using jc_voronoi for a natural neighbor interpolation problem, I was testing jc_voronoi to make sure it was getting linked correctly by giving it a square of points at {0,0}, {val, 0}, {0, val}, {-val, 0}, and {0, -val}.

    There appears to be a bug when val = 2 in this case. I went through and was checking the number of edges that each site said it had, and the {0,0} site is always supposed to say 4. When val = 2, it says it has two. This doesn't seem to happen when I rotate or shift the square. This code runs through these cases and some other vals other than 2

    #define JC_VORONOI_IMPLEMENTATION
    #include "jc_voronoi.h"
    #include <cmath>
    #include <stdlib.h>
    #include <iostream>
    
    void printSquare( float val, int mode )
    {
      std::cout << "\nStart Of Test\n";
      int pointCount = 5;
      jcv_point list[pointCount];
      // list[0] = {  0,   0 };
    
      if( mode == 1)
      {
        //45 degree rotation on unit circle
        list[0].x = 0;
        list[0].y = 0;
        float valUpdated = val/(std::sqrt(2));
        // list[1] = { valUpdated, valUpdated };
        list[1].x = valUpdated;
        list[1].y = valUpdated;
        // list[2] = { -valUpdated, valUpdated };
        list[2].x = -valUpdated;
        list[2].y = valUpdated;
        // list[3] = { valUpdated, -valUpdated };
        list[3].x = valUpdated;
        list[3].y = -valUpdated;
        // list[4] = { -valUpdated, -valUpdated };
        list[4].x = -valUpdated;
        list[4].y = -valUpdated;
      }else if ( mode == 2)
      {
        //offset from origin
        float xOffset = std::rand() % 100 + 1;
        float yOffset = std::rand() % 100 + 1;
    
        list[0].x = 0 + xOffset;
        list[0].y = 0 + yOffset;
    
        // list[1] = { val + xOffset , 0 + yOffset };
        list[1].x = val + xOffset;
        list[1].y = 0 + yOffset;
        // list[2] = { 0 + xOffset, val + yOffset  };
        list[2].x = 0 + xOffset;
        list[2].y = val + yOffset;
        // list[3] = { -val + xOffset, 0 + yOffset  };
        list[3].x = -val + xOffset;
        list[3].y = 0 + yOffset;
        // list[4] = { 0 + xOffset, -val + yOffset };
        list[4].x = 0 + xOffset;
        list[4].y = -val + yOffset;
      }else
      {
        list[0].x = 0;
        list[0].y = 0;
        // list[1] = { val, 0 };
        list[1].x = val;
        list[1].y = 0;
        // list[2] = { 0, val };
        list[2].x = 0;
        list[2].y = val;
        // list[3] = { -val, 0 };
        list[3].x = -val;
        list[3].y = 0;
        // list[4] = { 0, -val };
        list[4].x = 0;
        list[4].y = -val;
      }
      jcv_diagram diagram;
      memset(&diagram, 0, sizeof(jcv_diagram));
      jcv_diagram_generate(pointCount, list, nullptr, &diagram);
    
      const jcv_site *sites = jcv_diagram_get_sites(&diagram);
      for( int i = 0; i < diagram.numsites; ++i )
      {
          const jcv_site* site = &sites[i];
          std::cout << "\nAt site index " << site->index << " with position (" << site->p.x << ", " << site->p.y << ")\n";
          const jcv_graphedge* e = site->edges;
          int edgeCount = 0;
    			while( e )
    			{
    			// 	std::cout << "\nSite pos  = " << site->p.x << ", " << site->p.y << "\n";
          //   std::cout << "Edge 1 pos = " << e->pos[0].x << ", " << e->pos[0].y << "\n";
          //   std::cout << "Edge 2 pos = " << e->pos[1].x << ", " << e->pos[1].y << "\n";
            edgeCount++;
    				e = e->next;
    			}
          // std::cout << "\nSite pos  = " << site->p.x << ", " << site->p.y << "\n";
          std::cout << "Number of edges is " << edgeCount << "\n";
          if(site->p.x == 0 && site->p.y == 0)
          {
            if(edgeCount != 4)
            {
              std::cout << "At (0,0) the cell does not have 4 edges!!!!!!!!!!!\n";
            }else{
              std::cout << "As expected, the cell at (0,0) has 4 edges\n";
            }
          }
      }
      std::cout << "Done printing sites and edge counts for this test\n\n\n";
      jcv_diagram_free( &diagram );
    
    }
    
    int main( int argc, const char *argv[])
    {
    
      float eps = std::numeric_limits<float>::epsilon();
      std::cout << "\nDemonstration of bug at 2\n\n";
    
      printSquare( 1, 0 );
      printSquare( 3, 0 );
      std::cout << "\nThis is the buggy one\n";
      printSquare( 2, 0 ); //issue is here
      std::cout << "\nEnd of the buggy one\n";
      printSquare( 2, 1 );
      printSquare( 2, 2 );
      printSquare( 2+2*eps, 0 );
      printSquare( 2-2*eps, 0 );
    
      std::cout << "\n\n\nEnd of Tests\n";
    
      return 0;
    
    }
    

    The result from this is

    Demonstration of bug at 2
    
    
    Start Of Test
    
    At site index 4 with position (0, -1)
    Number of edges is 4
    
    At site index 3 with position (-1, 0)
    Number of edges is 4
    
    At site index 0 with position (0, 0)
    Number of edges is 4
    As expected, the cell at (0,0) has 4 edges
    
    At site index 1 with position (1, 0)
    Number of edges is 4
    
    At site index 2 with position (0, 1)
    Number of edges is 4
    Done printing sites and edge counts for this test
    
    
    
    Start Of Test
    
    At site index 4 with position (0, -3)
    Number of edges is 4
    
    At site index 3 with position (-3, 0)
    Number of edges is 4
    
    At site index 0 with position (0, 0)
    Number of edges is 4
    As expected, the cell at (0,0) has 4 edges
    
    At site index 1 with position (3, 0)
    Number of edges is 4
    
    At site index 2 with position (0, 3)
    Number of edges is 4
    Done printing sites and edge counts for this test
    
    
    
    This is the buggy one
    
    Start Of Test
    
    At site index 4 with position (0, -2)
    Number of edges is 3
    
    At site index 3 with position (-2, 0)
    Number of edges is 2
    
    At site index 0 with position (0, 0)
    Number of edges is 2
    At (0,0) the cell does not have 4 edges!!!!!!!!!!!
    
    At site index 1 with position (2, 0)
    Number of edges is 4
    
    At site index 2 with position (0, 2)
    Number of edges is 4
    Done printing sites and edge counts for this test
    
    
    
    End of the buggy one
    
    Start Of Test
    
    At site index 4 with position (-1.41421, -1.41421)
    Number of edges is 5
    
    At site index 3 with position (1.41421, -1.41421)
    Number of edges is 5
    
    At site index 0 with position (0, 0)
    Number of edges is 4
    As expected, the cell at (0,0) has 4 edges
    
    At site index 2 with position (-1.41421, 1.41421)
    Number of edges is 5
    
    At site index 1 with position (1.41421, 1.41421)
    Number of edges is 5
    Done printing sites and edge counts for this test
    
    
    
    Start Of Test
    
    At site index 4 with position (8, 48)
    Number of edges is 4
    
    At site index 3 with position (6, 50)
    Number of edges is 4
    
    At site index 0 with position (8, 50)
    Number of edges is 4
    
    At site index 1 with position (10, 50)
    Number of edges is 4
    
    At site index 2 with position (8, 52)
    Number of edges is 4
    Done printing sites and edge counts for this test
    
    
    
    Start Of Test
    
    At site index 4 with position (0, -2)
    Number of edges is 4
    
    At site index 3 with position (-2, 0)
    Number of edges is 4
    
    At site index 0 with position (0, 0)
    Number of edges is 4
    As expected, the cell at (0,0) has 4 edges
    
    At site index 1 with position (2, 0)
    Number of edges is 4
    
    At site index 2 with position (0, 2)
    Number of edges is 4
    Done printing sites and edge counts for this test
    
    
    
    Start Of Test
    
    At site index 4 with position (0, -2)
    Number of edges is 4
    
    At site index 3 with position (-2, 0)
    Number of edges is 4
    
    At site index 0 with position (0, 0)
    Number of edges is 4
    As expected, the cell at (0,0) has 4 edges
    
    At site index 1 with position (2, 0)
    Number of edges is 4
    
    At site index 2 with position (0, 2)
    Number of edges is 4
    Done printing sites and edge counts for this test
    
    
    
    
    
    End of Tests
    

    I am not sure why this is happening, especially since it works when val = 2 + 2 *numeric_limits::epsilon.

    I am hoping someone more familiar with the code can find it the reason

    bug 
    opened by archimedes4000 3
  • Provide simple example of usage

    Provide simple example of usage

    Thanks for the great library!

    It would be helpful to provide a simple example program of usage. The main.c is a little verbose and has a lot of cruft dealing with coloring and saving to an image file.

    Here is an example program:

    // to compile:
    //
    // gcc jc_voronoi_example.c -I../src -o jc_voronoi_example  -lm
    //
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define JC_VORONOI_IMPLEMENTATION
    // If you wish to use doubles
    //#define JCV_REAL_TYPE double
    //#define JCV_FABS fabs
    //#define JCV_ATAN2 atan2
    #include "jc_voronoi.h"
    
    #define NPOINT 10
    
    int main(int argc, char **argv) {
      int i;
      jcv_rect bounding_box = { { 0.0, 0.0 }, { 1.0, 1.0 } };
      jcv_diagram diagram;
      jcv_point points[NPOINT];
      const jcv_site *sites;
      jcv_graphedge *graph_edge;
    
      memset(&diagram, 0, sizeof(jcv_diagram));
    
      srand(0);
      for (i=0; i<NPOINT; i++) {
        points[i].x = (float)rand()/(1.0 + RAND_MAX);
        points[i].y = (float)rand()/(1.0 + RAND_MAX);
      }
    
      // seed sites
      //
      for (i=0; i<NPOINT; i++) {
        printf("%f %f\n\n", points[i].x, points[i].y);
      }
    
      jcv_diagram_generate(NPOINT, (const jcv_point *)points, &bounding_box, &diagram);
    
      sites = jcv_diagram_get_sites(&diagram);
      for (i=0; i<diagram.numsites; i++) {
    
        graph_edge = sites[i].edges;
        while (graph_edge) {
          printf("%f %f\n", graph_edge->pos[0].x, graph_edge->pos[0].y);
          printf("%f %f\n", graph_edge->pos[1].x, graph_edge->pos[1].y);
          printf("\n");
    
          graph_edge = graph_edge->next;
        }
    
      }
    
      jcv_diagram_free(&diagram);
    }
    

    The generated seed sites:

    0.840188 0.394383
    0.783099 0.798440
    0.911647 0.197551
    0.335223 0.768230
    0.277775 0.553970
    0.477397 0.628871
    0.364784 0.513401
    0.952230 0.916195
    0.635712 0.717297
    0.141603 0.606969
    

    Visualizing the seed sites and edges with gnuplot:

    jcv_voronoi_example

    This assumes you're in an example subdirectory, say, to compile and run. The number of points is hard coded and it creates the points randomly in a 1x1 box, but the above example gets across clearly how to set up, use and get useful information out of the library. Presumably this 'double covers' the half edges but for illustration purposes I don't think that's a problem.

    I'd be happy to submit a pull request if that's helpful.

    enhancement 
    opened by abetusk 3
  • Bug when generating voronoi clipped in a rectangle with only 2 vertices

    Bug when generating voronoi clipped in a rectangle with only 2 vertices

    I was running simulations using your library, and I found some errors. Here's an example:

    jcv_diagram d{};
    
    // example that fails using double
    //jcv_point points[]
    //{
    //	{888.19238281250000, 377.82843017578125},
    //	{914.00000000000000, 341.00000000000000},
    //};
    
    // example that fails using the standard float version of the library
    jcv_point points[]
    	{
    		{883.382263f, 340.749908f},
    		{850.622253f, 378.323486f},
    	};
    
    jcv_rect rect;
    rect.min = { 600, 250 };
    rect.max = { 1000, 650 };
    const auto count = sizeof(points) / sizeof(*points);
    jcv_diagram_generate(count, points, &rect, 0, &d);
    const jcv_site* sites = jcv_diagram_get_sites(&d);
    for (int i = 0; i != d.numsites; i++)
    {
    	const jcv_site* site = &sites[i];
    	const jcv_graphedge* e = site->edges;
    	int cnt = 0;
    	while (e)
    	{
    		cnt++;
    		e = e->next;
    	}
    	std::cout << cnt << " sides\n";
    }
    
    /* output:
    4 sides
    2 sides
    Obviously wrong. One can clearly see the voronoi should have 5 sides and 3 sides in each cell
    */
    

    I believe it is associated with this issue, but this one is easier to reproduce because of only 2 vertices.

    opened by davi-v 0
  • Access to the site by its index

    Access to the site by its index

    Now, to get access to a separate site by its index, you need to either make a separate array sorted by site indexes, or use a chart search. Maybe there are other ways that I can't see?

    opened by Mikez2015 0
  • How to support clip by nonconvex boundary?

    How to support clip by nonconvex boundary?

    Hi, is there any simple method to enable the library to support clip by nonconvex boundary? I tested when use concave polygon as clip polygon, the result is blank.

    opened by manuel76413 4
  • Assertion error on jcv_diagram_generate

    Assertion error on jcv_diagram_generate

    Hi. first of all, thank you so much for your great OSS. This library is very useful and easy to use. Thanks!!

    I might find an issue of this OSS. When I input a certain data into jcv_diagram_generate function, an assertion was reported:

    Assertion failed: (internal->numsites == 1), function jcv_fillgaps, file src/jc_voronoi.h, line 1143.

    This repo is a fork of your voronoi repo and I added a test code for reproducing the issue. https://github.com/AtsushiSakai/voronoi If you have time, please take a look the file test/assert_text.c file. https://github.com/AtsushiSakai/voronoi/blob/master/test/assert_test.c test/invalid_data.h includes input x-y point data for the issue.

    #38 might be a same issue.

    opened by AtsushiSakai 1
  • Time Complexity Question

    Time Complexity Question

    In my knowledge, the time complexity of Fortune's Sweepline algorithm is O(n log n). This algorithm uses a balanced binary search tree(BBST) to insert/delete parabola and to do a binary search in O(log n).

    I found that this code uses a linked list, instead of BBST. The linked list makes this code O(n^2), and it means this code will take lots of time to calculate Voronoi Diagram in specific inputs.

    Generator of test input is here.

    #!/usr/bin/ruby
    n = 1000000
    n.times do |i|
    	puts "%d %d" % [(i+1), -(i+1)]
    	puts "%d %d" % [-(i+1), -(i+1)]
    end
    

    You can check that your program is almost stopped at this part or this part.

    Actually, an implementation used linked list will work well in the average case.

    opened by zigui-ps 6
Releases(v0.8.0)
  • v0.8.0(Dec 25, 2022)

  • v0.7.0(Nov 2, 2019)

    • Added support for clipping against convex polygons
    • Added JCV_EDGE_INTERSECT_THRESHOLD for edge intersections
    • Fixed issue where the bounds calculation wasn’t considering all points
    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Oct 21, 2018)

  • v0.5.0(Oct 14, 2018)

    • Fixed issue where the graph edge had the wrong edge assigned (issue #28)
    • Fixed issue where a point was falsely passing the jcv_is_valid() test (issue #22)
    • Fixed jcv_diagram_get_edges() so it now returns all edges (issue #28)
    • Added jcv_diagram_get_next_edge() to skip zero length edges (issue #10)
    • Added defines JCV_CEIL/JCV_FLOOR/JCV_FLT_MAX for easier configuration
    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Apr 20, 2017)

  • v0.2.0(Apr 20, 2017)

Owner
Mathias Westerdahl
Engine developer at @defold, a free game engine. Try it out at http://www.defold.com // CTO @refold, https://www.refold.io/
Mathias Westerdahl
MADE (Masked Autoencoder Density Estimation) implementation in PyTorch

pytorch-made This code is an implementation of "Masked AutoEncoder for Density Estimation" by Germain et al., 2015. The core idea is that you can turn

Andrej 498 Dec 30, 2022
Graph Regularized Residual Subspace Clustering Network for hyperspectral image clustering

Graph Regularized Residual Subspace Clustering Network for hyperspectral image clustering

Yaoming Cai 5 Jul 18, 2022
Official implementation of "Accelerating Reinforcement Learning with Learned Skill Priors", Pertsch et al., CoRL 2020

Accelerating Reinforcement Learning with Learned Skill Priors [Project Website] [Paper] Karl Pertsch1, Youngwoon Lee1, Joseph Lim1 1CLVR Lab, Universi

Cognitive Learning for Vision and Robotics (CLVR) lab @ USC 134 Dec 06, 2022
Official code for our ICCV paper: "From Continuity to Editability: Inverting GANs with Consecutive Images"

GANInversion_with_ConsecutiveImgs Official code for our ICCV paper: "From Continuity to Editability: Inverting GANs with Consecutive Images" https://a

QingyangXu 38 Dec 07, 2022
Hyperbolic Procrustes Analysis Using Riemannian Geometry

Hyperbolic Procrustes Analysis Using Riemannian Geometry The code in this repository creates the figures presented in this article: Please notice that

Ronen Talmon's Lab 2 Jan 08, 2023
Animation of solving the traveling salesman problem to optimality using mixed-integer programming and iteratively eliminating sub tours

tsp-streamlit Animation of solving the traveling salesman problem to optimality using mixed-integer programming and iteratively eliminating sub tours.

4 Nov 05, 2022
The PyTorch improved version of TPAMI 2017 paper: Face Alignment in Full Pose Range: A 3D Total Solution.

Face Alignment in Full Pose Range: A 3D Total Solution By Jianzhu Guo. [Updates] 2020.8.30: The pre-trained model and code of ECCV-20 are made public

Jianzhu Guo 3.4k Jan 02, 2023
Black box hyperparameter optimization made easy.

BBopt BBopt aims to provide the easiest hyperparameter optimization you'll ever do. Think of BBopt like Keras (back when Theano was still a thing) for

Evan Hubinger 70 Nov 03, 2022
[ICCV 2021] Excavating the Potential Capacity of Self-Supervised Monocular Depth Estimation

EPCDepth EPCDepth is a self-supervised monocular depth estimation model, whose supervision is coming from the other image in a stereo pair. Details ar

Rui Peng 110 Dec 23, 2022
Hybrid CenterNet - Hybrid-supervised object detection / Weakly semi-supervised object detection

Hybrid-Supervised Object Detection System Object detection system trained by hybrid-supervision/weakly semi-supervision (HSOD/WSSOD): This project is

5 Dec 10, 2022
LIMEcraft: Handcrafted superpixel selectionand inspection for Visual eXplanations

LIMEcraft LIMEcraft: Handcrafted superpixel selectionand inspection for Visual eXplanations The LIMEcraft algorithm is an explanatory method based on

MI^2 DataLab 4 Aug 01, 2022
DALL-Eval: Probing the Reasoning Skills and Social Biases of Text-to-Image Generative Transformers

DALL-Eval: Probing the Reasoning Skills and Social Biases of Text-to-Image Generative Transformers Authors: Jaemin Cho, Abhay Zala, and Mohit Bansal (

Jaemin Cho 98 Dec 15, 2022
KUIELAB-MDX-Net got the 2nd place on the Leaderboard A and the 3rd place on the Leaderboard B in the MDX-Challenge ISMIR 2021

KUIELAB-MDX-Net got the 2nd place on the Leaderboard A and the 3rd place on the Leaderboard B in the MDX-Challenge ISMIR 2021

IELab@ Korea University 74 Dec 28, 2022
An abstraction layer for mathematical optimization solvers.

MathOptInterface Documentation Build Status Social An abstraction layer for mathematical optimization solvers. Replaces MathProgBase. Citing MathOptIn

JuMP-dev 284 Jan 04, 2023
Learning Visual Words for Weakly-Supervised Semantic Segmentation

[IJCAI 2021] Learning Visual Words for Weakly-Supervised Semantic Segmentation Implementation of IJCAI 2021 paper Learning Visual Words for Weakly-Sup

Lixiang Ru 24 Oct 05, 2022
PICK: Processing Key Information Extraction from Documents using Improved Graph Learning-Convolutional Networks

Code for the paper "PICK: Processing Key Information Extraction from Documents using Improved Graph Learning-Convolutional Networks" (ICPR 2020)

Wenwen Yu 498 Dec 24, 2022
Implementation of BI-RADS-BERT & The Advantages of Section Tokenization.

BI-RADS BERT Implementation of BI-RADS-BERT & The Advantages of Section Tokenization. This implementation could be used on other radiology in house co

1 May 17, 2022
Unofficial TensorFlow implementation of Protein Interface Prediction using Graph Convolutional Networks.

[TensorFlow] Protein Interface Prediction using Graph Convolutional Networks Unofficial TensorFlow implementation of Protein Interface Prediction usin

YeongHyeon Park 9 Oct 25, 2022
IDA file loader for UF2, created for the DEFCON 29 hardware badge

UF2 Loader for IDA The DEFCON 29 badge uses the UF2 bootloader, which conveniently allows you to dump and flash the firmware over USB as a mass storag

Kevin Colley 6 Feb 08, 2022
Customer Segmentation using RFM

Customer-Segmentation-using-RFM İş Problemi Bir e-ticaret şirketi müşterilerini segmentlere ayırıp bu segmentlere göre pazarlama stratejileri belirlem

Nazli Sener 7 Dec 26, 2021