Domanda

Given a (possibly open) mesh with a density texture and a number of points I need to distribute these points according to the density on the mesh.

So far I have made up several solutions some of which worked and others did not. One of the algorithms I tried was to have the points being connected by springs and simulate the distribution until equilibrium (or until the solution fits for the users needs). Source Retiling Polygonal Surfaces Unfortunately this is somewhat slow for higher number of points (>2k) so I need a feasible solution for higher numbers.

I already have some ideas but I would like to hear if there are some standard solutions for this. I tried google but the keywords I used (distribution density discrete) only turned up pages that handle with other problems than mine. So I would already be happy if you point me to the right words to search for.

È stato utile?

Soluzione

In general, across an arbitrary space with an arbitrary density function, you could get a reasonable approximation via rejection sampling.

  • Find your maximum density D.
  • Pick a random point p.
  • Pick a random number r from the range [0,D).
  • If the density at p is greater than r, accept p as one of your points.

I'm not sure how easy this will be to apply to your case. Generating random, uniformly distributed points across a mesh sounds like a tricky problem in itself. The only solution I can think of is to calculate the area of every triangle in your mesh, randomly pick a triangle with probability proportional to its area, and pick a random point within the triangle. I believe you could do this selection in O(logN) on N triangles.

But considering that you might be throwing most of these points away, this could turn out much worse than your current approach, given a large enough mesh and an unpleasant enough density function (i.e. one with a maximum much greater than the average).


Like any kind of random sampling, it takes quite a few points before it will start to resemble the underlying distribution; a small set of points will probably look more or less random. But you might be able to get around this with some kind of quasi-random method of point placement (and even with a large set, it might give better results).

One thing which comes to mind is a hybrid of the above approach and @BlackBear's algorithm. You could calculate the area and average density for each triangle, and use this to decide how many points each one must contain. Then, to actually place the points within the triangle, use the rejection sampling method.

Altri suggerimenti

Here's my idea:

  1. Split the density texture in equal rectangle-shaped parts;
  2. For each part compute a number between 0.0 and 1.0, where 1.0 means "highest density of points" and 0.0 means the opposite, "no points at all";
  3. Compute how many points correspond to the 1.0 value, by dividing the total amount of points you want to place and the sum of all averages;
  4. Then you know how many points you have to draw in every rectangle so draw them uniformly;

Now, the smaller are the rectangles, the more precise is the result you get. I did a small test program (C# using slow methods i.e. GetPixel and SetPixel on a bitmap) and here are the results, assuming 10k points, a 256x256 px image. Pictures show the algorithm work with 2^2, 10^2, 50^2 and 90^2 parts:

enter image description here

And this is the benchmark:

parts   time
------------------------
2      00:00:00.7957087
10     00:00:00.6713345
50     00:00:00.5873524
90     00:00:00.4153544

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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top