calculating an intercept point between a straight line and an ellipse - python - python

Iv'e been trying lately to calculate a point an ellipse
The desired point is the green point , knowing the red dots
and the ellipse equation.
I've used numpy linspace to create an array on points
and iterate them using zip(x axis , y axis)
between the red points , and using the ellipse
equation figure which of the points is the closest to 1.
(which is the outcome of the ellipse equation ).
this concept works most of the time , but in some location
of the red outer dot , this method doesn't seem to give good outcome
long story short, any idea how to calculate the green dot in python?
p.s - ellipse might have angle, both of hes axis are known.

I end up using the ellipse equation from this answer:
and created an in_ellipse function
then Iv'e used the Intermediate value theorem , to get a good estimation
of the point
def in_ellipse(point, ellipse):
return true if point in ellipse
return false
dot_a = ellipse_center
dot_b = dot
for i in range(20):
center_point = ((dot_b.y - dot_a.y)/2, (dot_b.x - dot_a.x)/2)
if in_ellipse(center_point):
dot_a = center_point
else:
dot_b = center_point
return center_point
this system gives the point in 7 (2^20) digits resolution after decimal point
you can increase the range for better resolution.

Let ellipse center is (0,0) (otherwise just subtract center coordinates), semi-axes are a, b and rotation angle is theta. We can build affine tranformation to transform ellipse into circle and apply the same transform to point P.
1) Rotate by -theta
px1 = px * Cos(theta) + py * Sin(theta)
py1 = -px * Sin(theta) + py * Cos(theta)
2) Extend (or shrink) along OY axis by a/b times
px2 = px1
py2 = py1 * a / b
3) Find intersection point
plen = hypot(px2, py2) (length of p2 vector)
if (a > plen), then segment doesn't intersect ellipse - it fully lies inside
ix = a * px2 / plen
iy = a * py2 / plen
4) Make backward shrinking
ix2 = ix
iy2 = iy * b / a
5) Make backward rotation
ixfinal = ix2 * Cos(theta) - iy2 * Sin(theta)
iyfinal = ix2 * Sin(theta) + iy2 * Cos(theta)

Related

Intersection coordinates (lat/lon) of two circles (given the coordinates of the center and the radius) on earth

I am not that experienced in python but improving it thanks to this community! I desperately need a function which takes the input and gives the ouput below:
Input:
1- Latitude/longitude coordinates of the center of circle 1 (e.g. (50.851295, 5.667969) )
2- The radius of circle 1 in meters (e.g. 200)
3- Latitude/longitude coordinates of the center of circle 2 (e.g. (50.844101, 5.725889) )
4- The radius of circle 2 in meters (e.g. 300)
Output: Possible output examples can be;
The intersection points are (50.848295, 5.707969) and (50.849295, 5.717969)
The circles are overlapping
The circles are tangential and the intersection point is (50.847295, 5.705969)
The circles do not intersect
I have examined the similar topics in this platform, other platforms, libraries, tried to combine different solutions but couldn't succeed. Any help is much appreciated!
EDIT:
The problem is solved many thanks to Ture Pålsson who commented below and directed me to whuber's brilliant work in this link https://gis.stackexchange.com/questions/48937/calculating-intersection-of-two-circles Based on that work, I wrote the code below and as far as I tested it works. I want to share it here in case someone might find it helpful. Any feedback is appreciated.
'''
FINDING THE INTERSECTION COORDINATES (LAT/LON) OF TWO CIRCLES (GIVEN THE COORDINATES OF THE CENTER AND THE RADII)
Many thanks to Ture Pålsson who directed me to the right source, the code below is based on whuber's brilliant logic and
explanation here https://gis.stackexchange.com/questions/48937/calculating-intersection-of-two-circles
The idea is that;
1. The points in question are the mutual intersections of three spheres: a sphere centered beneath location x1 (on the
earth's surface) of a given radius, a sphere centered beneath location x2 (on the earth's surface) of a given radius, and
the earth itself, which is a sphere centered at O = (0,0,0) of a given radius.
2. The intersection of each of the first two spheres with the earth's surface is a circle, which defines two planes.
The mutual intersections of all three spheres therefore lies on the intersection of those two planes: a line.
Consequently, the problem is reduced to intersecting a line with a sphere.
Note that "Decimal" is used to have higher precision which is important if the distance between two points are a few
meters.
'''
from decimal import Decimal
from math import cos, sin, sqrt
import math
import numpy as np
def intersection(p1, r1_meter, p2, r2_meter):
# p1 = Coordinates of Point 1: latitude, longitude. This serves as the center of circle 1. Ex: (36.110174, -90.953524)
# r1_meter = Radius of circle 1 in meters
# p2 = Coordinates of Point 2: latitude, longitude. This serves as the center of circle 1. Ex: (36.110174, -90.953524)
# r2_meter = Radius of circle 2 in meters
'''
1. Convert (lat, lon) to (x,y,z) geocentric coordinates.
As usual, because we may choose units of measurement in which the earth has a unit radius
'''
x_p1 = Decimal(cos(math.radians(p1[1]))*cos(math.radians(p1[0]))) # x = cos(lon)*cos(lat)
y_p1 = Decimal(sin(math.radians(p1[1]))*cos(math.radians(p1[0]))) # y = sin(lon)*cos(lat)
z_p1 = Decimal(sin(math.radians(p1[0]))) # z = sin(lat)
x1 = (x_p1, y_p1, z_p1)
x_p2 = Decimal(cos(math.radians(p2[1]))*cos(math.radians(p2[0]))) # x = cos(lon)*cos(lat)
y_p2 = Decimal(sin(math.radians(p2[1]))*cos(math.radians(p2[0]))) # y = sin(lon)*cos(lat)
z_p2 = Decimal(sin(math.radians(p2[0]))) # z = sin(lat)
x2 = (x_p2, y_p2, z_p2)
'''
2. Convert the radii r1 and r2 (which are measured along the sphere) to angles along the sphere.
By definition, one nautical mile (NM) is 1/60 degree of arc (which is pi/180 * 1/60 = 0.0002908888 radians).
'''
r1 = Decimal(math.radians((r1_meter/1852) / 60)) # r1_meter/1852 converts meter to Nautical mile.
r2 = Decimal(math.radians((r2_meter/1852) / 60))
'''
3. The geodesic circle of radius r1 around x1 is the intersection of the earth's surface with an Euclidean sphere
of radius sin(r1) centered at cos(r1)*x1.
4. The plane determined by the intersection of the sphere of radius sin(r1) around cos(r1)*x1 and the earth's surface
is perpendicular to x1 and passes through the point cos(r1)x1, whence its equation is x.x1 = cos(r1)
(the "." represents the usual dot product); likewise for the other plane. There will be a unique point x0 on the
intersection of those two planes that is a linear combination of x1 and x2. Writing x0 = ax1 + b*x2 the two planar
equations are;
cos(r1) = x.x1 = (a*x1 + b*x2).x1 = a + b*(x2.x1)
cos(r2) = x.x2 = (a*x1 + b*x2).x2 = a*(x1.x2) + b
Using the fact that x2.x1 = x1.x2, which I shall write as q, the solution (if it exists) is given by
a = (cos(r1) - cos(r2)*q) / (1 - q^2),
b = (cos(r2) - cos(r1)*q) / (1 - q^2).
'''
q = Decimal(np.dot(x1, x2))
if q**2 != 1 :
a = (Decimal(cos(r1)) - Decimal(cos(r2))*q) / (1 - q**2)
b = (Decimal(cos(r2)) - Decimal(cos(r1))*q) / (1 - q**2)
'''
5. Now all other points on the line of intersection of the two planes differ from x0 by some multiple of a vector
n which is mutually perpendicular to both planes. The cross product n = x1~Cross~x2 does the job provided n is
nonzero: once again, this means that x1 and x2 are neither coincident nor diametrically opposite. (We need to
take care to compute the cross product with high precision, because it involves subtractions with a lot of
cancellation when x1 and x2 are close to each other.)
'''
n = np.cross(x1, x2)
'''
6. Therefore, we seek up to two points of the form x0 + t*n which lie on the earth's surface: that is, their length
equals 1. Equivalently, their squared length is 1:
1 = squared length = (x0 + t*n).(x0 + t*n) = x0.x0 + 2t*x0.n + t^2*n.n = x0.x0 + t^2*n.n
'''
x0_1 = [a*f for f in x1]
x0_2 = [b*f for f in x2]
x0 = [sum(f) for f in zip(x0_1, x0_2)]
'''
The term with x0.n disappears because x0 (being a linear combination of x1 and x2) is perpendicular to n.
The two solutions easily are t = sqrt((1 - x0.x0)/n.n) and its negative. Once again high precision
is called for, because when x1 and x2 are close, x0.x0 is very close to 1, leading to some loss of
floating point precision.
'''
if (np.dot(x0, x0) <= 1) & (np.dot(n,n) != 0): # This is to secure that (1 - np.dot(x0, x0)) / np.dot(n,n) > 0
t = Decimal(sqrt((1 - np.dot(x0, x0)) / np.dot(n,n)))
t1 = t
t2 = -t
i1 = x0 + t1*n
i2 = x0 + t2*n
'''
7. Finally, we may convert these solutions back to (lat, lon) by converting geocentric (x,y,z) to geographic
coordinates. For the longitude, use the generalized arctangent returning values in the range -180 to 180
degrees (in computing applications, this function takes both x and y as arguments rather than just the
ratio y/x; it is sometimes called "ATan2").
'''
i1_lat = math.degrees( math.asin(i1[2]))
i1_lon = math.degrees( math.atan2(i1[1], i1[0] ) )
ip1 = (i1_lat, i1_lon)
i2_lat = math.degrees( math.asin(i2[2]))
i2_lon = math.degrees( math.atan2(i2[1], i2[0] ) )
ip2 = (i2_lat, i2_lon)
return [ip1, ip2]
elif (np.dot(n,n) == 0):
return("The centers of the circles can be neither the same point nor antipodal points.")
else:
return("The circles do not intersect")
else:
return("The centers of the circles can be neither the same point nor antipodal points.")
'''
Example: the output of below is [(36.989311051533505, -88.15142628069133), (38.2383796094578, -92.39048549120287)]
intersection_points = intersection((37.673442, -90.234036), 107.5*1852, (36.109997, -90.953669), 145*1852)
print(intersection_points)
'''
Depending on the precision you need, you may or may not consider the Earth as a sphere. In the second case, calculations become more complex.
The best option for precise measurements when the radius is small (as in your example) is to use a projection (UTM for example) and then apply the common flat euclidean calculations.
Let's first copy the flat circle intersection function from https://stackoverflow.com/a/55817881/2148416:
def circle_intersection(x0, y0, r0, x1, y1, r1):
d = math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
if d > r0 + r1: # non intersecting
return None
if d < abs(r0 - r1): # one circle within other
return None
if d == 0 and r0 == r1: # coincident circles
return None
a = (r0 ** 2 - r1 ** 2 + d ** 2) / (2 * d)
h = math.sqrt(r0 ** 2 - a ** 2)
x2 = x0 + a * (x1 - x0) / d
y2 = y0 + a * (y1 - y0) / d
x3 = x2 + h * (y1 - y0) / d
y3 = y2 - h * (x1 - x0) / d
x4 = x2 - h * (y1 - y0) / d
y4 = y2 + h * (x1 - x0) / d
return (x3, y3), (x4, y4)
The precise calculation for a small radius (up to a few kilometers) can be done in UTM coordinates with the help of the utm library. It handles all the complications regarding the fact the the Earth is more an ellipsoid than a sphere:
import utm
def geo_circle_intersection(latlon0, radius0, latlon1, radius1):
# Convert lat/lon to UTM
x0, y0, zone, letter = utm.from_latlon(latlon0[0], latlon0[1])
x1, y1, _, _ = utm.from_latlon(latlon1[0], latlon1 [1], force_zone_number=zone)
# Calculate intersections in UTM coordinates
a_utm, b_utm = circle_intersection(x0, y0, r0, x1, y1, r1)
# Convert intersections from UTM back to lat/lon
a = utm.to_latlon(a_utm[0], a_utm[1], zone, letter)
b = utm.to_latlon(b_utm[0], b_utm[1], zone, letter)
return a, b
Using your example (with slightly larger radii):
>>> p0 = 50.851295, 5.667969
>>> r0 = 2000
>>> p1 = 50.844101, 5.725889
>>> r1 = 3000
>>> a, b = geo_circle_intersection(p0, r0, p1, r1)
>>> print(a)
(50.836848562566004, 5.684869539768468)
>>> print(b)
(50.860635308778285, 5.692236858407678)

How to move mouse along arc given by 3 points and a length?

I'm currently working on a section of a program that moves the mouse in an arc.
I'm given three points that define the arc: a starting point p1, a intermediate point on the arc p2 , and the end point p3. I'm also given length of the arc. If length is greater than the actual length of the arc subtended by p1 and p3, then p3 will not be the end point of the arc, but the mouse will continue moving in a circle until it has traveled distance length.
I have worked out the center of the circle (x, y), its radius r, and angle sweeped.
To move the mouse, am hoping to divide angle into smaller intervals each with angle dAngle and moving the mouse between its current position and the new position after sweeping dAngle. What I have in mind is in the pseudocode below:
for i in range(intervals):
x = center.x + r * cos(i * dAngle)
y = center.y + r * sin(i * dAngle)
Move mouse to (x, y)
Sleep 1
However, I've encountered some problems while trying to parametrically find the new point on the arc.
My mouse does not start at p1, but at what I assume is at the point where the line from the mouse to the center and the horizontal line subtends 0 degees, as I haven't factored into the parameters the initial angle. How do I find the initial angle of the mouse?
How do I determine whether to rotate clockwise or anticlockwise, i.e. whether x = center.x + r * cos(i * dAngle) or x = center.x - r * cos(i * dAngle)
If there is a more efficient way of moving in an arc please suggest it.
You can calculate starting angle as
a1 = math.atan2(p1.y-center.y, p1.x-center.x)
then use it in
x = center.x + r * cos(a1 + i * dAngle)
y = center.y + r * sin(a1 + i * dAngle)
About direction - perhaps you can determine direction when arc center is calculated. If no, and arc sweep angle is less than Pi (180 degrees), just find sign of expression
sg = math.sign((p1.x-center.x) * (p3.y-center.y) - (p1.y-center.y) * (p3.x-center.x))
and use it with dAngle
x = center.x + r * cos(a1 + i * sg * dAngle)
similar for y
P.S. note that minus in x = center.x - r * cos(i * dAngle) is wrong way to change direction

Calculating distance between two points using latitude longitude and altitude (elevation)

I'm trying to calculate distance between two points, using latitude longitude and altitude (elevation).
I was using euklides formula in order to get my distance:
D=√((Long1-Long2)²+(Lat1-Lat2)²+(Alt1-Alt2)²)
My points are geographical coordinates and ofcourse altitude is my height above the sea.
I only have lat and lng, I'm using GOOGLE API Elevation to get my altitude.
I'm developing an application which calculates my traveled distance (on my skis). Every application which I have used, gets distance traveled with included altitude. Like #Endomondo or #Garmin I cannot get my distance in 2D space because true distances are going to vary from the ones I've returned.
Which formula would be the best to calculate my distance ? Ofcourse with included altitude.
I'm writing my app in Python, with PostGis.
You can calculate distance between flat coordinates in, say, meters by using geopy package or Vincenty's formula, pasting coordinates directly. Suppose the result is d meters. Then the total distance travelled is sqrt(d**2 + h**2) where h is the change in elevation in meters.
EDIT 2019: Since this answer, I composed a Q&A style example to answer similar questions (including this one as an example): How to calculate 3D distance (including altitude) between two points in GeoDjango.
In sort:
We need to calculate the 2D great-circle distance between 2 points using either the Haversine formula or the Vicenty formula and then we can combine it with the difference (delta) in altitude between the 2 points to calculate the Euclidean distance between them as follows:
dist = sqrt(great_circle((lat_1, lon_1), (lat_2, lon_2)).m**2, (alt_1 - alt_2)**2)
The solution assumes that the altitude is in meters and thus converts the great_circle's result into meters as well.
You can get the correct calculation by translating your coordinates from Polar (long, lat, alt) to Cartesian (x, y, z):
Let:
polar_point_1 = (long_1, lat_1, alt_1)
and polar_point_2 = (long_2, lat_2, alt_2)
Translate each point to it's Cartesian equivalent by utilizing this formula:
x = alt * cos(lat) * sin(long)
y = alt * sin(lat)
z = alt * cos(lat) * cos(long)
and you will have p_1 = (x_1, y_1, z_1) and p_2 = (x_2, y_2, z_2) points respectively.
Finally use the Euclidean formula:
dist = sqrt((x_2-x_1)**2 + (y_2-y_1)**2 + (z_2-z_1)**2)
I used the solution provided by John Moutafis but I didn't get a right answer.The formula needs some corrections. You will get the conversion of coordinates from Polar to Cartesian (x, y, z) at http://electron9.phys.utk.edu/vectors/3dcoordinates.htm.
Use the above formula to convert spherical coordinates(Polar) to Cartesian and calculate Euclidean distance.
I used the following c# in a console app.
Considering following dummy lat long
double lat_1 = 18.457793 * (Math.PI / 180);
double lon_1 = 73.3951930277778 *(Math.PI/180);
double alt_1 = 270.146;
double lat_2 = 18.4581253333333 * (Math.PI / 180);
double lon_2 = 73.3963755277778 * (Math.PI / 180);
double alt_2 = 317.473;
const Double r = 6376.5 *1000; // Radius of Earth in metres
double x_1 = r * Math.Sin(lon_1) * Math.Cos(lat_1);
double y_1 = r * Math.Sin(lon_1) * Math.Sin(lat_1);
double z_1 = r * Math.Cos(lon_1);
double x_2 = r * Math.Sin(lon_2) * Math.Cos(lat_2);
double y_2 = r * Math.Sin(lon_2) * Math.Sin(lat_2);
double z_2 = r * Math.Cos(lon_2);
double dist = Math.Sqrt((x_2 - x_1) * (x_2 - x_1) + (y_2 - y_1) *
(y_2 - y_1) + (z_2 - z_1) * (z_2 - z_1));

What is most efficient way to find the intersection of a line and a circle in python?

I have a polygon consists of lots of points. I want to find the intersection of the polygon and a circle. Providing the circle center of [x0,y0] and radius of r0, I have wrote a rough function to simply solve the quadratic equation of the circle and a line. But what about the efficiency of find the intersection of every line segment of the polygon one by one? Is there more efficient way?
I know sympy already provide the feature to get the intersections of different geometry. But also what about the efficiency of external library like sympy compared to calculate it by my own function, if I want to deal with lots of polygons?
def LineIntersectCircle(p,lsp,lep):
# p is the circle parameter, lsp and lep is the two end of the line
x0,y0,r0 = p
x1,y1 = lsp
x2,y2 = esp
if x1 == x2:
if abs(r0) >= abs(x1 - x0):
p1 = x1, y0 - sqrt(r0**2 - (x1-x0)**2)
p2 = x1, y0 + sqrt(r0**2 - (x1-x0)**2)
inp = [p1,p2]
# select the points lie on the line segment
inp = [p for p in inp if p[1]>=min(y1,y2) and p[1]<=max(y1,y2)]
else:
inp = []
else:
k = (y1 - y2)/(x1 - x2)
b0 = y1 - k*x1
a = k**2 + 1
b = 2*k*(b0 - y0) - 2*x0
c = (b0 - y0)**2 + x0**2 - r0**2
delta = b**2 - 4*a*c
if delta >= 0:
p1x = (-b - sqrt(delta))/(2*a)
p2x = (-b + sqrt(delta))/(2*a)
p1y = k*x1 + b0
p2y = k*x2 + b0
inp = [[p1x,p1y],[p2x,p2y]]
# select the points lie on the line segment
inp = [p for p in inp if p[0]>=min(x1,x2) and p[0]<=max(x1,x2)]
else:
inp = []
return inp
I guess maybe your question is about how to in theory do this in the fastest manner. But if you want to do this quickly, you should really use something which is written in C/C++.
I am quite used to Shapely, so I will provide an example of how to do this with this library. There are many geometry libraries for python. I will list them at the end of this answer.
from shapely.geometry import LineString
from shapely.geometry import Point
p = Point(5,5)
c = p.buffer(3).boundary
l = LineString([(0,0), (10, 10)])
i = c.intersection(l)
print i.geoms[0].coords[0]
(2.8786796564403576, 2.8786796564403576)
print i.geoms[1].coords[0]
(7.121320343559642, 7.121320343559642)
What is a little bit counter intuitive in Shapely is that circles are the boundries of points with buffer areas. This is why I do p.buffer(3).boundry
Also the intersection i is a list of geometric shapes, both of them points in this case, this is why I have to get both of them from i.geoms[]
There is another Stackoverflow question which goes into details about these libraries for those interested.
SymPy
CGAL Python bindings
PyEuclid
PythonOCC
Geometry-simple
EDIT because comments:
Shapely is based on GEOS (trac.osgeo.org/geos) which is built in C++ and considerably faster than anything you write natively in python. SymPy seems to be based on mpmath (mpmath.org) which also seems to be python, but seems to have lots of quite complex math integrated into it. Implementing that yourself may require a lot of work, and will probably not be as fast as GEOS C++ implementations.
Here's a solution that computes the intersection of a circle with either a line or a line segment defined by two (x, y) points:
def circle_line_segment_intersection(circle_center, circle_radius, pt1, pt2, full_line=True, tangent_tol=1e-9):
""" Find the points at which a circle intersects a line-segment. This can happen at 0, 1, or 2 points.
:param circle_center: The (x, y) location of the circle center
:param circle_radius: The radius of the circle
:param pt1: The (x, y) location of the first point of the segment
:param pt2: The (x, y) location of the second point of the segment
:param full_line: True to find intersections along full line - not just in the segment. False will just return intersections within the segment.
:param tangent_tol: Numerical tolerance at which we decide the intersections are close enough to consider it a tangent
:return Sequence[Tuple[float, float]]: A list of length 0, 1, or 2, where each element is a point at which the circle intercepts a line segment.
Note: We follow: http://mathworld.wolfram.com/Circle-LineIntersection.html
"""
(p1x, p1y), (p2x, p2y), (cx, cy) = pt1, pt2, circle_center
(x1, y1), (x2, y2) = (p1x - cx, p1y - cy), (p2x - cx, p2y - cy)
dx, dy = (x2 - x1), (y2 - y1)
dr = (dx ** 2 + dy ** 2)**.5
big_d = x1 * y2 - x2 * y1
discriminant = circle_radius ** 2 * dr ** 2 - big_d ** 2
if discriminant < 0: # No intersection between circle and line
return []
else: # There may be 0, 1, or 2 intersections with the segment
intersections = [
(cx + (big_d * dy + sign * (-1 if dy < 0 else 1) * dx * discriminant**.5) / dr ** 2,
cy + (-big_d * dx + sign * abs(dy) * discriminant**.5) / dr ** 2)
for sign in ((1, -1) if dy < 0 else (-1, 1))] # This makes sure the order along the segment is correct
if not full_line: # If only considering the segment, filter out intersections that do not fall within the segment
fraction_along_segment = [(xi - p1x) / dx if abs(dx) > abs(dy) else (yi - p1y) / dy for xi, yi in intersections]
intersections = [pt for pt, frac in zip(intersections, fraction_along_segment) if 0 <= frac <= 1]
if len(intersections) == 2 and abs(discriminant) <= tangent_tol: # If line is tangent to circle, return just one point (as both intersections have same location)
return [intersections[0]]
else:
return intersections
A low cost spacial partition might be to divide the plane into 9 pieces
Here is a crappy diagram. Imagine, if you will, that the lines are just touching the circle.
| |
__|_|__
__|O|__
| |
| |
8 of the areas we are interested in are surrounding the circle. The square in the centre isn't much use for a cheap test, but you can place a square of r/sqrt(2) inside the circle, so it's corners just touch the circle.
Lets label the areas
A |B| C
__|_|__
D_|O|_E
| |
F |G| H
And the square of r/sqrt(2) in the centre we'll call J
We will call the set of points in the centre square shown in the diagram that aren't in J, Z
Label each vertex of the polygon with it's letter code.
Now we can quickly see
AA => Outside
AB => Outside
AC => Outside
...
AJ => Intersects
BJ => Intersects
...
JJ => Inside
This can turned into a lookup table
So depending on your dataset, you may have saved yourself a load of work. Anything with an endpoint in Z will need to be tested however.
I think that the formula you use to find the coordinates of the two intersections cannot be optimized further. The only improvement (which is numerically important) is to distinguish the two cases: |x_2-x_1| >= |y_2-y_1| and |x_2-x1| < |y_2-y1| so that the quantity k is always between -1 and 1 (in your computation you can get very high numerical errors if |x_2-x_1| is very small). You can swap x-s and y-s to reduce one case to the other.
You could also implement a preliminary check: if both endpoints are internal to the circle there are no intersection. By computing the squared distance from the points to the center of the circle this becomes a simple formula which does not use the square root function. The other check: "whether the line is outside the circle" is already implemented in your code and corresponds to delta < 0. If you have a lot of small segments these two check should give a shortcut answer (no intersection) in most cases.

Give 3 points and a plot circle

I want to give the points [0,1],[1,0] and [0,-1] to python and plot the circle that passes over them. Does exists a python module that make this? I have tried using matplotlib:
import matplotlib.pyplot as plt
plt.plot([0,1,0],[1,0,-1])
plt.show()
But only gave me two lines.
There was a "code golf" question exactly matching this (except that the circle's equation was requested, rather than plotting it) -- see https://codegolf.stackexchange.com/questions/2289/circle-through-three-points . Unraveling the first and shortest (Python) solution into more readable, less-hacky form to match your exact specs - but keeping the core idea of using complex numbers for simpler calculations:
x, y, z = 0+1j, 1+0j, 0-1j
w = z-x
w /= y-x
c = (x-y)*(w-abs(w)**2)/2j/w.imag-x
print '(x%+.3f)^2+(y%+.3f)^2 = %.3f^2' % (c.real, c.imag, abs(c+x))
OK, this still "prints the equation" rather than "plotting the circle", but, we're getting close:-). To actually plot the circle in matplotlib, see e.g plot a circle with pyplot -- in the solution above, c is the (negated) center of the circle (as a complex number, so use .real and .imag for the x/y coordinates), and abs(c+x) the radius (a real number, abs makes it so).
This code also lets you easily check whether the 3 points form a line or not.
def define_circle(p1, p2, p3):
"""
Returns the center and radius of the circle passing the given 3 points.
In case the 3 points form a line, returns (None, infinity).
"""
temp = p2[0] * p2[0] + p2[1] * p2[1]
bc = (p1[0] * p1[0] + p1[1] * p1[1] - temp) / 2
cd = (temp - p3[0] * p3[0] - p3[1] * p3[1]) / 2
det = (p1[0] - p2[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p2[1])
if abs(det) < 1.0e-6:
return (None, np.inf)
# Center of circle
cx = (bc*(p2[1] - p3[1]) - cd*(p1[1] - p2[1])) / det
cy = ((p1[0] - p2[0]) * cd - (p2[0] - p3[0]) * bc) / det
radius = np.sqrt((cx - p1[0])**2 + (cy - p1[1])**2)
return ((cx, cy), radius)
And to solve the original question:
center, radius = define_circle((0,1), (1,0), (0,-1))
if center is not None:
plt.figure(figsize=(4, 4))
circle = plt.Circle(center, radius)
plt.gcf().gca().add_artist(circle)
(Adjusted from here)
I was very curious why the accepted answer by Alex Martelli works. And I had to create a report for my lecture anyway, so I'm pasting it here for posterity.
Given three points whose coordinates are:
(p,t) (q,u) (s,z)
...the equation of the circle defined by those three points is:
x^2 + y^2 + Ax + By + C = 0
where:
A=((u-t)*z^2+(-u^2+t^2-q^2+p^2)*z+t*u^2+(-t^2+s^2-p^2)*u+(q^2-s^2)*t)/((q-p)*z+(p-s)*u+(s-q)*t)
B=-((q-p)*z^2+(p-s)*u^2+(s-q)*t^2+(q-p)*s^2+(p^2-q^2)*s+p*q^2-p^2*q)/((q-p)*z+(p-s)*u+(s-q)*t)
C=-((p*u-q*t)*z^2+(-p*u^2+q*t^2-p*q^2+p^2*q)*z+s*t*u^2+(-s*t^2+p*s^2-p^2*s)*u+(q^2*s-q*s^2)*t)/((q-p)*z+(p-s)*u+(s-q)*t)
The above is the general solution. You can put the formulas for A, B, and C into your program
and find the equation for any circle, given 3 points.
For your particular problem with points (0,1) (1,0) (0,-1) you will get:
A=0
B=0
C=-1
... so the equation will be
x^2 + y^2 -1 = 0 (the unit circle)
To draw a circle in matplotlib, first you need to declare an artist
circle = plt.Circle((0,0), 2)
you then have to add that artist to an instance of axes:
fig, ax = plt.subplots()
ax.add_artist(circle)
then you can draw it safely.
plt.show()
Notice that artist Circle takes (x,y) coordinates of the circle's center, and radius r. That means you're going to have to calculate those values yourself.

Categories

Resources