3D: avoid pinching at poles when creating sphere from polar coordinates - math

I'm using Wikipedia's spherical coordinate system article to create a sphere made out of particles in Three.js. Based on this article, I created a small Polarizer class that takes in polar coordinates with setPolar(rho, theta, phi) and it returns its corresponding x, y, z
Here's the setPolar() function:
// Rho: radius
// theta θ: polar angle on Y axis
// phi φ: azimuthal angle on Z axis
Polarizer.prototype.setPolar = function(rho, theta, phi){
// Limit values to zero
this.rho = Math.max(0, rho);
this.theta = Math.max(0, theta);
this.phi = Math.max(0, phi);
// Calculate x,y,z
this.x = this.rho * Math.sin(this.theta) * Math.sin(this.phi);
this.y = this.rho * Math.cos(this.theta);
this.z = this.rho * Math.sin(this.theta) * Math.cos(this.phi);
return this;
}
I'm using it to position my particles as follows:
var tempPolarizer = new Polarizer();
for(var i = 0; i < geometry.vertices.length; i++){
tempPolarizer.setPolar(
50, // Radius of 50
Math.random() * Math.PI, // Theta ranges from 0 - PI
Math.random() * 2 * Math.PI // Phi ranges from 0 - 2PI
);
// Set new vertex positions
geometry.vertices[i].set(
tempPolarizer.x,
tempPolarizer.y,
tempPolarizer.z
);
}
It works wonderfully, except that I'm getting high particle densities, or "pinching" at the poles:
I'm stumped as to how to avoid this from happening. I thought of passing a weighted random number to the latitude, but I'm hoping to animate the particles without the longitude also slowing down and bunching up at the poles.
Is there a different formula to generate a sphere where the poles don't get as much weight? Should I be using quaternions instead?

For random uniform sampling
use random point in unit cube , handle it as vector and set its length to radius of your sphere. For example something like this in C++:
x = 2.0*Random()-1.0;
y = 2.0*Random()-1.0;
z = 2.0*Random()-1.0;
m=r/sqrt(x*x+y*y+z*z);
x*=m;
y*=m;
z*=m;
where Random return number in <0.0,1.0>. For more info see:
Procedural generation of stars with skybox
For uniform non-random sampling
see related QAs:
Sphere triangulation by mesh subdivision
Make a sphere with equidistant vertices

In order to avoid high density at the poles, I had to lower the likelihood of theta (latitude) landing close to 0 and PI. My input of
Math.random() * Math.PI, for theta gives an equal likelihood to all values (orange).
Math.acos((Math.random() * 2) - 1) perfectly weights the output to make 0 and PI less likely along the sphere's surface (yellow)
Now I can't even tell where the poles are!

Related

Calculate a vector in meter from two GPS coordinates

I need to calculate a distance vector from two GPS coordinates.
The purpose is to calculate the vector of one's change in position,
so the coordinates are not far from each other.
I would like to calculate the latitudinal and longitudinal distances in meters.
I found something here,
but this only gives the direction without distance.
Due to the fact that those coordinates are very close in my case,
I made the approximation that the center of the earth and those two points form a triangle.
Thus, I can use the Al Kashi theorem.
Here is the code:
// Common values
double b = EARTH_RADIUS + destination.altitude;
double c = EARTH_RADIUS + this.altitude;
double b2 = b*b;
double c2 = c*c;
double bc2 = 2*b*c;
// Longitudinal calculations
double alpha = destination.longitude - this.longitude;
// Conversion to radian
alpha = alpha * Math.PI / 180;
// Small-angle approximation
double cos = 1 - alpha*alpha/2; //Math.cos(alpha);
// Use the law of cosines / Al Kashi theorem
double x = Math.sqrt(b2 + c2 - bc2*cos);
// Repeat for latitudinal calculations
alpha = destination.latitude - this.latitude;
alpha = alpha * Math.PI / 180;
double cos = 1 - alpha*alpha/2; //Math.cos(alpha);
double y = Math.sqrt(b2 + c2 - bc2*cos);
// Obtain vertical difference, too
double z = destination.altitude - this.altitude;
return new Vector3D(x, y, z);
As you can see,
I have approximated the cosine because the angles are really small.
I think adding the altitude to the earth's radius doesn't give a better approximation,
but since I have it…
I tested it compared to Google Maps for a distance of 38 meters and I got a result of 37.877.
My result might be more accurate! ^^

How to calculate ray in real-world coordinate system from image using projection matrix?

Given n images and a projection matrix for each image, how can i calculate the ray (line) emitted by each pixel of the images, which is intersecting one of the three planes of the real-world coordinate system? The object captured by the camera is at the same position, just the camera's position is different for each image. That's why there is a separate projection matrix for each image.
As far as my research suggests, this is the inverse of the 3D to 2D projection. Since information is lost when projecting to 2D, it's only possible to calculate the ray (line) in the real-world coordinate system, which is fine.
An example projection matrix P, that a calculated based on given K, R and t component, according to K*[R t]
3310.400000 0.000000 316.730000
K= 0.000000 3325.500000 200.550000
0.000000 0.000000 1.000000
-0.14396457836077139000 0.96965263281337499000 0.19760617153779569000
R= -0.90366580603479685000 -0.04743335255026152200 -0.42560419233334673000
-0.40331536459778505000 -0.23984130575212276000 0.88306936201487163000
-0.010415508744
t= -0.0294278883669
0.673097816109
-604.322 3133.973 933.850 178.711
P= -3086.026 -205.840 -1238.247 37.127
-0.403 -0.240 0.883 0.673
I am using the "DinoSparseRing" data set available at http://vision.middlebury.edu/mview/data
for (int i = 0; i < 16; i++) {
RealMatrix rotationMatrix = MatrixUtils.createRealMatrix(rotationMatrices[i]);
RealVector translationVector = MatrixUtils.createRealVector(translationMatrices[i]);
// construct projection matrix according to K*[R t]
RealMatrix projMatrix = getP(kalibrationMatrices[i], rotationMatrices[i], translationMatrices[i]);
// getM returns the first 3x3 block of the 3x4 projection matrix
RealMatrix projMInverse = MatrixUtils.inverse(getM(projMatrix));
// compute camera center
RealVector c = rotationMatrix.transpose().scalarMultiply(-1.f).operate(translationVector);
// compute all unprojected points and direction vector per project point
for (int m = 0; m < image_m_num_pixel; m++) {
for (int n = 0; n < image_n_num_pixel; n++) {
double[] projectedPoint = new double[]{
n,
m,
1};
// undo perspective divide
projectedPoint[0] *= projectedPoint[2];
projectedPoint[1] *= projectedPoint[2];
// undo projection by multiplying by inverse:
RealVector projectedPointVector = MatrixUtils.createRealVector(projectedPoint);
RealVector unprojectedPointVector = projMInverse.operate(projectedPointVector);
// compute direction vector
RealVector directionVector = unprojectedPointVector.subtract(c);
// normalize direction vector
double dist = Math.sqrt((directionVector.getEntry(0) * directionVector.getEntry(0))
+ (directionVector.getEntry(1) * directionVector.getEntry(1))
+ (directionVector.getEntry(2) * directionVector.getEntry(2)));
directionVector.setEntry(0, directionVector.getEntry(0) * (1.0 / dist));
directionVector.setEntry(1, directionVector.getEntry(1) * (1.0 / dist));
directionVector.setEntry(2, directionVector.getEntry(2) * (1.0 / dist));
}
}
}
The following 2 plots show the outer rays for each images (total of 16 images). The blue end is the camera point and the cyan is a bounding box containing the object captured by the camera. One can clearly see the rays projecting back to the object in world coordinate system.
To define the ray you need a start point (which is the camera/eye position) and a direction vector, which can be calculated using any point on the ray.
For a given pixel in the image, you have a projected X and Y (zeroed at the center of the image) but no Z depth value. However the real-world co-ordinates corresponding to all possible depth values for that pixel will all lie on the ray you are trying to calculate, so you can just choose any arbitrary non-zero Z value, since any point on the ray will do.
float projectedX = (x - imageCenterX) / (imageWidth * 0.5f);
float projectedY = (y - imageCenterY) / (imageHeight * 0.5f);
float projectedZ = 1.0f; // any arbitrary value
Now that you have a 3D projected co-ordinate you can undo the projection by applying the perspective divide in reverse by multiplying X and Y by Z, then multiplying the result by the inverse projection matrix to get the unprojected point.
// undo perspective divide (redundant if projectedZ = 1, shown for completeness)
projectedX *= projectedZ;
projectedY *= projectedZ;
Vector3 projectedPoint = new Vector3(projectedX, projectedY, projectedZ);
// undo projection by multiplying by inverse:
Matrix invProjectionMat = projectionMat.inverse();
Vector3 unprojectedPoint = invProjectionMat.multiply(projectedPoint);
Subtract the camera position from the unprojected point to get the direction vector from the camera to the point, and then normalize it. (This step assumes that the projection matrix defines both the camera position and orientation, if the position is stored separately then you don't need to do the subtraction)
Vector3 directionVector = unprojectedPoint.subtract(cameraPosition);
directionVector.normalize();
The ray is defined by the camera position and the normalized direction vector. You can then intersect it with any of the X, Y, Z planes.

Math Help: 3d Modeling / Three.js - Rotating figure dynamically

This is a big one for any math/3d geometry lovers. Thank you in advance.
Overview
I have a figure created by extruding faces around twisting spline curves in space. I'm trying to place a "loop" (torus) oriented along the spline path at a given segment of the curve, so that it is "aligned" with the spline. By that I mean the torus's width is parallel to the spline path at the given extrusion segment, and it's height is perpendicular to the face that is selected (see below for picture).
Data I know:
I am given one of the faces of the figure. From that I can also glean that face's centroid (center point), the vertices that compose it, the surrounding faces, and the normal vector of the face.
Current (Non-working) solution outcome:
I can correctly create a torus loop around the centroid of the face that is clicked. However, it does not rotate properly to "align" with the face. See how they look a bit "off" below.
Here's a picture with the material around it:
and here's a picture with it in wireframe mode. You can see the extrusion segments pretty clearly.
Current (Non-working) methodology:
I am attempting to do two calculations. First, I'm calculating the the angle between two planes (the selected face and the horizontal plane at the origin). Second, I'm calculating the angle between the face and a vertical plane at the point of origin. With those two angles, I am then doing two rotations - an X and a Y rotation on the torus to what I hope would be the correct orientation. It's rotating the torus at a variable amount, but not in the place I want it to be.
Formulas:
In doing the above, I'm using the following to calculate the angle between two planes using their normal vectors:
Dot product of normal vector 1 and normal vector 2 = Magnitude of vector 1 * Magnitude of vector 2 * Cos (theta)
Or:
(n1)(n2) = || n1 || * || n2 || * cos (theta)
Or:
Angle = ArcCos { ( n1 * n2 ) / ( || n1 || * || n2 || ) }
To determine the magnitude of a vector, the formula is:
The square root of the sum of the components squared.
Or:
Sqrt { n1.x^2 + n1.y^2 + n1.z^2 }
Also, I'm using the following for the normal vectors of the "origin" planes:
Normal vector of horizontal plane: (1, 0, 0)
Normal vector of Vertical plane: (0, 1, 0)
I've thought through the above normal vectors a couple times... and I think(?) they are right?
Current Implementation:
Below is the code that I'm currently using to implement it. Any thoughts would be much appreciated. I have a sinking feeling that I'm taking a wrong approach in trying to calculate the angles between the planes. Any advice / ideas / suggestions would be much appreciated. Thank you very much in advance for any suggestions.
Function to calculate the angles:
this.toRadians = function (face, isX)
{
//Normal of the face
var n1 = face.normal;
//Normal of the vertical plane
if (isX)
var n2 = new THREE.Vector3(1, 0, 0); // Vector normal for vertical plane. Use for Y rotation.
else
var n2 = new THREE.Vector3(0, 1, 0); // Vector normal for horizontal plane. Use for X rotation.
//Equation to find the cosin of the angle. (n1)(n2) = ||n1|| * ||n2|| (cos theta)
//Find the dot product of n1 and n2.
var dotProduct = (n1.x * n2.x) + (n1.y * n2.y) + (n1.z * n2.z);
// Calculate the magnitude of each vector
var mag1 = Math.sqrt (Math.pow(n1.x, 2) + Math.pow(n1.y, 2) + Math.pow(n1.z, 2));
var mag2 = Math.sqrt (Math.pow(n2.x, 2) + Math.pow(n2.y, 2) + Math.pow(n2.z, 2));
//Calculate the angle of the two planes. Returns value in radians.
var a = (dotProduct)/(mag1 * mag2);
var result = Math.acos(a);
return result;
}
Function to create and rotate the torus loop:
this.createTorus = function (tubeMeshParams)
{
var torus = new THREE.TorusGeometry(5, 1.5, segments/10, 50);
fIndex = this.calculateFaceIndex();
//run the equation twice to calculate the angles
var xRadian = this.toRadians(geometry.faces[fIndex], false);
var yRadian = this.toRadians(geometry.faces[fIndex], true);
//Rotate the Torus
torus.applyMatrix(new THREE.Matrix4().makeRotationX(xRadian));
torus.applyMatrix(new THREE.Matrix4().makeRotationY(yRadian));
torusLoop = new THREE.Mesh(torus, this.m);
torusLoop.scale.x = torusLoop.scale.y = torusLoop.scale.z = tubeMeshParams['Scale'];
//Create the torus around the centroid
posx = geometry.faces[fIndex].centroid.x;
posy = geometry.faces[fIndex].centroid.y;
posz = geometry.faces[fIndex].centroid.z;
torusLoop.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(posx, posy, posz));
torusLoop.geometry.computeCentroids();
torusLoop.geometry.computeFaceNormals();
torusLoop.geometry.computeVertexNormals();
return torusLoop;
}
I found I was using an incorrect approach to do this. Instead of trying to calculate each angle and do a RotationX and a RotationY, I should have done a rotation by axis. Definitely was over thinking it.
makeRotationAxis(); is a function built into three.js.

Generate a random point within a circle (uniformly)

I need to generate a uniformly random point within a circle of radius R.
I realize that by just picking a uniformly random angle in the interval [0 ... 2π), and uniformly random radius in the interval (0 ... R) I would end up with more points towards the center, since for two given radii, the points in the smaller radius will be closer to each other than for the points in the larger radius.
I found a blog entry on this over here but I don't understand his reasoning. I suppose it is correct, but I would really like to understand from where he gets (2/R2)×r and how he derives the final solution.
Update: 7 years after posting this question I still hadn't received a satisfactory answer on the actual question regarding the math behind the square root algorithm. So I spent a day writing an answer myself. Link to my answer.
How to generate a random point within a circle of radius R:
r = R * sqrt(random())
theta = random() * 2 * PI
(Assuming random() gives a value between 0 and 1 uniformly)
If you want to convert this to Cartesian coordinates, you can do
x = centerX + r * cos(theta)
y = centerY + r * sin(theta)
Why sqrt(random())?
Let's look at the math that leads up to sqrt(random()). Assume for simplicity that we're working with the unit circle, i.e. R = 1.
The average distance between points should be the same regardless of how far from the center we look. This means for example, that looking on the perimeter of a circle with circumference 2 we should find twice as many points as the number of points on the perimeter of a circle with circumference 1.
Since the circumference of a circle (2πr) grows linearly with r, it follows that the number of random points should grow linearly with r. In other words, the desired probability density function (PDF) grows linearly. Since a PDF should have an area equal to 1 and the maximum radius is 1, we have
So we know how the desired density of our random values should look like.
Now: How do we generate such a random value when all we have is a uniform random value between 0 and 1?
We use a trick called inverse transform sampling
From the PDF, create the cumulative distribution function (CDF)
Mirror this along y = x
Apply the resulting function to a uniform value between 0 and 1.
Sounds complicated? Let me insert a blockquote with a little side track that conveys the intuition:
Suppose we want to generate a random point with the following distribution:
That is
1/5 of the points uniformly between 1 and 2, and
4/5 of the points uniformly between 2 and 3.
The CDF is, as the name suggests, the cumulative version of the PDF. Intuitively: While PDF(x) describes the number of random values at x, CDF(x) describes the number of random values less than x.
In this case the CDF would look like:
To see how this is useful, imagine that we shoot bullets from left to right at uniformly distributed heights. As the bullets hit the line, they drop down to the ground:
See how the density of the bullets on the ground correspond to our desired distribution! We're almost there!
The problem is that for this function, the y axis is the output and the x axis is the input. We can only "shoot bullets from the ground straight up"! We need the inverse function!
This is why we mirror the whole thing; x becomes y and y becomes x:
We call this CDF-1. To get values according to the desired distribution, we use CDF-1(random()).
…so, back to generating random radius values where our PDF equals 2x.
Step 1: Create the CDF:
Since we're working with reals, the CDF is expressed as the integral of the PDF.
CDF(x) = ∫ 2x = x2
Step 2: Mirror the CDF along y = x:
Mathematically this boils down to swapping x and y and solving for y:
CDF: y = x2
Swap: x = y2
Solve: y = √x
CDF-1: y = √x
Step 3: Apply the resulting function to a uniform value between 0 and 1
CDF-1(random()) = √random()
Which is what we set out to derive :-)
Let's approach this like Archimedes would have.
How can we generate a point uniformly in a triangle ABC, where |AB|=|BC|? Let's make this easier by extending to a parallelogram ABCD. It's easy to generate points uniformly in ABCD. We uniformly pick a random point X on AB and Y on BC and choose Z such that XBYZ is a parallelogram. To get a uniformly chosen point in the original triangle we just fold any points that appear in ADC back down to ABC along AC.
Now consider a circle. In the limit we can think of it as infinitely many isoceles triangles ABC with B at the origin and A and C on the circumference vanishingly close to each other. We can pick one of these triangles simply by picking an angle theta. So we now need to generate a distance from the center by picking a point in the sliver ABC. Again, extend to ABCD, where D is now twice the radius from the circle center.
Picking a random point in ABCD is easy using the above method. Pick a random point on AB. Uniformly pick a random point on BC. Ie. pick a pair of random numbers x and y uniformly on [0,R] giving distances from the center. Our triangle is a thin sliver so AB and BC are essentially parallel. So the point Z is simply a distance x+y from the origin. If x+y>R we fold back down.
Here's the complete algorithm for R=1. I hope you agree it's pretty simple. It uses trig, but you can give a guarantee on how long it'll take, and how many random() calls it needs, unlike rejection sampling.
t = 2*pi*random()
u = random()+random()
r = if u>1 then 2-u else u
[r*cos(t), r*sin(t)]
Here it is in Mathematica.
f[] := Block[{u, t, r},
u = Random[] + Random[];
t = Random[] 2 Pi;
r = If[u > 1, 2 - u, u];
{r Cos[t], r Sin[t]}
]
ListPlot[Table[f[], {10000}], AspectRatio -> Automatic]
Here is a fast and simple solution.
Pick two random numbers in the range (0, 1), namely a and b. If b < a, swap them. Your point is (b*R*cos(2*pi*a/b), b*R*sin(2*pi*a/b)).
You can think about this solution as follows. If you took the circle, cut it, then straightened it out, you'd get a right-angled triangle. Scale that triangle down, and you'd have a triangle from (0, 0) to (1, 0) to (1, 1) and back again to (0, 0). All of these transformations change the density uniformly. What you've done is uniformly picked a random point in the triangle and reversed the process to get a point in the circle.
Note the point density in proportional to inverse square of the radius, hence instead of picking r from [0, r_max], pick from [0, r_max^2], then compute your coordinates as:
x = sqrt(r) * cos(angle)
y = sqrt(r) * sin(angle)
This will give you uniform point distribution on a disk.
http://mathworld.wolfram.com/DiskPointPicking.html
Think about it this way. If you have a rectangle where one axis is radius and one is angle, and you take the points inside this rectangle that are near radius 0. These will all fall very close to the origin (that is close together on the circle.) However, the points near radius R, these will all fall near the edge of the circle (that is, far apart from each other.)
This might give you some idea of why you are getting this behavior.
The factor that's derived on that link tells you how much corresponding area in the rectangle needs to be adjusted to not depend on the radius once it's mapped to the circle.
Edit: So what he writes in the link you share is, "That’s easy enough to do by calculating the inverse of the cumulative distribution, and we get for r:".
The basic premise is here that you can create a variable with a desired distribution from a uniform by mapping the uniform by the inverse function of the cumulative distribution function of the desired probability density function. Why? Just take it for granted for now, but this is a fact.
Here's my somehwat intuitive explanation of the math. The density function f(r) with respect to r has to be proportional to r itself. Understanding this fact is part of any basic calculus books. See sections on polar area elements. Some other posters have mentioned this.
So we'll call it f(r) = C*r;
This turns out to be most of the work. Now, since f(r) should be a probability density, you can easily see that by integrating f(r) over the interval (0,R) you get that C = 2/R^2 (this is an exercise for the reader.)
Thus, f(r) = 2*r/R^2
OK, so that's how you get the formula in the link.
Then, the final part is going from the uniform random variable u in (0,1) you must map by the inverse function of the cumulative distribution function from this desired density f(r). To understand why this is the case you need to find an advanced probability text like Papoulis probably (or derive it yourself.)
Integrating f(r) you get F(r) = r^2/R^2
To find the inverse function of this you set u = r^2/R^2 and then solve for r, which gives you r = R * sqrt(u)
This totally makes sense intuitively too, u = 0 should map to r = 0. Also, u = 1 shoudl map to r = R. Also, it goes by the square root function, which makes sense and matches the link.
Let ρ (radius) and φ (azimuth) be two random variables corresponding to polar coordinates of an arbitrary point inside the circle. If the points are uniformly distributed then what is the disribution function of ρ and φ?
For any r: 0 < r < R the probability of radius coordinate ρ to be less then r is
P[ρ < r] = P[point is within a circle of radius r] = S1 / S0 =(r/R)2
Where S1 and S0 are the areas of circle of radius r and R respectively.
So the CDF can be given as:
0 if r<=0
CDF = (r/R)**2 if 0 < r <= R
1 if r > R
And PDF:
PDF = d/dr(CDF) = 2 * (r/R**2) (0 < r <= R).
Note that for R=1 random variable sqrt(X) where X is uniform on [0, 1) has this exact CDF (because P[sqrt(X) < y] = P[x < y**2] = y**2 for 0 < y <= 1).
The distribution of φ is obviously uniform from 0 to 2*π. Now you can create random polar coordinates and convert them to Cartesian using trigonometric equations:
x = ρ * cos(φ)
y = ρ * sin(φ)
Can't resist to post python code for R=1.
from matplotlib import pyplot as plt
import numpy as np
rho = np.sqrt(np.random.uniform(0, 1, 5000))
phi = np.random.uniform(0, 2*np.pi, 5000)
x = rho * np.cos(phi)
y = rho * np.sin(phi)
plt.scatter(x, y, s = 4)
You will get
The reason why the naive solution doesn't work is that it gives a higher probability density to the points closer to the circle center. In other words the circle that has radius r/2 has probability r/2 of getting a point selected in it, but it has area (number of points) pi*r^2/4.
Therefore we want a radius probability density to have the following property:
The probability of choosing a radius smaller or equal to a given r has to be proportional to the area of the circle with radius r. (because we want to have a uniform distribution on the points and larger areas mean more points)
In other words we want the probability of choosing a radius between [0,r] to be equal to its share of the overall area of the circle. The total circle area is pi*R^2, and the area of the circle with radius r is pi*r^2. Thus we would like the probability of choosing a radius between [0,r] to be (pi*r^2)/(pi*R^2) = r^2/R^2.
Now comes the math:
The probability of choosing a radius between [0,r] is the integral of p(r) dr from 0 to r (that's just because we add all the probabilities of the smaller radii). Thus we want integral(p(r)dr) = r^2/R^2. We can clearly see that R^2 is a constant, so all we need to do is figure out which p(r), when integrated would give us something like r^2. The answer is clearly r * constant. integral(r * constant dr) = r^2/2 * constant. This has to be equal to r^2/R^2, therefore constant = 2/R^2. Thus you have the probability distribution p(r) = r * 2/R^2
Note: Another more intuitive way to think about the problem is to imagine that you are trying to give each circle of radius r a probability density equal to the proportion of the number of points it has on its circumference. Thus a circle which has radius r will have 2 * pi * r "points" on its circumference. The total number of points is pi * R^2. Thus you should give the circle r a probability equal to (2 * pi * r) / (pi * R^2) = 2 * r/R^2. This is much easier to understand and more intuitive, but it's not quite as mathematically sound.
It really depends on what you mean by 'uniformly random'. This is a subtle point and you can read more about it on the wiki page here: http://en.wikipedia.org/wiki/Bertrand_paradox_%28probability%29, where the same problem, giving different interpretations to 'uniformly random' gives different answers!
Depending on how you choose the points, the distribution could vary, even though they are uniformly random in some sense.
It seems like the blog entry is trying to make it uniformly random in the following sense: If you take a sub-circle of the circle, with the same center, then the probability that the point falls in that region is proportional to the area of the region. That, I believe, is attempting to follow the now standard interpretation of 'uniformly random' for 2D regions with areas defined on them: probability of a point falling in any region (with area well defined) is proportional to the area of that region.
Here is my Python code to generate num random points from a circle of radius rad:
import matplotlib.pyplot as plt
import numpy as np
rad = 10
num = 1000
t = np.random.uniform(0.0, 2.0*np.pi, num)
r = rad * np.sqrt(np.random.uniform(0.0, 1.0, num))
x = r * np.cos(t)
y = r * np.sin(t)
plt.plot(x, y, "ro", ms=1)
plt.axis([-15, 15, -15, 15])
plt.show()
I think that in this case using polar coordinates is a way of complicate the problem, it would be much easier if you pick random points into a square with sides of length 2R and then select the points (x,y) such that x^2+y^2<=R^2.
Solution in Java and the distribution example (2000 points)
public void getRandomPointInCircle() {
double t = 2 * Math.PI * Math.random();
double r = Math.sqrt(Math.random());
double x = r * Math.cos(t);
double y = r * Math.sin(t);
System.out.println(x);
System.out.println(y);
}
based on previus solution https://stackoverflow.com/a/5838055/5224246 from #sigfpe
I used once this method:
This may be totally unoptimized (ie it uses an array of point so its unusable for big circles) but gives random distribution enough. You could skip the creation of the matrix and draw directly if you wish to. The method is to randomize all points in a rectangle that fall inside the circle.
bool[,] getMatrix(System.Drawing.Rectangle r) {
bool[,] matrix = new bool[r.Width, r.Height];
return matrix;
}
void fillMatrix(ref bool[,] matrix, Vector center) {
double radius = center.X;
Random r = new Random();
for (int y = 0; y < matrix.GetLength(0); y++) {
for (int x = 0; x < matrix.GetLength(1); x++)
{
double distance = (center - new Vector(x, y)).Length;
if (distance < radius) {
matrix[x, y] = r.NextDouble() > 0.5;
}
}
}
}
private void drawMatrix(Vector centerPoint, double radius, bool[,] matrix) {
var g = this.CreateGraphics();
Bitmap pixel = new Bitmap(1,1);
pixel.SetPixel(0, 0, Color.Black);
for (int y = 0; y < matrix.GetLength(0); y++)
{
for (int x = 0; x < matrix.GetLength(1); x++)
{
if (matrix[x, y]) {
g.DrawImage(pixel, new PointF((float)(centerPoint.X - radius + x), (float)(centerPoint.Y - radius + y)));
}
}
}
g.Dispose();
}
private void button1_Click(object sender, EventArgs e)
{
System.Drawing.Rectangle r = new System.Drawing.Rectangle(100,100,200,200);
double radius = r.Width / 2;
Vector center = new Vector(r.Left + radius, r.Top + radius);
Vector normalizedCenter = new Vector(radius, radius);
bool[,] matrix = getMatrix(r);
fillMatrix(ref matrix, normalizedCenter);
drawMatrix(center, radius, matrix);
}
First we generate a cdf[x] which is
The probability that a point is less than distance x from the centre of the circle. Assume the circle has a radius of R.
obviously if x is zero then cdf[0] = 0
obviously if x is R then the cdf[R] = 1
obviously if x = r then the cdf[r] = (Pi r^2)/(Pi R^2)
This is because each "small area" on the circle has the same probability of being picked, So the probability is proportionally to the area in question. And the area given a distance x from the centre of the circle is Pi r^2
so cdf[x] = x^2/R^2 because the Pi cancel each other out
we have cdf[x]=x^2/R^2 where x goes from 0 to R
So we solve for x
R^2 cdf[x] = x^2
x = R Sqrt[ cdf[x] ]
We can now replace cdf with a random number from 0 to 1
x = R Sqrt[ RandomReal[{0,1}] ]
Finally
r = R Sqrt[ RandomReal[{0,1}] ];
theta = 360 deg * RandomReal[{0,1}];
{r,theta}
we get the polar coordinates
{0.601168 R, 311.915 deg}
This might help people interested in choosing an algorithm for speed; the fastest method is (probably?) rejection sampling.
Just generate a point within the unit square and reject it until it is inside a circle. E.g (pseudo-code),
def sample(r=1):
while True:
x = random(-1, 1)
y = random(-1, 1)
if x*x + y*y <= 1:
return (x, y) * r
Although it may run more than once or twice sometimes (and it is not constant time or suited for parallel execution), it is much faster because it doesn't use complex formulas like sin or cos.
The area element in a circle is dA=rdr*dphi. That extra factor r destroyed your idea to randomly choose a r and phi. While phi is distributed flat, r is not, but flat in 1/r (i.e. you are more likely to hit the boundary than "the bull's eye").
So to generate points evenly distributed over the circle pick phi from a flat distribution and r from a 1/r distribution.
Alternatively use the Monte Carlo method proposed by Mehrdad.
EDIT
To pick a random r flat in 1/r you could pick a random x from the interval [1/R, infinity] and calculate r=1/x. r is then distributed flat in 1/r.
To calculate a random phi pick a random x from the interval [0, 1] and calculate phi=2*pi*x.
You can also use your intuition.
The area of a circle is pi*r^2
For r=1
This give us an area of pi. Let us assume that we have some kind of function fthat would uniformly distrubute N=10 points inside a circle. The ratio here is 10 / pi
Now we double the area and the number of points
For r=2 and N=20
This gives an area of 4pi and the ratio is now 20/4pi or 10/2pi. The ratio will get smaller and smaller the bigger the radius is, because its growth is quadratic and the N scales linearly.
To fix this we can just say
x = r^2
sqrt(x) = r
If you would generate a vector in polar coordinates like this
length = random_0_1();
angle = random_0_2pi();
More points would land around the center.
length = sqrt(random_0_1());
angle = random_0_2pi();
length is not uniformly distributed anymore, but the vector will now be uniformly distributed.
There is a linear relationship between the radius and the number of points "near" that radius, so he needs to use a radius distribution that is also makes the number of data points near a radius r proportional to r.
I don't know if this question is still open for a new solution with all the answer already given, but I happened to have faced exactly the same question myself. I tried to "reason" with myself for a solution, and I found one. It might be the same thing as some have already suggested here, but anyway here it is:
in order for two elements of the circle's surface to be equal, assuming equal dr's, we must have dtheta1/dtheta2 = r2/r1. Writing expression of the probability for that element as P(r, theta) = P{ r1< r< r1 + dr, theta1< theta< theta + dtheta1} = f(r,theta)*dr*dtheta1, and setting the two probabilities (for r1 and r2) equal, we arrive to (assuming r and theta are independent) f(r1)/r1 = f(r2)/r2 = constant, which gives f(r) = c*r. And the rest, determining the constant c follows from the condition on f(r) being a PDF.
I am still not sure about the exact '(2/R2)×r' but what is apparent is the number of points required to be distributed in given unit 'dr' i.e. increase in r will be proportional to r2 and not r.
check this way...number of points at some angle theta and between r (0.1r to 0.2r) i.e. fraction of the r and number of points between r (0.6r to 0.7r) would be equal if you use standard generation, since the difference is only 0.1r between two intervals. but since area covered between points (0.6r to 0.7r) will be much larger than area covered between 0.1r to 0.2r, the equal number of points will be sparsely spaced in larger area, this I assume you already know, So the function to generate the random points must not be linear but quadratic, (since number of points required to be distributed in given unit 'dr' i.e. increase in r will be proportional to r2 and not r), so in this case it will be inverse of quadratic, since the delta we have (0.1r) in both intervals must be square of some function so it can act as seed value for linear generation of points (since afterwords, this seed is used linearly in sin and cos function), so we know, dr must be quadratic value and to make this seed quadratic, we need to originate this values from square root of r not r itself, I hope this makes it little more clear.
Such a fun problem.
The rationale of the probability of a point being chosen lowering as distance from the axis origin increases is explained multiple times above. We account for that by taking the root of U[0,1].
Here's a general solution for a positive r in Python 3.
import numpy
import math
import matplotlib.pyplot as plt
def sq_point_in_circle(r):
"""
Generate a random point in an r radius circle
centered around the start of the axis
"""
t = 2*math.pi*numpy.random.uniform()
R = (numpy.random.uniform(0,1) ** 0.5) * r
return(R*math.cos(t), R*math.sin(t))
R = 200 # Radius
N = 1000 # Samples
points = numpy.array([sq_point_in_circle(R) for i in range(N)])
plt.scatter(points[:, 0], points[:,1])
A programmer solution:
Create a bit map (a matrix of boolean values). It can be as large as you want.
Draw a circle in that bit map.
Create a lookup table of the circle's points.
Choose a random index in this lookup table.
const int RADIUS = 64;
const int MATRIX_SIZE = RADIUS * 2;
bool matrix[MATRIX_SIZE][MATRIX_SIZE] = {0};
struct Point { int x; int y; };
Point lookupTable[MATRIX_SIZE * MATRIX_SIZE];
void init()
{
int numberOfOnBits = 0;
for (int x = 0 ; x < MATRIX_SIZE ; ++x)
{
for (int y = 0 ; y < MATRIX_SIZE ; ++y)
{
if (x * x + y * y < RADIUS * RADIUS)
{
matrix[x][y] = true;
loopUpTable[numberOfOnBits].x = x;
loopUpTable[numberOfOnBits].y = y;
++numberOfOnBits;
} // if
} // for
} // for
} // ()
Point choose()
{
int randomIndex = randomInt(numberOfBits);
return loopUpTable[randomIndex];
} // ()
The bitmap is only necessary for the explanation of the logic. This is the code without the bitmap:
const int RADIUS = 64;
const int MATRIX_SIZE = RADIUS * 2;
struct Point { int x; int y; };
Point lookupTable[MATRIX_SIZE * MATRIX_SIZE];
void init()
{
int numberOfOnBits = 0;
for (int x = 0 ; x < MATRIX_SIZE ; ++x)
{
for (int y = 0 ; y < MATRIX_SIZE ; ++y)
{
if (x * x + y * y < RADIUS * RADIUS)
{
loopUpTable[numberOfOnBits].x = x;
loopUpTable[numberOfOnBits].y = y;
++numberOfOnBits;
} // if
} // for
} // for
} // ()
Point choose()
{
int randomIndex = randomInt(numberOfBits);
return loopUpTable[randomIndex];
} // ()
1) Choose a random X between -1 and 1.
var X:Number = Math.random() * 2 - 1;
2) Using the circle formula, calculate the maximum and minimum values of Y given that X and a radius of 1:
var YMin:Number = -Math.sqrt(1 - X * X);
var YMax:Number = Math.sqrt(1 - X * X);
3) Choose a random Y between those extremes:
var Y:Number = Math.random() * (YMax - YMin) + YMin;
4) Incorporate your location and radius values in the final value:
var finalX:Number = X * radius + pos.x;
var finalY:Number = Y * radois + pos.y;

Bounding Boxes for Circle and Arcs in 3D

Given curves of type Circle and Circular-Arc in 3D space, what is a good way to compute accurate bounding boxes (world axis aligned)?
Edit: found solution for circles, still need help with Arcs.
C# snippet for solving BoundingBoxes for Circles:
public static BoundingBox CircleBBox(Circle circle)
{
Point3d O = circle.Center;
Vector3d N = circle.Normal;
double ax = Angle(N, new Vector3d(1,0,0));
double ay = Angle(N, new Vector3d(0,1,0));
double az = Angle(N, new Vector3d(0,0,1));
Vector3d R = new Vector3d(Math.Sin(ax), Math.Sin(ay), Math.Sin(az));
R *= circle.Radius;
return new BoundingBox(O - R, O + R);
}
private static double Angle(Vector3d A, Vector3d B)
{
double dP = A * B;
if (dP <= -1.0) { return Math.PI; }
if (dP >= +1.0) { return 0.0; }
return Math.Acos(dP);
}
One thing that's not specified is how you convert that angle range to points in space. So we'll start there and assume that the angle 0 maps to O + r***X** and angle π/2 maps to O + r***Y**, where O is the center of the circle and
X = (x1,x2,x3)
and
Y = (y1,y2,y3)
are unit vectors.
So the circle is swept out by the function
P(θ) = O + rcos(θ)X + rsin(θ)Y
where θ is in the closed interval [θstart,θend].
The derivative of P is
P'(θ) = -rsin(θ)X + rcos(θ)Y
For the purpose of computing a bounding box we're interested in the points where one of the coordinates reaches an extremal value, hence points where one of the coordinates of P' is zero.
Setting -rsin(θ)xi + rcos(θ)yi = 0 we get
tan(θ) = sin(θ)/cos(θ) = yi/xi.
So we're looking for θ where θ = arctan(yi/xi) for i in {1,2,3}.
You have to watch out for the details of the range of arctan(), and avoiding divide-by-zero, and that if θ is a solution then so is θ±k*π, and I'll leave those details to you.
All you have to do is find the set of θ corresponding to extremal values in your angle range, and compute the bounding box of their corresponding points on the circle, and you're done. It's possible that there are no extremal values in the angle range, in which case you compute the bounding box of the points corresponding to θstart and θend. In fact you may as well initialize your solution set of θ's with those two values, so you don't have to special case it.

Resources