Determine angle of triangle - geometry

Given following problem:
I have 2 solutions:
First is to calculate difference in absolute angles, then renormalize angle. bad idea, 2 x atan2() is slow, renormalisation is inefficient.
angle = clamp_to_range( atan2(P1.y, P1.x) - atan2(P0.y, P0.x));
Second is to calculate dot product, normalize, calculate arccos(). Also bad idea, because angle sign will be incorrect.
angle = acos( dot(P0, P1) / sqrt( dot(P0,P0) * dot(P1, P1) ) );
I feel, that there should be some formula. How to solve given problem efficiently?

It is possible to use only one atan2 but both cross product and scalar product of vectors:
angle = atan2(Cross(P0, P1), Dot(P0, P1);

Do you really need the angle in radians / degrees, instead of as a unit vector or rotation matrix?
An xy unit vector can represent angle instead of absolute direction; the angle is the angle between the vertical (or horizontal) axis and the unit vector. Trig functions are very slow compared to simple multiply / add / subtract, and still slow compared to div / sqrt, so representing angles as vectors is usually a good thing.
You can calculate its components using the Cross(P0, P1) and Dot(P0, P1), but then normalize them into an xy unit vector instead of using atan2 on them.
See also Rotate Object Towards Direction in 2D on gamedev.SE, and Is it better to track rotation with a vector or a float?
This is easy to vectorize with SIMD, much moreso than a SIMD atan2. rsqrtps exists mostly to speed up x *= 1.0 / sqrt(foo) (and reusing the same multiplier for a SIMD vector of y values) for normalization. But rsqrtps is very low accuracy so you often need a Newton Raphson iteration to refine. The most recent CPUs (Skylake) have good FP sqrt / div throughput, so you could just normalize the naive way with _mm_sqrt_ps and leave optimization for later. See Fast vectorized rsqrt and reciprocal with SSE/AVX depending on precision.

Related

how to calculate anti/clockwise angle in direction of lines?

I need to offset a curve, which by the simplest way is just shifting the points perpendicularly. I can access each point to calculate angle of each line along given path, for now I use atan2. Then I take those two angle and make average of it. It returns the shortest angle, not what I need in this case.
How can I calculate angle of each connection? Concerning that I am not interested in the shortest angle but the one that would create parallel offset curve.
Assuming 2D case...
So do a cross product of direction vectors of 2 neighboring lines the sign of z coordinate of the result will tell you if the lines are CW/CCW
So if you got 3 consequent control points on the polyline: p0,p1,p2 then:
d1 = p1-p0
d2 = p2-p1
if you use some 3D vector math then convert them to 3D by setting:
d1.z=0;
d2.z=0;
now compute 3D cross:
n = cross(d1,d2)
which returns vector perpendicular to both vectors of size equals to the area of quad (parallelogram) constructed with d1,d2 as base vectors. The direction (from the 2 possible) is determined by the winding rule of the p0,p1,p2 so inspecting z of the result is enough.
The n.x,n.y are not needed so you can compute directly without doing full cross product:
n.z=(d1.x*d2.y)-(d1.y*d2.x)
if (n.z>0) case1
if (n.z<0) case2
if the case1 is CW or CCW depends on your coordinate system properties (left/right handness). This approach is very commonly used in CG fur back face culling of polygons ...
if n.z is zero it means that your vectors/lines are either parallel or at lest one of them is zero.
I think these might interest you:
draw outline for some connected lines
How can I create an internal spiral for a polygon?
Also in 2D you do not need atan2 to get perpendicular vector... You can do instead this:
u = (x,y)
v = (-y,x)
w = (x,-y)
so u is any 2D vector and v,w are the 2 possible perpendicular vectors to u in 2D. they are the result of:
cross((x,y,0),(0,0,1))
cross((0,0,1),(x,y,0))

What is the fastest way to find the center of an irregular convex polygon?

I'm interested in a fast way to calculate the rotation-independent center of a simple, convex, (non-intersecting) 2D polygon.
The example below (on the left) shows the mean center (sum of all points divided by the total), and the desired result on the right.
Some options I've already considered.
bound-box center (depends on rotation, and ignores points based on their relation to the axis).
Straight skeleton - too slow to calculate.
I've found a way which works reasonably well, (weight the points by the edge-lengths) - but this means a square-root call for every edge - which I'd like to avoid.(Will post as an answer, even though I'm not entirely satisfied with it).
Note, I'm aware of this questions similarity with:What is the fastest way to find the "visual" center of an irregularly shaped polygon?
However having to handle convex polygons increases the complexity of the problem significantly.
The points of the polygon can be weighted by their edge length which compensates for un-even point distribution.
This works for convex polygons too but in that case the center point isn't guaranteed to be inside the polygon.
Psudo-code:
def poly_center(poly):
sum_center = (0, 0)
sum_weight = 0.0
for point in poly:
weight = ((point - point.next).length +
(point - point.prev).length)
sum_center += point * weight
sum_weight += weight
return sum_center / sum_weight
Note, we can pre-calculate all edge lengths to halve the number of length calculations, or reuse the previous edge-length for half+1 length calculations. This is just written as an example to show the logic.
Including this answer for completeness since its the best method I've found so far.
There is no much better way than the accumulation of coordinates weighted by the edge length, which indeed takes N square roots.
If you accept an approximation, it is possible to skip some of the vertices by curve simplification, as follows:
decide of a deviation tolerance;
start from vertex 0 and jump to vertex M (say M=N/2);
check if the deviation along the polyline from 0 to M exceeds the tolerance (for this, compute the height of the triangle formed by the vertices 0, M/2, M);
if the deviation is exceeded, repeat recursively with 0, M/4, M/2 and M/2, 3M/4, M;
if the deviation is not exceeded, assume that the shape is straight between 0 and M.
continue until the end of the polygon.
Where the points are dense (like the left edge on your example), you should get some speedup.
I think its easiest to do something with the center of masses of the delaunay triangulation of the polygon points. i.e.
def _centroid_poly(poly):
T = spatial.Delaunay(poly).simplices
n = T.shape[0]
W = np.zeros(n)
C = 0
for m in range(n):
sp = poly[T[m,:],:]
W[m] = spatial.ConvexHull(sp).volume
C += W[m] +np.mean(sp, axis = 0)
return C / np.sum(W)
This works well for me!

finding value of a point between measured points on a 2D plane

I'm trying to find the best way to calculate this. On a 2D plane I have fixed points all with an instantaneous measurement value. The coordinates of these points is known. I want to predict the value of a movable point between these fixed points. The movable point coodinates will be known. So the distance betwwen the points is known as well.
This could be comparable to temperature readings or elevation on topography. I this case I'm wanting to predict ionospheric TEC of the mobile point from the fixed point measurements. The fixed point measurements are smoothed over time however I do not want to have to store previous values of the mobile point estimate in RAM.
Would some sort of gradient function be the way to go here?
This is the same algorithm for interpolating the height of a point from a triangle.
In your case you don't have z values for heights, but some other float value for each triangle vertex, but it's the same concept, still 3D points.
Where you have 3D triangle points p, q, r and test point pt, then pseudo code from the above mathgem is something like this:
Vector3 v1 = q - p;
Vector3 v2 = r - p;
Vector3 n = v1.CrossProduct(v2);
if n.z is not zero
return ((n.x * (pt.x - p.x) + n.y * (pt.y - p.y)) / -n.z) + p.z
As you indicate in your comment to #Phpdevpad, you do have 3 fixed points so this will work.
You can try contour plots especially contour lines. Simply use a delaunay triangulation of the points and a linear transformation along the edges. You can try my PHP implementations https://contourplot.codeplex.com for geographic maps. Another algorithm is conrec algorithm from Paul Bourke.

How to calculate the mass of a inhomogenous sphere?

I want to calculated the mass of a sphere based on a threedimensional discret inhomogenous density distribution. Lets say a set of 3x3x3 cubes of different densities is inscribed by a sphere. What is the fastest way to sum up the partitioned masses using Python?
I tried to calculate the volume under the mathematical equation for a sphere: x^2 +y^2 +z^2 = R^2 for the range of one of the cubes using scipy.integrate.dblquad.
However, the result is only valid if the boundaries are smaller than the radius of the sphere and repetitive calculation for lets say 50'000 spheres with 27 cubes each would be quite slow.
On the other hand, the usual equation for CoM caluations could't be used in my opinion, due to the rather coarse and discrete mass distribution.
Timing Experiment
You didn't specify your timing constraints, so I've done a little experiment with a nice integration package.
Without optimization, each integral in spherical coordinates can be evaluated in 0.005 secs in a standard laptop if the cubes densities are straightforward functions.
Just as a reference, this is the program in Mathematica:
Clear#f;
(* Define a cuboid as density function *)
iP = IntegerPart;
f[{x_, y_, z_}, {lx_, ly_, lz_}] := iP[x - lx] + iP[y - ly] + iP[z - lz] /;
lx <= x <= lx + 3 && ly <= y <= ly + 3 && lz <= z <= lz + 3;
f[{x_, y_, z_}, {lx_, ly_, lz_}] := Break[] /; True;
Timing[Table[s = RandomReal[{0, 3}, 3]; (*sphere center random*)
sphereRadius = Min[Union[s, 3 - s]]; (*max radius inside cuboid *)
NIntegrate[(f[{x, y, z} - s, -s] /. (*integrate in spherical coords *)
{x -> r Cos#th Sin#phi,
y -> r Sin#th Sin#phi,
z -> r Cos#phi}) r^2 Sin#phi,
{r, 0, sphereRadius}, {th, 0, 2 Pi}, {phi, 0, Pi}],
{10000}]][[1]]
The result is 52 secs for 10^4 iterations.
So perhaps you don't need to optimize a lot ...
I cannot get your exact meaning of inscribed by a sphere. Also I havent tried the scipy.integrate. However, here are some though:
Set a 3x3x3 cube to unit density. Then take the integration for each cube respectively, so you should have the volume cube V_ijk here. Now for each of sphere, you can get the mass of each sphere by summing V_ijk*D_ijk, where the D_ijk is the density of the sphere.
It should be much faster because you do not need to do integration now.
You can obtain an analytic formula for the intersecting volume between a cube (or rectangular prism) and a sphere. It won't be easy, but it should be possible. I have done it for an arbitrary triangle and circle in 2D. The basic idea is to decompose the intersection into simpler pieces, like tetrahedra and volumetric spherical triangle sectors, for which relatively simple volume formulas are known. The main difficult is in considering all the possible cases of intersections. Luckily both objects are convex, so you are guaranteed a single convex intersection volume.
An approximate method might be to simply subdivide the cubes until your approximate numerical integration algorithm does work; this should still be relatively fast. Do you know about Pick's Theorem? That only works in 2D, but there are, I believe, 3D generalizations.

Given a set of points, how do I approximate the major axis of its shape?

Given a "shape" drawn by the user, I would like to "normalize" it so they all have similar size and orientation. What we have is a set of points. I can approximate the size using bounding box or circle, but the orientation is a bit more tricky.
The right way to do it, I think, is to calculate the majoraxis of its bounding ellipse. To do that you need to calculate the eigenvector of the covariance matrix. Doing so likely will be way too complicated for my need, since I am looking for some good-enough estimate. Picking min, max, and 20 random points could be some starter. Is there an easy way to approximate this?
Edit:
I found Power method to iteratively approximate eigenvector. Wikipedia article.
So far I am liking David's answer.
You'd be calculating the eigenvectors of a 2x2 matrix, which can be done with a few simple formulas, so it's not that complicated. In pseudocode:
// sums are over all points
b = -(sum(x * x) - sum(y * y)) / (2 * sum(x * y))
evec1_x = b + sqrt(b ** 2 + 1)
evec1_y = 1
evec2_x = b - sqrt(b ** 2 + 1)
evec2_y = 1
You could even do this by summing over only some of the points to get an estimate, if you expect that your chosen subset of points would be representative of the full set.
Edit: I think x and y must be translated to zero-mean, i.e. subtract mean from all x, y first (eed3si9n).
Here's a thought... What if you performed a linear regression on the points and used the slope of the resulting line? If not all of the points, at least a sample of them.
The r^2 value would also give you information about the general shape. The closer to 0, the more circular/uniform the shape is (circle/square). The closer to 1, the more stretched out the shape is (oval/rectangle).
The ultimate solution to this problem is running PCA
I wish I could find a nice little implementation for you to refer to...
Here you go! (assuming x is a nx2 vector)
def majAxis(x):
e,v = np.linalg.eig(np.cov(x.T)); return v[:,np.argmax(e)]

Resources