This project is an implementation of the incremental voronoi set generator described in the paper Incremental Voronoi sets for instant stippling by Lei Ma, Yanyun Chen, Yinling Qian and Hanqiu Sun.
The paper describes an algorithm for real-time stippling and digital halftoning relying on so-called “Incremental Voronoi Sets” (IVS). An IVS is basically a set of ordered points generated by incrementally adding points to a Delaunay triangulation on a periodic (toroidal) domain. Each point added in sequence is added at the center of the largest current circumcircle of the triangles into the triangulation. This generates a blue noise pattern of points, and crucially the index of each point is inversely proportional to the area it’s Voronoi cell covers.
This repository contains an implementation of the generator for these sets. The generator outputs both a text file with all the points in order, as well as images for debug/visualization purposes. In order to generate the toroidal Delaunay triangulations, it uses the 2D Periodic Delaunay Triangulation generator from CGAL, and Cairo for the graphics.
The dependencies for this project are CGAL, Cairo and GLM. CGAL itself uses Boost, and I noticed that on Arch Linux you needed to install that as well. I haven’t tested it super-thoroughly on different systems, but this should work for macOS (using Homebrew):
brew install cgal cairo boost glm
On Arch Linux, using pacman:
pacman -Sy cgal cairo boost glm
I’m pretty sure it’s the same on most UNIXy systems with package managers. Let me know if you have trouble.
This project uses CMake for it’s build script, so build method is pretty standard. Doing a basic makefile build looks like this:
mkdir build
cd build
cmake ..
make
This will create a binary in the build folder called `ivs`.
Example usage would be
./ivs -n 4096 -c 10 -f ivs.png set.txt
This will create an IVS with 4096 points using 10 seed points, save the final image to ivs.png and save the set to a textfile `set.txt`. The text file contains the coordinates for the points (each coordinate is between 0 and 1) one per line.
Full usage:
Usage: ivs [options] [<output-file>] The <output-file> option is a file to save the finished IVS set into. Each line will have the X and Y coordinates of the dot (in the range [0,1)) separated by a comma. If <output-file> is a "-", then print to stdout instead. Options: -h, --help Print this help text -n, --number Number of total points to generate --seed <n> Seed for RNG -c, --seed-count <n> Number of initial seed points (default = 2) -f, --draw-final <file> Save final image to file -i, --draw-inter <files> Save intermediate images to file Example: ivs_%07d.png --draw-voronoi Draw the voronoi diagram --draw-delaunay Draw the Delaunay triangulation --draw-circumcircles Draw the circumcircles of the triangulation -p, --point-size <n> Radius of a drawn point -l, --line-width <n> Width a drawn line -o, --img-size <n> Saved images size (output images are square, this is the size of one side)
25 points with Delaunay triangulation, Voronoi diagram and circumcircles drawn
./ivs -n 25 --draw-delaunay --draw-voronoi --draw-circumcircles -f n25-decorated.png
Set of 4096 points with 10 seed points
./ivs -n 4096 -c 10 -f c10n4096.png
The implementation is quite performant. The paper suggest using a priority queue to store candidate circumcircles so that the algorithm will not be less than O(n²), which is eactly what I did, and it works very nicely. The details of the algorithm can be found in the file src/ivs.cpp (it’s has lengthy comments and documentation). The generator performs quite well: on my computer it generates an IVS set of 1024*1024 points in little under 3 minutes. The paper states it took them around 3800 seconds, so I’m pretty statisfied with that (most of the credit obviously belongs to CGAL and the papers authors for coming up with the idea in the first place).
I have made a number of Deluanay generators myself over the years, but I chose to go with the one from CGAL because it can generate periodic triangulations over a toroidal domain. The papers authors state that the purpose of this is to make the set tileable. While it is undoubtedly true that this is one benefit, I also suspect that the iterative algorithm generating it only really makes sense on a toroidal domain, because the boundary condition becomes really weird: if you generate a non-periodic Delaunay triangulation from some set of point in a restricted domain, by far the biggest circumcircles will be on triangles lying on the edges, and those circumcircles will be massive, and the circumcircle far away.
This is obviously because if you have a Delaunay triangulation where three points are almost collinear, the circumcircle of those three points will be huge (tending towards infinity). This will only happen on the edges, because in the middle of the triangulation, such a triangle would not be valid (as it would likely contain another point of the triangulation. As such, any new points added iteratively would almost always fall far outside of the domain we’re interested in. Using a periodic triangulation avoids all these pitfalls: all circumcircles are comparatively small.
One issue I have noticed with this algorihtm is that some of the images it generates tend to have large scale structures in them in the form of hexagonal lattices. This is an image of 20000 points showing the effect, with lattices circled:
In the Fourier transform of the image, you can see these sections as white “splotches” in the spectrum:
This is obviously undesirable for stippling purposes. These previous images were generated with two random seed points, and the issue can be lessened by increasing the number seed points. This is 20000 points with 100 seed points, as well as its spectrum:
Upping the seed-points should be used with caution, though: the whole point of the algorithm is that the index of each point is inversely proportional with it’s covered area, and the seed points do not follow these rules. In addition, given that they have the lowest indexes, their “covered area” is by far the largest of any of the points, so they will almost always appear in the final stippling. It might be worth considering skipping these seed points entirely in the final rendering, but the details on that is fuzzy to me. You could also consider doing something like Lloyd relaxation on the initial seeds to be sure they’re a bit spread out, but I haven’t implemented or tried that.
In any case, you have to weigh the seed point count carefully: too few and you get these structures, too many and you miss the point of the technique.
Copyright 2020 Oskar Sigvardsson This file is part of ivs-generator. ivs-generator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ivs-generator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with ivs-generator. If not, see <https://www.gnu.org/licenses/>.