Random Point on a given Sphere
Asked Answered
S

3

15

I want to select random points on a given sphere. This page explains it quite well:

http://mathworld.wolfram.com/SpherePointPicking.html ("To obtain points such that any small area on the sphere...")

But I'm not entirely sure if I'm implementing it correctly in JavaScript, as I have little means of testing it properly:

var u = random();
var v = random();
var angle1 = 2 * Math.PI * u;
var angle2 = Math.pow(Math.cos (2 * v - 1), -1);
X = X0 + (radius * Math.sin(angle1) * Math.cos(angle2));
Y = Y0 + (radius * Math.sin(angle1) * Math.sin(angle1));
Z = Z0 + (radius * Math.cos(angle1));

I'm especially unsure about if I've understood that cos(-1) correctly, which I implemented as "The cosine to the power of -1".

Susannsusanna answered 3/4, 2011 at 19:15 Comment(2)
"cos^-1" means "inverse cosine", AKA "arccos" (or "acos").Popular
You can partially test the answer by making sure that sqrt(x^2+y^2+z^2) (without x0/y0/z0 offsets) is equal to radius +/- a small tolerance for floating-point errors.Dorris
A
36

The cube algorithm will not give an even distribution over the sphere - in particular the areas near the projections of the corners will have the densest distribution of points and near the centers of the faces of the cubes will be the lowest.

You can understand this intuitively since the volume of cube projected onto the underlying sphere is larger near the corners that near the centers of the cubes faces. In fact, the volume of a small piece (that projects on a small circle on the sphere) is proportional to the cube of size of the vector from the origin through the center of the small circle to the point on the sphere that it intersects.

So the relative volume on a cube face center (like (1,0,0)) is 1, but for a corner (e.g., (1,1,1)) is cube of sqrt(3) or 1.73 cubed, about 5.2, so almost 5 times denser!

The spreadPoints() function might do a better job, but I'm not sure.

There are a couple of errors in you JavaScript - the use of the pow(..,-1) function instead of acos(), mix ups on the angles and missing the Math object for the random() call.,

Here is similar but correct JavaScript to do what the Wolfram links says:

/*
Returns a random point of a sphere, evenly distributed over the sphere.
The sphere is centered at (x0,y0,z0) with the passed in radius.
The returned point is returned as a three element array [x,y,z]. 
*/
function randomSpherePoint(x0,y0,z0,radius){
   var u = Math.random();
   var v = Math.random();
   var theta = 2 * Math.PI * u;
   var phi = Math.acos(2 * v - 1);
   var x = x0 + (radius * Math.sin(phi) * Math.cos(theta));
   var y = y0 + (radius * Math.sin(phi) * Math.sin(theta));
   var z = z0 + (radius * Math.cos(phi));
   return [x,y,z];
}
Affrica answered 24/2, 2013 at 3:14 Comment(1)
What about if we want a random point within the volume of a sphere?Covert
A
24

I think that an easier algorithm is

  1. Pick a random point inside the [-1,1]x[-1,1]x[-1,1] cube
  2. If x*x + y*y + z*z > 1 repeat from 1
  3. Normalize dividing x, y and z by Math.sqrt(x*x + y*y + z*z)

in other words just pick a random point inside the sphere and project on the sphere. Don't worry too much about the "loop" because the probability of a point being outside the sphere is about 0.4764 and on average the loop will require less than two iterations.

You can see this algorithm in action on this link. Note that if you use chrome there will be some banding around an equator that in my opinion is a bug in Math.random or just a low quality random generator (works fine on Firefox or Safari, but the same problem is visible also on Android browser). The banding is much more visible with an higher number of points (e.g. 10000 instead of the 1000 points I'm using now to keep animation smooth). EDIT: This bug has now been fixed on chrome and Android.

Note that if you're looking for a method to distribute points evenly over a sphere you can do something nicer by choosing ten random points as described above and then accepting only the one with the biggest 3d distance from the set of already chosen points. This is still globally random (i.e. the probablity that a disc with a prescribed radius will receive a point is the same for all discs on the sphere) but will distribute points better if you need to do a "sampling" of the sphere. This function is coded as spreadPoints() in the html file pointed by the link.

You can see the difference between the two approaches here:

enter image description here

Both spheres have 1000 random points drawn on them: the sphere on the left used uniform random points, the sphere on the right instead made the choice by picking each point among ten random candidates to maximize the distance from already chosen points.

Aargau answered 3/4, 2011 at 23:9 Comment(2)
Is this really the optimal algorithm? I would expect one that doesn't throw away so many points would better?Pinkney
Optimality is not a scalar value, you have to choose the criteria. The algorithm in the question requires more sophisticated computations, while the one shown here has simpler computations but a loop that on average will run less than two times but that however requires three random numbers per iteration. Is it better or worse? ... as it often happens in computing the reply is "it depends".Aargau
B
0

This should be slightly more efficient. See https://karlsims.com/random-in-sphere.html for a visual explanation.

function randomSpherePoint(x0,y0,z0,radius) {
   var y = Math.random() * 2 - 1;  // random y from -1 to 1
   var r = Math.sqrt(1 - y*y);     // radius on xz plane at y
   var long = Math.random() * 2 * Math.PI;  // random longitude
   return [x0 + radius * r * Math.sin(long),
           y0 + radius * y,
           z0 + radius * r * Math.cos(long)];
}
Bodiless answered 26/6 at 19:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.