Calculating lines of best fit for an ellipse - python

I'm trying to calculate the number of lines of best fit for an ellipse; given a desired error margin (minimum distance from the boundary).
My solution for a unit circle was thus.
def f(u, v, r):
mid_uv = (u + v) * 0.5
N = normalized(mid_uv)
return N * r
And repeat v = f(u, v, r) until radius - |v| < error.
Then simply take 2^i (i being the number of iterations) as the number of segments required.
This algorithm could probably be O(1), and does not work for ellipses (which is what I need it for).
How can I adapt it?
Or better yet, is there another solution?

I cannot formulate a nice neat answer - working with ellipses is quite a bit more challenging that circles - but here goes, in steps:
First - I would tighten up the algorithm for the circle by using a bit of trig. If you draw a chord (line segment) that spans angle angle through a unit circle, the maximum distance from the circle to the chord is calculated thus:
error = 1 - math.cos( angle / 2 )
(You can see this if you draw a diagram with the circle, chord, and chord's bisector.) Inverting this formula, you can calculate the angle given the tolerable error. The first line of code gives the precise angle; the second line shrinks the angle if needed so that it is an exact fraction of the whole circle.
angle = 2 * math.acos( 1 - error )
angle = (2*math.pi) / math.ceil( (2*math.pi) / angle )
Once you have the angle, it's simple to calculate the points around the unit circle for your chord end-points: [(1,0), (cos(angle),sin(angle)), cos(2*angle),sin(2*angle)), ... ]. You will end up with a regular polygon.
Second - For a circle with radius radius, run the above formulas adjusted as follows:
angle = 2 * math.acos( 1 - error/radius )
angle = (2*math.pi) / math.ceil( (2*math.pi) / angle )
And calculate the chord end-points by multiplying the sin and cos values by the radius.
Third - For an ellipse with maximal and minimal radii major and minor, I would use the circle formula to again calculate an angle:
radius = max( major, minor )
angle = 2 * math.acos( 1 - error/radius )
angle = (2*math.pi) / math.ceil( (2*math.pi) / angle )
If the major radius is in the x direction and the minor radius is in the y direction, then you can calculate the chord end-points like this:
[ (major, 0),
(major*cos(angle), minor*sin(angle)),
(major*cos(2*angle), minor*sin(2*angle)),
... ]
This does not always give you the minimal polygon for an ellipse (it will have more chords than necessary near the minor axis, especially for very squashed ellipses), but you only have to do the angle calculation once. If you really need to minimize the number of chords, then after drawing each chord, you will need to re-calculate the angle after each chord, and the formula is not straight-forward (where "not straight-forward" = "difficult for me to figure out").

There is O(1) solution for circle: you can calculate number of equal segments to obtain needed sagitta. Ellipse is more hard case. Maximum sagitta will for a chord that is perpendicular to larger semiaxis (near focuses), so it seems reasonable to choose the points of junction of segments at the ends of larger semiaxis (at least - as first approximation)

Related

Need to know if a sphere intersects with cube using python code...?

I am developing a code in python to check whether a sphere having a center at (x, y, z) coordinates and with radius R, intersects the cube of dimension one, i.e., l = 1, b = 1, h = 1. As mentioned above, I want to know if the sphere intersects the cube at any point or direction, or proportion.
I have a list of sphere coordinates (x,y,z) that must be checked for the intersection. I had done some research on it but couldn't clear my doubts regarding how to approach this example.
I would love to know both the math and coding part of it. Can someone please help me solve it..??
Edit: Cube is axis aligned and placed at the origin.
To reveal a fact of intersection, you can calculate distance from cube to sphere center and compare it with sphere radius. 2D case is described here, it could be easily extended to 3D case.
Get cube center as (rcx, rcy, rcz) and find coordinate differences from cube center to sphere center
dx, dy, dz = x - rcx, y - rcy, z - rcz
Let SquaredDist = 0, and for every coordinate make:
t = dx + 0.5 # 0.5 is half-size of your cube
if t < 0:
SquaredDist += t * t
else:
t = dx - 0.5
if t > 0:
SquaredDist += t * t
finally compare SquaredDist with R*R
Some explanation to comment:
Look at the picture in linked answer. For rectangle ABCD we have center G and coordinate differences GK and GJ, they include half of width and half of height. Squared distance (EC here) is sum of squared distances to proper side lines (planes in 3D case). When the closest (to sphere center) point is cube corner, we take into account three planes, when closest point lies at the edge - we take into account two planes, when closest point lies at facet - we take into account only one plane, and when sphere center is inside - SquaredDist remains zero.

Random point inside annulus with a shifted hole

First of all, will appreciate if someone will give me a proper term for "annulus with a shifted hole", see exactly what kind of shape I mean on a picture below.
Back to main question: I want to pick a random point in the orange area, uniform distribution is not required. For a case of a usual annulus I would've picked random point in (r:R) range and a random angle, then transform those to x,y and it's done. But for this unusual shape... is there even a "simple" formula for that, or should I approach it by doing some kind of polygonal approximation of a shape?
I'm interested in a general approach but will appreciate an example in python, javascript or any coding language of your choice.
Here's a simple method that gives a uniform distribution with no resampling.
For simplicity assume that the center of the outer boundary circle (radius r_outer) is at (0, 0) and that the center of the inner circular boundary (radius r_inner) lies at (x_inner, y_inner).
Write D for the outer disk, H1 for the subset of the plane given by the off-center inner hole, and H2 for the central disk of radius r_inner, centered at (0, 0).
Now suppose that we ignore the fact that the inner circle is not central, and instead of sampling from D-H1 we sample from D-H2 (which is easy to do uniformly). Then we've made two mistakes:
there's a region A = H1 - H2 that we might sample from, even though those samples shouldn't be in the result.
there's a region B = H2 - H1 that we never sample from, even though we should
But here's the thing: the regions A and B are congruent: given any point (x, y) in the plane, (x, y) is in H2 if and only if (x_inner - x, y_inner - y) is in H1, and it follows that (x, y) is in A if and only if (x_inner - x, y_inner - y) is in B! The map (x, y) -> (x_inner - x, y_inner - y) represents a rotation by 180 degress around the point (0.5*x_inner, 0.5*y_inner). So there's a simple trick: generate from D - H2, and if we end up with something in H1 - H2, rotate to get the corresponding point of H2 - H1 instead.
Here's the code. Note the use of the square root of a uniform distribution to choose the radius: this is a standard trick. See this article, for example.
import math
import random
def sample(r_outer, r_inner, x_inner, y_inner):
"""
Sample uniformly from (x, y) satisfiying:
x**2 + y**2 <= r_outer**2
(x-x_inner)**2 + (y-y_inner)**2 > r_inner**2
Assumes that the inner circle lies inside the outer circle;
i.e., that hypot(x_inner, y_inner) <= r_outer - r_inner.
"""
# Sample from a normal annulus with radii r_inner and r_outer.
rad = math.sqrt(random.uniform(r_inner**2, r_outer**2))
angle = random.uniform(-math.pi, math.pi)
x, y = rad*math.cos(angle),rad*math.sin(angle)
# If we're inside the forbidden hole, reflect.
if math.hypot(x - x_inner, y - y_inner) < r_inner:
x, y = x_inner - x, y_inner - y
return x, y
And an example plot, generated by the following:
import matplotlib.pyplot as plt
samples = [sample(5, 2, 1.0, 2.0) for _ in range(10000)]
xs, ys = zip(*samples)
plt.scatter(xs, ys, s=0.1)
plt.axis("equal")
plt.show()
Do you really need exact sampling? Because with acceptance/rejection it should work just fine. I assume big orange circle is located at (0,0)
import math
import random
def sample_2_circles(xr, yr, r, R):
"""
R - big radius
r, xr, yr - small radius and its position
"""
x = xr
y = yr
cnd = True
while cnd:
# sample uniformly in whole orange circle
phi = 2.0 * math.pi * random.random()
rad = R * math.sqrt(random.random())
x = rad * math.cos(phi)
y = rad * math.sin(phi)
# check condition - if True we continue in the loop with sampling
cnd = ( (x-xr)**2 + (y-yr)**2 < r*r )
return (x,y)
Since you have shown no equation, algorithm, or code of your own, but just an outline of an algorithm for center-aligned circles, I'll also just give the outline of an algorithm here for the more general case.
The smaller circle is the image of the larger circle under a similarity transformation. I.e. there is a fixed point in the larger circle and a ratio (which is R/r, greater than one) such that you can take any point on the smaller circle, examine the vector from the fixed point to that point, and multiply that vector by the ratio, then the end of that vector when it starts from the fixed point is a point on the larger circle. This transformation is one-to-one.
So you can choose a random point on the smaller circle (choose the angle at random between 0 and two-pi) and choose a ratio at random between 1 and the proportionality ratio R/r between the circles. Then use that the similarity transformation with the same fixed point but using the random ratio to get the image point of the just-chosen point on the smaller circle. This is a random point in your desired region.
This method is fairly simple. In fact, the hardest mathematical part is finding the fixed point of the similarity transformation. But this is pretty easy, given the centers and radii of the two circles. Hint: the transformation takes the center of the smaller circle to the center of the larger circle.
Ask if you need more detail. My algorithm does not yield a uniform distribution: the points will be more tightly packed where the circles are closest together and less tightly packed where the circles are farthest apart.
Here is some untested Python 3.6.2 code that does the above. I'll test it and show a graphic for it when I can.
import math
import random
def rand_pt_between_circles(x_inner,
y_inner,
r_inner,
x_outer,
y_outer,
r_outer):
"""Return a random floating-point 2D point located between the
inner and the outer circles given by their center coordinates and
radii. No error checking is done on the parameters."""
# Find the fixed point of the similarity transformation from the
# inner circle to the outer circle.
x_fixed = x_inner - (x_outer - x_inner) / (r_outer - r_inner) * r_inner
y_fixed = y_inner - (y_outer - y_inner) / (r_outer - r_inner) * r_inner
# Find a a random transformation ratio between 1 and r_outer / r_inner
# and a random point on the inner circle
ratio = 1 + (r_outer - r_inner) * random.random()
theta = 2 * math.pi * random.random()
x_start = x_inner + r_inner * math.cos(theta)
y_start = y_inner + r_inner * math.sin(theta)
# Apply the similarity transformation to the random point.
x_result = x_fixed + (x_start - x_fixed) * ratio
y_result = y_fixed + (y_start - y_fixed) * ratio
return x_result, y_result
The acceptance/rejection method as described by Severin Pappadeux is probably the simplest.
For a direct approach, you can also work in polar coordinates, with the center of the hole as the pole.
The polar equation (Θ, σ) (sorry, no rho) of the external circle will be
(σ cosΘ - xc)² + (σ sinΘ - yc)² = σ² - 2(cosΘ xc + sinΘ yc)σ + xc² + yc² = R²
This is a quadratic equation in σ, that you can easily solve in terms of Θ. Then you can draw an angle in 0, 2π an draw a radius between r and σ.
This won't give you a uniform distribution, because the range of σ is a function of Θ and because of the polar bias. This might be fixed by computing a suitable transfer function, but this is a little technical and probably not tractable analytically.

Generate random xy values within two-dimensional circular radius?

I have some points that are located in the same place, with WGS84 latlngs, and I want to 'jitter' them randomly so that they don't overlap.
Right now I'm using this crude method, which jitters them within a square:
r['latitude'] = float(r['latitude']) + random.uniform(-0.0005, 0.0005)
r['longitude'] = float(r['longitude']) + random.uniform(-0.0005, 0.0005)
How could I adapt this to jitter them randomly within a circle?
I guess I want a product x*y = 0.001 where x and y are random values. But I have absolutely no idea how to generate this!
(I realise that really I should use something like this to account for the curvature of the earth's surface, but in practice a simple circle is probably fine :) )
One simple way to generate random samples within a circle is to just generate square samples as you are, and then reject the ones that fall outside the circle.
The basic idea is, you generate a vector with x = radius of circle y = 0.
You then rotate the vector by a random angle between 0 and 360, or 0 to 2 pi radians.
You then apply this displacement vector and you have your random jitter in a circle.
An example from one of my scripts:
def get_randrad(pos, radius):
radius = random() * radius
angle = random() * 2 * pi
return (int(pos[0] + radius * cos(angle)),
int(pos[1] + radius * sin(angle)))
pos beeing the target location and radius beeing the "jitter" range.
As pjs pointed out, add
radius *= math.sqrt(random())
for uniform distribution
Merely culling results that fall outside your circle will be sufficient.
If you don't want to throw out some percentage of random results, you could choose a random angle and distance, to ensure all your values fall within the radius of your circle. It's important to note, with this solution, that the precision of the methods you use to extrapolate an angle into a vector will skew your distribution to be more concentrated in the center.
If you make a vector out of your x,y values, and then do something like randomize the length of said vector to fall within your circle, your distribution will no longer be uniform, so I would steer clear of that approach, if uniformity is your biggest concern.
The culling approach is the most evenly distributed, of the three I mentioned, although the random angle/length approach is usually fine, except in cases involving very fine precision and granularity.

python: elegant way of finding the GPS coordinates of a circle around a certain GPS location

I have a set of GPS coordinates in decimal notation, and I'm looking for a way to find the coordinates in a circle with variable radius around each location.
Here is an example of what I need. It is a circle with 1km radius around the coordinate 47,11.
What I need is the algorithm for finding the coordinates of the circle, so I can use it in my kml file using a polygon. Ideally for python.
see also Adding distance to a GPS coordinate for simple relations between lat/lon and short-range distances.
this works:
import math
# inputs
radius = 1000.0 # m - the following code is an approximation that stays reasonably accurate for distances < 100km
centerLat = 30.0 # latitude of circle center, decimal degrees
centerLon = -100.0 # Longitude of circle center, decimal degrees
# parameters
N = 10 # number of discrete sample points to be generated along the circle
# generate points
circlePoints = []
for k in xrange(N):
# compute
angle = math.pi*2*k/N
dx = radius*math.cos(angle)
dy = radius*math.sin(angle)
point = {}
point['lat']=centerLat + (180/math.pi)*(dy/6378137)
point['lon']=centerLon + (180/math.pi)*(dx/6378137)/math.cos(centerLat*math.pi/180)
# add to list
circlePoints.append(point)
print circlePoints
Use the formula for "Destination point given distance and bearing from start point" here:
http://www.movable-type.co.uk/scripts/latlong.html
with your centre point as start point, your radius as distance, and loop over a number of bearings from 0 degrees to 360 degrees. That will give you the points on a circle, and will work at the poles because it uses great circles everywhere.
It is a simple trigonometry problem.
Set your coordinate system XOY at your circle centre. Start from y = 0 and find your x value with x = r. Then just rotate your radius around origin by angle a (in radians). You can find the coordinates of your next point on the circle with Xi = r * cos(a), Yi = r * sin(a). Repeat the last 2 * Pi / a times.
That's all.
UPDATE
Taking the comment of #poolie into account, the problem can be solved in the following way (assuming the Earth being the right sphere). Consider a cross section of the Earth with its largest diameter D through our point (call it L). The diameter of 1 km length of our circle then becomes a chord (call it AB) of the Earth cross section circle. So, the length of the arc AB becomes (AB) = D * Theta, where Theta = 2 * sin(|AB| / 2). Further, it is easy to find all other dimensions.

How to draw a spherical triangle on a sphere in 3D?

Suppose you know the three vertices for a spherical triangle.
Then how do you draw the sides on a sphere in 3D?
I need some python code to use in Blender 3d modelisation software.
I already have the sphere done in 3D in Blender.
Thanks & happy blendering.
note 1:
i have the 3 points / vertices (p1,p2,p3 ) on the sphere for a spherical triangle
but i need to trace the edges on the sphere in 3D
so what would be the equations needed to determine all vertices between each points pair of the triangle on the sphere
3 edges from p1 to p2 - p2 to p3 and o3 to p1
i know it has something to do with the Great circle for Geodesic on a sphere
but cannot find the proper equations to do the calculations in spherical coordinates!
Thanks
Great circles
it would have been interesting to see a solution with great circle and see tehsolution in spherical coordinates directly !
but still interesting to do it in the euclidiens space
Thanks
ok i used this idea of line segment between 2 points
but did not do it as indicated before
i used an alternative method - Bezier line interpolation**
i parametrize the line with a bezier line
then subdivided and calculated as shonw ealier the ratio and angle for each of the subdivided bezier point on the chord
and it works very well and very precise
but it would be interesting to see how it is done whit the earlier method
but not certain how to do the iteration loop?
how do you load up the python code here
just past it with Ctrl-V?
Thanks and happy 2.5
i do use the blenders' forum
but no guaranti to get a clear answer all the time!
that's why i tried here - took a chance
i did the first edge seems to work
now got to make a loop to get multi segment for first edge and then do the other edges also
2- other subject
i open here a post on bezier triangle patch
i know it's not a usfull tool
but just to show how it is done
have youeseen a python sript to do theses triangel patch
and i did ask this questin on blender's foum and no answer
also on IRC python and sems to be dead right now
probably guys are too busy finishing the 2.5 Beta vesion which should come out in a week or 2
Hey Thanks a lot for this math discussion
if i have problem be back tomorrow
happy math and 2.5
Create Sine Mesh
Python code to create a sine wave mesh in Blender:
import math
import Blender
from Blender import NMesh
x = -1 * math.pi
mesh = NMesh.GetRaw()
vNew = NMesh.Vert( x, math.sin( x ), 0 )
mesh.verts.append( vNew )
while x < math.pi:
x += 0.1
vOld = vNew
vNew = NMesh.Vert( x, math.sin( x ), 0 )
mesh.verts.append( vNew )
mesh.addEdge( vOld, vNew )
NMesh.PutRaw( mesh, "SineWave", 1 )
Blender.Redraw()
The code's explanation is at: http://davidjarvis.ca/blender/tutorial-04.shtml
Algorithm to Plot Edges
Drawing one line segment is the same as drawing three, so the problem can be restated as:
How do you draw an arc on a sphere,
given two end points?
In other words, draw an arc between the following two points on a sphere:
P1 = (x1, y1, z1)
P2 = (x2, y2, z2)
Solve this by plotting many mid-points along the arc P1P2 as follows:
Calculate the radius of the sphere:
R = sqrt( x12 + y12 + z12 )
Calculate the mid-point (m) of the line between P1 and P2:
Pm = (xm, ym, zm)
xm = (x1 + x2) / 2
ym = (y1 + y2) / 2
zm = (z1 + z2) / 2
Calculate the length to the mid-point of the line between P1 and P2:
Lm = sqrt( xm2, ym2, zm2 )
Calculate the ratio of the sphere's radius to the length of the mid-point:
k = R / Lm
Calculate the mid-point along the arc:
Am = k * Pm = (k * xm, k * ym, k * zm)
For P1 to P2, create two edges:
P1 to Am
Am to P2
The two edges will cut through the sphere. To solve this, calculate the mid-points between P1Am and AmP2. The more mid-points, the more closely the line segments will follow the sphere's surface.
As Blender is rather precise with its calculations, the resulting arc will likely be (asymptotically) hidden by the sphere. Once you have created the triangular mesh, move it away from the sphere by a few units (like 0.01 or so).
Use a Spline
Another solution is to create a spline from the following:
sphere's radius (calculated as above)
P1
Am
P2
The resulting splines must be moved in front of the sphere.
Blender Artists Forums
The Blender experts will also have great ideas on how to solve this; try asking them.
See Also
http://www.mathnews.uwaterloo.ca/Issues/mn11106/DotProduct.php
http://cr4.globalspec.com/thread/27311/Urgent-Midpoint-of-Arc-formula
One cheap and easy method for doing this would be to create the triangle and subdivide the faces down to the level of detail you want, then normalize all the vertices to the radius you want.

Categories

Resources