The problem of distributing a point cloud as per an arbitrary function is isomorphic with dithering a greyscale image to a black and white image. The algorithms all have runtime complexity proportional to O(N) (where N is the number of points in the original image/bitmap/grid/whatever), with simple approaches like Bayer matrix ordered dithering performing only one floating-point comparison per point, and more complex error diffusion methods running at more like O(24N) but producing better results with fewer artifacts.
Using a simple Gaussian density function, the simple Bayer matrix approach yields surprisingly good results with as small as a 32x32 matrix:
![Bayer matrix dither]()
A matrix-based ordered dither can be generated in a straightforward manner given the matrix in question:
matrix = generate_bayer(32);
for (y=0; y<height; y++) {
for (var x=0; x<width; x++) {
i = x % matrix.length;
j = y % matrix.length;
function_val = gaussian(x, y); #returns a value 0<=x<=1
scaled_val = function_val * matrix.length * matrix.length; #scale to the max element in the matrix
if (scaled_val > matrix[j][i]) {
draw_pixel(x, y);
}
}
}
Generating the matrix is slightly more tricky, but here's an iterative approach for matricies where the side length is a power of 2:
//size should be a power of 2, and is the length of a side of the matrix
function generate_bayer(size) {
base = [[0, 2],
[3, 1]];
old_matrix = base;
mult = 4;
//successively double the size of the matrix, using the previous one as the base
while (old_matrix.length < size) {
//generate a new matrix of the proper dimensions
new_size = old_matrix.length * 2;
matrix = new Array[new_size][new_size];
for (y=0; y<old_matrix.length; y++) {
for (x=0; x<old_matrix[y].length; x++) {
//fill in the new 4x4 submatrix
matrix[y*2] [x*2] = old_matrix[y][x] + (mult * base[0][0]);
matrix[y*2] [x*2 + 1] = old_matrix[y][x] + (mult * base[0][1]);
matrix[y*2 + 1][x*2] = old_matrix[y][x] + (mult * base[1][0]);
matrix[y*2 + 1][x*2 + 1] = old_matrix[y][x] + (mult * base[1][1]);
}
}
mult *= 4;
old_matrix = matrix;
}
return old_matrix;
}