Arrange X amount of things evenly around a point in 3d space - math

If I have X amount of things (lets just randomly say 300)
Is there an algorithm that will arrange these things somewhat evenly around a central point? Like a 100 sided dice or a 3d mesh of a sphere?
Id rather have the things somewhat evenly spaced like this..
Rather than this polar way..
ps. For those interested, wondering why do I want to do this?
Well I'm doing these for fun, and after completing #7 I decided I'd like to represent the array of wires in 3d in Unity and watch them operate in a slowed down manner.

Here is a simple transformation that maps a uniform sample in the rectangle [0, 2 pi] x [-1, 1] onto a uniform sample on the sphere of radius r:
T(phi, z) = (r cos(phi) sqrt(1 - z^2), r sin(phi) sqrt(1 - zˆ2), r z)
The reason why this transformation produces uniform samples on the sphere is that the area of any region T(U) obtained by transforming the region U from the rectangle does not depend on U but on the area of U.
To prove this mathematically it is enough to verify that the norm of the vectorial product
| ∂T/∂phi x ∂T/∂z |
is constant (the area on the sphere is the integral of this vectorial product w.r.t. phi and z).
Summarizing
To produce a random sample uniformly distributed in the Sphere of radius r do the following:
Produce a random sample (phi_1, ..., phi_n) uniformly distributed in [0, 2 pi].
Produce a random sample (z_1, ..., z_n) uniformly distributed in [-1, 1].
For every pair (phi_j, z_k) calculate T(phi_j, z_k) using the formula above.

Here's a three-step approach. 1a) Make more points than you need. 1b) Remove some. 2) Adjust the rest.
1a) To make more points that you need, take any quasiregular polyhedron with sides that tessellate (triangles, squares, diamonds). Tesselate the spherical faces by subdivision, generating more vertices. For example, if you use the regular icosahedron you get geodesic domes. (Subdivide by 2, you get the dual to the C60 buckyball.) Working out exact formulas isn't hard. The number of new vertices per face is quadratic in the subdivision.
1b) Randomly remove enough points to get you down to your target number.
2) Use a force-directed layout algorithm to redistribute the vertices over the sphere. The underlying force graph is just that provided by the nearest neighbors in your underlying tesselation.
There are other ways to do step 1), such as just generating random points in any distribution. There is an advantage of starting with a quasiregular figure, though. Force-directed algorithms have a reputation for poor convergence in some cases. By starting with something that's already mostly optimal, you'll bypass most all of any convergence problems you might have.

One elegant solution I came across recently is a spherical fibonacci lattice (http://extremelearning.com.au/how-to-evenly-distribute-points-on-a-sphere-more-effectively-than-the-canonical-fibonacci-lattice/)
The nice thing about it is that you can specify the exact number of points you want
// C# Code example
Vector3[] SphericalFibonacciLattice(int n) {
Vector3[] res = new Vector3[n];
float goldenRatio = (1.0f + MathF.Sqrt(5.0f)) * 0.5f;
for(int i = 0; i < n; i++)
{
float theta = 2.0f * MathF.PI * i / goldenRatio;
float phi = MathF.Acos(1.0f - 2.0f * (i + 0.5f) / n);
Vector3 p = new Vector3(MathF.Cos(theta) * MathF.Sin(phi),
MathF.Sin(theta) * MathF.Sin(phi),
MathF.Cos(phi));
res[i] = p;
}
return res;
}
The linked article extends on this to create an even more uniform distribution, but even this basic version creates very nice results.

Related

Uniform sampling (by volume) within a cone

I'm looking for an algorithm that can generate points within a cone with a flat bottom (a disk).
I have the normalized axis along which the cone is being created (for our purposes let's just say it is the y-axis so (0, 1, 0) and the angle of the cone (let's say it is 45 degrees).
The only resources I could find online generate vectors within a cone, but they are based on sampling a sphere, so at the bottom you get a kind of "snow-cone" effect instead of a disk at the bottom.
That is done with the following pseudocode:
// Sample phi uniformly on [0, 2PI]
float phi = rand(0, 1) * 2 * PI
// Sample u uniformly from [cos(angle), 1]
float u = rand(0, 1) * (1 - cos(angle * PI/180)) + cos(angle * PI/180)
vec3 = vec3(sqrt(1 - u^2) * cos(phi), u, sqrt(1 - u^2) * sin(phi)))
The below picture is what I am going for. Having the ability to generate samples either on the surface or inside would be nice as well:
I could explain my solution in detail using integrals and probability distributions, but the lack of MathJax on this site makes that difficult. I'll keep my explanation at a simple level, but it should be clear. I'll also make the solution a little more general than you ask: we want a random point inside a right circular cone of height a and radius of base b, and we want the point with uniform sampling over the volume of that cone. This method directly chooses a random point in the cone without any rejection testing.
First let's consider the small cone of height h inside that larger cone, both cones with the same apex and parallel bases. The two cones are of course similar figures, and the square-cube law says that the volume of the smaller cone varies as the cube of its height. That height varies from 0 to a and we want its cube to be uniform over that range. Therefore we choose h to vary with the cube root of a uniform random variable, and we get (in Python 3 code),
h = a * (random()) ** (1/3)
We next consider the circular region that is the base of that smaller cone of height h. The radius of that base is (b / a) * h, by similar triangles. Now think of a smaller circular region of radius r inside that larger circular region, both circles in the same plane and with the same center. The area of the smaller circle varies with the square of its radius, so to get a uniform area over its range we take the square root of a uniform random variable. We get
r = (b / a) * h * sqrt(random())
We now want the angle t (for theta) of a point on the circumference of that smaller circle of radius r. The angle in radians obviously does not depend on the other factors, so we just use a uniform random variable to get
t = 2 * pi * random()
We now use those three random variables h, r, and t to choose our point inside the starting cone. If the apex of the cone is at the origin and the axis of the cone is along the positive y-axis, so that the center of the base is (0, a, 0) and a point on the circumference of the base is (b, a, 0), you can choose
x = r * cos(t)
y = h
z = r * sin(t)
When you asked about generating samples "on the surface" you did not clarify if you mean just the side (or is it "sides"?) of the cone, just the base, or the entire surface. Your second graphic appears to mean just the side, but I'll give code for all three.
The side only
Again we use a smaller cone of height h inside the larger cone. Its surface area varies as the square of its height, so we take the square root of a uniform random variable. The circle in its base is fixed, if our point is to be on the surface, and again the angle is just uniform. So we get
h = a * sqrt(random())
r = (b / a) * h
t = 2 * pi * random()
Use the same code for x, y, and z I used above for the interior of the cone to get the final random point on the side surface of the cone.
The base only
This is much like choosing a point in the interior, except the height is predetermined to equal the height of the entire cone. We get the following, somewhat simplified code:
h = a
r = b * sqrt(random())
t = 2 * pi * random()
Again, use the previous code for the final x, y, and z.
The entire surface
Here we can first decide, at random, whether to place our point on the base or on the surface, then place the point in one of the two ways above. The area of the base of a cone of height a and base radius b is pi * b * b while the surface area of the cone's side is pi * b * sqrt(a*a + b*b). We use the ratio of the base to the total of those areas to choose which subsurface to use for our point:
if random() < b / (b + sqrt(a*a + b*b)):
return point_on_base(a, b)
else:
return point_on_side(a, b)
Use my codes above for the side and base to complete that code.
Here are simple matplotlib 3D scatter plots of 10,000 random points, first inside the cone then on its side surface. Note that I made the apex angle 45°, as your text states but unlike your pictures. Viewing these from other angles seems to confirm that they are uniform in volume or area.

Generating random points on a surface of an n-dimensional torus

I'd like to generate random points being located on the surface of an n-dimensional torus. I have found formulas for how to generate the points on the surface of a 3-dimensional torus:
x = (c + a * cos(v)) * cos(u)
y = (c + a * cos(v)) * sin(u)
z = a * sin(v)
u, v ∈ [0, 2 * pi); c, a > 0.
My question is now: how to extend this formulas to n dimensions. Any help on the matter would be much appreciated.
I guess that you can do this recursively. Start with a full orthonormal basis of your vector space, and let the current location be the origin. At each step, choose a point in the plane spanned by the first two coordinate vectors, i.e. take w1 = cos(t)*v1 + sin(t)*v2. Shift the other basis vectors, i.e. w2 = v3, w3 = v4, …. Also take a step from your current position in the direction w1, with the radius r1 chosen up front. When you only have a single basis vector remaining, then the current point is a point on the n-dimensional torus of the outermost recursive call.
Note that while the above may be used to choose points randomly, it won't choose them uniformly. That would likely be a much harder question, and you definitely should ask about the math of that on Math SE or perhaps on Cross Validated (Statistics SE) to get the math right before you worry about implementation.
An n-torus (n being the dimensionality of the surface of the torus; a bagel or doughnut is therefore a 2-torus, not a 3-torus) is a smooth mapping of an n-rectangle. One way to approach this is to generate points on the rectangle and then map them onto the torus. Aside from the problem of figuring out how to map a rectangle onto a torus (I don't know it off-hand), there is the problem that the resulting distribution of points on the torus is not uniform even if the distribution of points is uniform on the rectangle. But there must be a way to adjust the distribution on the rectangle to make it uniform on the torus.
Merely generating u and v uniformly will not necessarily sample uniformly from a torus surface. An additional step is needed.
J.F. Williamson, "Random selection of points distributed on curved surfaces", Physics in Medicine & Biology 32(10), 1987, describes a general method of choosing a uniformly random point on a parametric surface. It is an acceptance/rejection method that accepts or rejects each candidate point depending on its stretch factor (norm-of-gradient). To use this method for a parametric surface, several things have to be known about the surface, namely—
x(u, v), y(u, v) and z(u, v), which are functions that generate 3-dimensional coordinates from two dimensional coordinates u and v,
The ranges of u and v,
g(point), the norm of the gradient ("stretch factor") at each point on the surface, and
gmax, the maximum value of g for the entire surface.
For the 3-dimensional torus with the parameterization you give in your question, g and gmax are the following:
g(u, v) = a * (c + cos(v) * a).
gmax = a * (a + c).
The algorithm to generate a uniform random point on the surface of a 3-dimensional torus with torus radius c and tube radius a is then as follows (where RNDEXCRANGE(x,y) returns a number in [x,y) uniformly at random, and RNDRANGE(x,y) returns a number in [x,y] uniformly at random):
// Maximum stretch factor for torus
gmax = a * (a + c)
while true
u = RNDEXCRANGE(0, pi * 2)
v = RNDEXCRANGE(0, pi * 2)
x = cos(u)*(c+cos(v)*a)
y = sin(u)*(c+cos(v)*a)
z = sin(v)*a
// Norm of gradient (stretch factor)
g = a*abs(c+cos(v)*a)
if g >= RNDRANGE(0, gmax)
// Accept the point
return [x, y, z]
end
end
If you have n-dimensional torus generating formulas, a similar approach can be used to generate uniform random points on that torus (accept a candidate point if norm-of-gradient equals or exceeds a random number in [0, gmax), where gmax is the maximum norm-of-gradient).

Algorithm to find all points on a 2D grid some distance away from another point

I have some point on a 2D grid (x, y) and I need to find all points that are n distance away from that point. The way I'm measuring distance is by using the distance formula between the two points. Anyone know how to do this?
Edit: Just for reference, what I'm trying to do is to write some AI path finding that will maintain some distance away from a target in a system that uses grid based locations. Currently I'm using A* path finding, but I'm not sure if that matters or makes a difference since I'm kind of new to this stuff.
Here's what I would do:
First filter out all points that are further than D on either x or y. These are certainly outside the circle of radius D. This is a much simpler computation, and it can quickly eliminate a lot of work. This is a outer bounding-box optimization.
You can also use an inner bounding-box optimization. If the points are closer than D * sqrt(2)/2 on either x or y, then they're certainly within the circle of radius D. This is also cheaper than calculating the distance formula.
Then you have a smaller number of candidate points that may be within the circle of radius D. For these, use the distance formula. Remember that if D = sqrt(Δx2+Δy2), then D2 = Δx2+Δy2.
So you can skip the cost of calculating square root.
So in pseudocode, you could do the following:
for each point
begin
if test 1 indicates the point is outside the outer bounding box,
then skip this point
if test 2 indicates the point is inside the inner bounding box,
then keep this point
if test 3 indicates the point is inside the radius of the circle,
then keep this point
end
This problem is known as range query. The brute force solution is just as you described: computed the distance of all points from the reference point and return those whose distance is less than the desired range value.
The brute force algorithm is O(N^2). There are, however, more efficient algorithms that employ spatial indexes to reduce algorithm complexity and the number of distance calculations. For example, you can use a R-Tree to index your points.
Its called nearest neighbor search. More at http://en.wikipedia.org/wiki/Nearest_neighbor_search
There are open libraries for that. I have used one written for C and recommend it: http://www.cs.umd.edu/~mount/ANN/. ANN stands for Approximate Nearest Neighbor, however, you can turn the approximation off and find the exact nearest neighbors.
This wouldn't use the distance formula, but if you're looking for points exactly n distance away, perhaps you could use sin/cos?
In pseudocode:
for degrees in range(360):
x = cos(degrees) * n
y = sin(degrees) * n
print x, y
That would print every point n away in 360 degree increments.
Java implementation:
public static Set<Point> findNearbyPoints(Set<Point> pts, Point centerPt, double radius) {
Set<Point> nearbyPtsSet = new HashSet<Point>();
double innerBound = radius * (Math.sqrt(2.0) / 2.0);
double radiusSq = radius * radius;
for (Point pt : pts) {
double xDist = Math.abs(centerPt.x - pt.x);
double yDist = Math.abs(centerPt.y - pt.y);
if (xDist > radius || yDist > radius)
continue;
if (xDist > innerBound || yDist > innerBound)
continue;
if (distSq(centerPt, pt) < radiusSq)
nearbyPtsSet.add(pt);
}
return nearbyPtsSet;
}

Scale-agnostic, differentiable, co-planarity measure

I am looking for an (almost everywhere) differentiable function f(p1, p2, p3, p4) that given four points will give me a scale-agnostic measure for co-planarity. It is zero if the four points lie on the same plane and positive otherwise. Scale-agnostic means that, when I uniformly scale all points the planarity measure will return the same.
I came up with something that is quite complex and not easy to optimize. Define u=p2-p1, v=p3-p1, w=p4-p1. Then the planarity measure is:
[(u x v) * w]² / (|u x v|² |w|²)
where x means cross product and '*' means dot product.
The numerator is simply (the square of) the volume of the tetrahedron defined by the four points, and the denominator is a normalizing factor that makes this measure become simply the cosine of an angle. Because angles do not changed under uniform scale, this function satisfies all my requirements.
Does anybody know of something simpler?
Alex.
Edit:
I eventually used an Augmented Lagrangian method to perform optimization, so I don't need it to be scale agnostic. Just using the constraint (u x v) * w = 0 is enough, as the optimization procedure finds the correct Lagrange multiplier to compensate for the scale.
Your methods seems ok, I'd do something like this for efficient implementation:
Take u, v, w as you did
Normalize them: various tricks exist to evaluate the inverse square root efficiently with whatever precision you want, like this jewel. Most modern processors have builtins for this operation.
Take f = |det(u, v, w)| ( = (u x v) . w ). There are fast direct implementations for 3x3 matrices; see #batty's answer to this question.
This amounts to what you do without the squares. It is still homogeneous and almost everywhere differentiable. Take the square of the determinant if you want something differentiable everywhere.
EDIT: #phkahler implicitly suggested using the ratio of the radius of the inscribed sphere to the radius of the circumscribed sphere as a measure of planarity. This is a bounded differentiable function of the points, invariant by scaling. However, this is at least as difficult to compute as what you (and I) suggest. Especially computing the radius of the circumscribed sphere is very sensitive to roundoff errors.
A measure that should be symmetric with respect to point reorderings is:
((u x v).w)^2/(|u||v||w||u-v||u-w||v-w|)
which is proportional to the volume of the tetrahedron squared divided by all 6 edge lengths. It is not simpler than your formula or Alexandre C.'s, but it is not much more complicated. However, it does become unnecessarily singular when any two points coincide.
A better-behaved, order-insensitive formula is:
let a = u x v
b = v x w
c = w x u
(a.w)^2/(|a| + |b| + |c| + |a+b+c|)^3
which is something like the volume of the tetrahedron divided by the surface area, but raised to appropriate powers to make the whole thing scale-insensitive. This is also a bit more complex than your formula, but it works unless all 4 points are collinear.
How about
|(u x v) * w| / |u|^3
(and you can change |x| to (x)^2 if you think it's simpler).

Vector math, finding coördinates on a planar between 2 vectors

I am trying to generate a 3d tube along a spline. I have the coördinates of the spline (x1,y1,z1 - x2,y2,z2 - etc) which you can see in the illustration in yellow. At those points I need to generate circles, whose vertices are to be connected at a later stadium. The circles need to be perpendicular to the 'corners' of two line segments of the spline to form a correct tube. Note that the segments are kept low for illustration purpose.
[apparently I'm not allowed to post images so please view the image at this link]
http://img191.imageshack.us/img191/6863/18720019.jpg
I am as far as being able to calculate the vertices of each ring at each point of the spline, but they are all on the same planar ie same angled. I need them to be rotated according to their 'legs' (which A & B are to C for instance).
I've been thinking this over and thought of the following:
two line segments can be seen as 2 vectors (in illustration A & B)
the corner (in illustraton C) is where a ring of vertices need to be calculated
I need to find the planar on which all of the vertices will reside
I then can use this planar (=vector?) to calculate new vectors from the center point, which is C
and find their x,y,z using radius * sin and cos
However, I'm really confused on the math part of this. I read about the dot product but that returns a scalar which I don't know how to apply in this case.
Can someone point me into the right direction?
[edit]
To give a bit more info on the situation:
I need to construct a buffer of floats, which -in groups of 3- describe vertex positions and will be connected by OpenGL ES, given another buffer with indices to form polygons.
To give shape to the tube, I first created an array of floats, which -in groups of 3- describe control points in 3d space.
Then along with a variable for segment density, I pass these control points to a function that uses these control points to create a CatmullRom spline and returns this in the form of another array of floats which -again in groups of 3- describe vertices of the catmull rom spline.
On each of these vertices, I want to create a ring of vertices which also can differ in density (amount of smoothness / vertices per ring).
All former vertices (control points and those that describe the catmull rom spline) are discarded.
Only the vertices that form the tube rings will be passed to OpenGL, which in turn will connect those to form the final tube.
I am as far as being able to create the catmullrom spline, and create rings at the position of its vertices, however, they are all on a planars that are in the same angle, instead of following the splines path.
[/edit]
Thanks!
Suppose you have a parametric curve such as:
xx[t_] := Sin[t];
yy[t_] := Cos[t];
zz[t_] := t;
Which gives:
The tangent vector to our curve is formed by the derivatives in each direction. In our case
Tg[t_]:= {Cos[t], -Sin[t], 1}
The orthogonal plane to that vector comes solving the implicit equation:
Tg[t].{x - xx[t], y - yy[t], z - zz[t]} == 0
In our case this is:
-t + z + Cos[t] (x - Sin[t]) - (y - Cos[t]) Sin[t] == 0
Now we find a circle in that plane, centered at the curve. i.e:
c[{x_, y_, z_, t_}] := (x - xx[t])^2 + (y - yy[t])^2 + (z - zz[t])^2 == r^2
Solving both equations, you get the equation for the circles:
HTH!
Edit
And by drawing a lot of circles, you may get a (not efficient) tube:
Or with a good Graphics 3D library:
Edit
Since you insist :) here is a program to calculate the circle at junctions.
a = {1, 2, 3}; b = {3, 2, 1}; c = {2, 3, 4};
l1 = Line[{a, b}];
l2 = Line[{b, c}];
k = Cross[(b - a), (c - b)] + b; (*Cross Product*)
angle = -ArcCos[(a - b).(c - b)/(Norm[(a - b)] Norm[(c - b)])]/2;
q = RotationMatrix[angle, k - b].(a - b);
circle[t_] := (k - b)/Norm[k - b] Sin#t + (q)/Norm[q] Cos#t + b;
Show[{Graphics3D[{
Red, l1,
Blue, l2,
Black, Line[{b, k}],
Green, Line[{b, q + b}]}, Axes -> True],
ParametricPlot3D[circle[t], {t, 0, 2 Pi}]}]
Edit
Here you have the mesh constructed by this method. It is not pretty, IMHO:
I don't know what your language of choice is, but if you speak MatLab there are already a few implementations available. Even if you are using another language, some of the code might be clear enough to inspire a reimplementation.
The key point is that if you don't want your tube to twist when you connect the vertices, you cannot determine the basis locally, but need to propagate it along the curve. The Frenet frame, as proposed by jalexiou, is one option but simpler stuff works fine as well.
I did a simple MatLab implementation called tubeplot.m in my formative years (based on a simple non-Frenet propagation), and googling it, I can see that Anders Sandberg from kth.se has done a (re?)implementation with the same name, available at http://www.nada.kth.se/~asa/Ray/Tubeplot/tubeplot.html.
Edit:
The following is pseudocode for the simple implementation in tubeplot.m. I have found it to be quite robust.
The plan is to propagate two normals a and b along the curve, so
that at each point on the curve a, b and the tangent to the curve
will form an orthogonal basis which is "as close as possible" to the
basis used in the previous point.
Using this basis we can find points on the circumference of the tube.
// *** Input/output ***
// v[0]..v[N-1]: Points on your curve as vectors
// No neighbours should overlap
// nvert: Number of vertices around tube, integer.
// rtube: Radius of tube, float.
// xyz: (N, nvert)-array with vertices of the tube as vectors
// *** Initialization ***
// 1: Tangent vectors
for i=1 to N-2:
dv[i]=v[i+1]-v[i-1]
dv[0]=v[1]-v[0], dv[N-1]=v[N-1]-v[N-2]
// 2: An initial value for a (must not be pararllel to dv[0]):
idx=<index of smallest component of abs(dv[0])>
a=[0,0,0], a[idx]=1.0
// *** Loop ***
for i = 0 to N-1:
b=normalize(cross(a,dv[i]));
a=normalize(cross(dv[i],b));
for j = 0 to nvert-1:
th=j*2*pi/nvert
xyz[i,j]=v[i] + cos(th)*rtube*a + sin(th)*rtube*b
Implementation details: You can probably speed up things by precalculating the cos and sin. Also, to get a robust performance, you should fuse input points closer than, say, 0.1*rtube, or a least test that all the dv vectors are non-zero.
HTH
You need to look at Fenet formulas in Differential Geometry. See figure 2.1 for an example with a helix.
Surfaces & Curves
Taking the cross product of the line segment and the up vector will give you a vector at right-angles to them both (unless the line segment points exactly up or down) which I'll call horizontal. Taking the cross product of horizontal and the line segment with give you another vector that's at right angles to the line segment and the other one (let's call it vertical). You can then get the circle coords by lineStart + cos theta * horizontal + sin theta * vertical for theta in 0 - 2Pi.
Edit: To get the points for the mid-point between two segments, use the sum of the two line segment vectors to find the average.

Resources