1
\$\begingroup\$

There are particles in a box, the black circle representing the spawn point and the red circle representing the particles.

How could I spawn them so they don't spawn outside the box, and so that they are at an exact distance away from the spawn point and particles can slightly overlap.

Here is an example of what I'm trying to do:

enter image description here

\$\endgroup\$
0
\$\begingroup\$

You can pick a random location an exact distance away from the spawn point, first pick a random angle, find the x & y components of that angle, scale out by the desired distance & add the results to the spawn point to get the new coord:

double spawnDistance = 10;
Point.Double spawnPoint = new Point.Double(15, 17);
Random rng = new Random();
double value = rng.nextDouble();
double dx = Math.cos(value*Math.PI*2) * spawnDistance;
double dy = Math.sin(value*Math.PI*2) * spawnDistance;
Point.Double randomSpawn = new Point.Double(spawnPoint.x+dx, spawnPoint.y+dy);
}

To make sure the newest spawn point doesn't clip the boundary, test that its center is at least as far away from the boundary edges as the size of its radius:

boolean clippedByBoundary = (randomSpawn.x - radius < minBoundary.x ||
                             randomSpawn.y - radius < minBoundary.y ||
                             randomSpawn.x + radius > maxBoundary.x ||
                             randomSpawn.y + radius > maxBoundary.y);   
// check clippedByBoundary & do whatever

To prevent the newest spawn point from overlapping other spawn points by too much, you'll need to check that it isn't too close to any other point generated thus far:

boolean isTooClose = false;
for(Point.Double pt : spawnPointList){
   if(pt.distance(randomSpawn) < maxOverlap){
     isTooClose = true;
     break;
   }
}
// check isTooClose & whatever you need based on the result

Note: the code above could be sped up a bit by checking squared distances instead of actual distances. Also, my solution has a bias for picking locations closer to the center of the spawn area. Uniform disk picking requires a couple more square root calculations:

double dx = Math.cos(value*Math.PI*2) * Math.sqrt(spawnDistance);
double dy = Math.sin(value*Math.PI*2) * Math.sqrt(spawnDistance);

Alternatively, you could also create a random 2D vector & normalize/scale it as needed (as suggested in comments by @Bálint).

For the convenience of answering, I put the test results in variables; you'll probably want to just use the tests directly in line or possibly as method calls depending on your situation. Since testing for edge clipping is cheaper, I would do that before checking for edge overlaps. If you need to generate many points, looping to check for overlap might prove to be a bit of a bottleneck - if that's the case, you might want to use a Poisson distribution placement scheme instead.

\$\endgroup\$
  • 2
    \$\begingroup\$ You could just create a random vector, normalize it and call it a day. It's just a sqrt and a division instead of two trgonometric functions. \$\endgroup\$ – Bálint Feb 20 '17 at 9:47
  • \$\begingroup\$ By 'create a random vector' do you mean pick a random x & a random y? \$\endgroup\$ – Pikalek Feb 20 '17 at 15:36
  • 1
    \$\begingroup\$ yep. Also, you made a small mistake, y should be calculated with sinus. \$\endgroup\$ – Bálint Feb 21 '17 at 7:13
  • \$\begingroup\$ Yes, both are good points. At first I thought the random vector approach might have some sort of bias, but discovered that it was my solution that has a bias. Thanks! \$\endgroup\$ – Pikalek Feb 21 '17 at 15:50
  • \$\begingroup\$ Mine definitely has some bias. The points that lie diagonlly from the center have √2 as mich chance getting picked as the ones that lie vertically and horizontally. \$\endgroup\$ – Bálint Feb 21 '17 at 22:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.