I have created the following function which gets two line points and outputs three points of an arrowhead triangle. What I need to add is a length according to which the arrowhead will be poisitioned away from the point B across the line AB. To explain it better, I would like it like this: A----->--B where the two dashes are equal to the length
def create_arrowhead(A, B):
"""
Calculate the arrowheads vertex positions according to the edge direction.
Parameters
----------
A : array
x,y Starting point of edge
B : array
x,y Ending point of edge
Returns
-------
B, v1, v2 : tuple
The point of head, the v1 xy and v2 xy points of the two base vertices of the arrowhead.
"""
w = 0.005 # half width of the triangle base
h = w * 0.8660254037844386467637 # sqrt(3)/2
mag = math.sqrt((B[0] - A[0])**2.0 + (B[1] - A[1])**2.0)
u0 = (B[0] - A[0]) / (mag)
u1 = (B[1] - A[1]) / (mag)
U = [u0, u1]
V = [-U[1], U[0]]
v1 = [B[0] - h * U[0] + w * V[0], B[1] - h * U[1] + w * V[1]]
v2 = [B[0] - h * U[0] - w * V[0], B[1] - h * U[1] - w * V[1]]
return (B, v1, v2)
I finally found the solution. By multiplying a variable of distance with the unit vector's components and subtracting this result from B's coordinates. This will create a point on the line AB with distance d from B.
def create_arrowhead(A, B, d):
"""
Use trigonometry to calculate the arrowheads vertex positions according to the line direction.
Parameters
----------
A : array
x,y Starting point of line segment
B : array
x,y Ending point of line segment
Returns
-------
C, v1, v2 : tuple
The point of head with distance d from point B, the v1 xy and v2 xy points of the two base vertices of the arrowhead.
"""
w = 0.003 # Half of the triangle base width
h = w / 0.26794919243 # tan(15)
AB = [B[0] - A[0], B[1] - A[1]]
mag = math.sqrt(AB[0]**2.0 + AB[1]**2.0)
u0 = AB[0] / mag
u1 = AB[1] / mag
U = [u0, u1] # Unit vector of AB
V = [-U[1], U[0]] # Unit vector perpendicular to AB
C = [ B[0] - d * u0, B[1] - d * u1 ]
v1 = [C[0] - h * U[0] + w * V[0], C[1] - h * U[1] + w * V[1]]
v2 = [C[0] - h * U[0] - w * V[0], C[1] - h * U[1] - w * V[1]]
return (C, v1, v2)
Related
I am interpolating data from the vertices of a quadrilateral to any random point inside the quadrilateral. I implement this by first doing a coordinate transformation which reshapes the quadrilateral to a unit square and then using bilinear interpolation.
Does any of the python libraries already have an implementation to do this?
Bilinear interpolation between the corners of a quadrilateral gives the coordinates
P = (P00 (1-u) + P10 u) (1-v) + (P01 (1-u) + P11 u) v
= P00 + (P10-P00) u + (P01-P00) v + (P11-P10-P01+P00) uv.
This forms a system of two quadratic equations in the unknowns u, v. If P is inside the quadrilateral, u, v ε [0, 1].
If we take the dot and cross products with P11-P10-P01+P0, the system takes the form
A + B u + C v + D uv = 0
a + b u + c v = 0
from which we eliminate v by
c (A + B u) - (C + D u) (a + b u) = -b D u² + (B c - b C - a D) u + (A c - a C) = 0
which is quadratic univariate. Compute the roots and keep the one in the domain. From u you get v.
Given only the coordinates of the vertices of an acute triangle, how can I efficiently and quickly find the coordinates of the point at which the altitude from a particular vertex meets the opposite base?
A solution using only math, numpy, or scipy would be incredibly helpful.
Needed point is orthogonal projection of vertex point (say vertex C) onto the line containing opposite side (say AB).
To find projection point, get vectors for AB and AC
AB = (B - A) //in coordinates ab.x = b.x-a.x, ab.y = b.y-a.y
AC = (C - A)
and find parameter using scalar product of AB and AC
t =(AB * AC) / (AB * AB)
t =((b.x-a.x)*(c.x-a.x) + (b.y-a.y)*(c.y-a.y)) / ((b.x-a.x)*(b.x-a.x) + (b.y-a.y)*(b.y-a.y))
Projection point coordinates
P = A + AB * t
p.x = a.x + (b.x-a.x) * t
p.y = a.y + (b.y-a.y) * t
That's all
def orthoProjection(ax, ay, bx, by, cx, cy):
abx = bx - ax
aby = by - ay
acx = cx - ax
acy = cy - ay
t = (abx * acx + aby * acy) / (abx * abx + aby * aby)
px = ax + t * abx
py = ay + t * aby
return px, py
print(orthoProjection(0, 0, 4, 4, -1, 5))
>>(2.0, 2.0)
Consider the triangle with vertices at points A, B and C, and you wish to find where the altitude extending from vertex C intersects the line AB.
So first, you can determine the equation for line AB. You have points A and B (Ax, Ay; and Bx, By). Given that you can calculate the slope_AB as (By-Ay)/(Bx-Ax).
Now the format of a line is Y = MX+B where M is the slope just calculated, and B is the Y intercept, so: Y_intercept_AB = Ay - slope_AB * Ax. So the equation for AB is Y = slope_AB*X + Y_intercept_AB.
OK, so now, the slope of the altitude from C to where it intersects line AB (let's call that point D, and the altitude line CD) is the negative reciprocal of the slope of AB; so slope_CD = -(1/slope_AB).
So now, given that you have one point (C) on line CD and its slope, you can get the equation for CD the same way as you did for AB. First, find its Y-intercept: Y_intercept_CD = Cy - slope_CD * Cx
So the equation for CD is Y = slope_CD * X + Y_intercept_CD.
So now you have equations for line AB and line CD:
Y = slope_AB * X + Y_intercept_AB
Y = slope_CD * X + Y_intercept_CD
And your problem is simplified to finding where those lines intersect, which is point D.
From the above equations, since both right-hand sides are equal to Y we can set them equal to each other:
slope_AB * X + Y_intercept_AB = slope_CD * X + Y_intercept_CD
and now it's just a matter of solving for X.
slope_AB * X - slope_CD*X = Y_intercept_CD - Y_intercept_AB
(slope_AB - slope_CD)*X = Y_intercept_CD - Y_intercept_AB
X = (Y_intercept_CD - Y_intercept_AB)/(slope_AB - slope_CD)
That will give you the X-value for D (Dx). For the Y-value, use either line equation. Let's use the one for AB:
Dy = slope_AB * Dx + Y_intercept_AB
Putting it all together, assume a triangle of A=(-4, 2), B=(0, 6), C=(6, -4):
#Points A, B,C:
Ax = -4; Ay = 2
Bx = 0; By = 6
Cx = 6; Cy = -4
#Line AB:
slope_AB = (By - Ay)/(Bx - Ax)
Y_intercept_AB = Ay - slope_AB*Ax
print("AB: slope: %s, intercept: %s" % (slope_AB, Y_intercept_AB))
#Line CD:
slope_CD = -(1/slope_AB)
Y_intercept_CD = Cy - slope_CD*Cx
print("CD: slope: %s, intercept: %s" % (slope_CD, Y_intercept_CD))
#Find the intersection of the two lines AB & CD:
Dx = (Y_intercept_CD - Y_intercept_AB)/(slope_AB - slope_CD)
Dy = slope_AB*Dx + Y_intercept_AB
print("Intersection at (%s, %s)" % (Dx, Dy))
Prints:
AB: slope: 1.0, intercept: 6.0
CD: slope: -1.0, intercept: 2.0
Intersection at (-2.0, 4.0)
One more thing: this will divide-by-zero and fail where points A & B have the same X-value (because it divides by Ax-Bx, which would be zero); but it's a start.
I've searched far and wide but have yet to find a suitable answer to this problem. Given two lines on a sphere, each defined by their start and end points, determine whether or not and where they intersect. I've found this site (http://mathforum.org/library/drmath/view/62205.html) which runs through a good algorithm for the intersections of two great circles, although I'm stuck on determining whether the given point lies along the finite section of the great circles.
I've found several sites which claim they've implemented this, Including some questions here and on stackexchange, but they always seem to reduce back to the intersections of two great circles.
The python class I'm writing is as follows and seems to almost work:
class Geodesic(Boundary):
def _SecondaryInitialization(self):
self.theta_1 = self.point1.theta
self.theta_2 = self.point2.theta
self.phi_1 = self.point1.phi
self.phi_2 = self.point2.phi
sines = math.sin(self.phi_1) * math.sin(self.phi_2)
cosines = math.cos(self.phi_1) * math.cos(self.phi_2)
self.d = math.acos(sines - cosines * math.cos(self.theta_2 - self.theta_1))
self.x_1 = math.cos(self.theta_1) * math.cos(self.phi_1)
self.x_2 = math.cos(self.theta_2) * math.cos(self.phi_2)
self.y_1 = math.sin(self.theta_1) * math.cos(self.phi_1)
self.y_2 = math.sin(self.theta_2) * math.cos(self.phi_2)
self.z_1 = math.sin(self.phi_1)
self.z_2 = math.sin(self.phi_2)
self.theta_wraps = (self.theta_2 - self.theta_1 > PI)
self.phi_wraps = ((self.phi_1 < self.GetParametrizedCoords(0.01).phi and
self.phi_2 < self.GetParametrizedCoords(0.99).phi) or (
self.phi_1 > self.GetParametrizedCoords(0.01).phi) and
self.phi_2 > self.GetParametrizedCoords(0.99))
def Intersects(self, boundary):
A = self.y_1 * self.z_2 - self.z_1 * self.y_2
B = self.z_1 * self.x_2 - self.x_1 * self.z_2
C = self.x_1 * self.y_2 - self.y_1 * self.x_2
D = boundary.y_1 * boundary.z_2 - boundary.z_1 * boundary.y_2
E = boundary.z_1 * boundary.x_2 - boundary.x_1 * boundary.z_2
F = boundary.x_1 * boundary.y_2 - boundary.y_1 * boundary.x_2
try:
z = 1 / math.sqrt(((B * F - C * E) ** 2 / (A * E - B * D) ** 2)
+ ((A * F - C * D) ** 2 / (B * D - A * E) ** 2) + 1)
except ZeroDivisionError:
return self._DealWithZeroZ(A, B, C, D, E, F, boundary)
x = ((B * F - C * E) / (A * E - B * D)) * z
y = ((A * F - C * D) / (B * D - A * E)) * z
theta = math.atan2(y, x)
phi = math.atan2(z, math.sqrt(x ** 2 + y ** 2))
if self._Contains(theta, phi):
return point.SPoint(theta, phi)
theta = (theta + 2* PI) % (2 * PI) - PI
phi = -phi
if self._Contains(theta, phi):
return spoint.SPoint(theta, phi)
return None
def _Contains(self, theta, phi):
contains_theta = False
contains_phi = False
if self.theta_wraps:
contains_theta = theta > self.theta_2 or theta < self.theta_1
else:
contains_theta = theta > self.theta_1 and theta < self.theta_2
phi_wrap_param = self._PhiWrapParam()
if phi_wrap_param <= 1.0 and phi_wrap_param >= 0.0:
extreme_phi = self.GetParametrizedCoords(phi_wrap_param).phi
if extreme_phi < self.phi_1:
contains_phi = (phi < max(self.phi_1, self.phi_2) and
phi > extreme_phi)
else:
contains_phi = (phi > min(self.phi_1, self.phi_2) and
phi < extreme_phi)
else:
contains_phi = (phi > min(self.phi_1, self.phi_2) and
phi < max(self.phi_1, self.phi_2))
return contains_phi and contains_theta
def _PhiWrapParam(self):
a = math.sin(self.d)
b = math.cos(self.d)
c = math.sin(self.phi_2) / math.sin(self.phi_1)
param = math.atan2(c - b, a) / self.d
return param
def _DealWithZeroZ(self, A, B, C, D, E, F, boundary):
if (A - D) is 0:
y = 0
x = 1
elif (E - B) is 0:
y = 1
x = 0
else:
y = 1 / math.sqrt(((E - B) / (A - D)) ** 2 + 1)
x = ((E - B) / (A - D)) * y
theta = (math.atan2(y, x) + PI) % (2 * PI) - PI
return point.SPoint(theta, 0)
def GetParametrizedCoords(self, param_value):
A = math.sin((1 - param_value) * self.d) / math.sin(self.d)
B = math.sin(param_value * self.d) / math.sin(self.d)
x = A * math.cos(self.phi_1) * math.cos(self.theta_1) + (
B * math.cos(self.phi_2) * math.cos(self.theta_2))
y = A * math.cos(self.phi_1) * math.sin(self.theta_1) + (
B * math.cos(self.phi_2) * math.sin(self.theta_2))
z = A * math.sin(self.phi_1) + B * math.sin(self.phi_2)
new_phi = math.atan2(z, math.sqrt(x**2 + y**2))
new_theta = math.atan2(y, x)
return point.SPoint(new_theta, new_phi)
EDIT: I forgot to specify that if two curves are determined to intersect, I then need to have the point of intersection.
A simpler approach is to express the problem in terms of geometric primitive operations like the dot product, the cross product, and the triple product. The sign of the determinant of u, v, and w tells you which side of the plane spanned by v and w contains u. This enables us to detect when two points are on opposite sites of a plane. That's equivalent to testing whether a great circle segment crosses another great circle. Performing this test twice tells us whether two great circle segments cross each other.
The implementation requires no trigonometric functions, no division, no comparisons with pi, and no special behavior around the poles!
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def dot(v1, v2):
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z
def cross(v1, v2):
return Vector(v1.y * v2.z - v1.z * v2.y,
v1.z * v2.x - v1.x * v2.z,
v1.x * v2.y - v1.y * v2.x)
def det(v1, v2, v3):
return dot(v1, cross(v2, v3))
class Pair:
def __init__(self, v1, v2):
self.v1 = v1
self.v2 = v2
# Returns True if the great circle segment determined by s
# straddles the great circle determined by l
def straddles(s, l):
return det(s.v1, l.v1, l.v2) * det(s.v2, l.v1, l.v2) < 0
# Returns True if the great circle segments determined by a and b
# cross each other
def intersects(a, b):
return straddles(a, b) and straddles(b, a)
# Test. Note that we don't need to normalize the vectors.
print(intersects(Pair(Vector(1, 0, 1), Vector(-1, 0, 1)),
Pair(Vector(0, 1, 1), Vector(0, -1, 1))))
If you want to initialize unit vectors in terms of angles theta and phi, you can do that, but I recommend immediately converting to Cartesian (x, y, z) coordinates to perform all subsequent calculations.
Intersection using plane trig can be calculated using the below code in UBasic.
5 'interx.ub adapted from code at
6 'https://rosettacode.org
7 '/wiki/Find_the_intersection_of_two_linesSinclair_ZX81_BASIC
8 'In U Basic by yuji kida https://en.wikipedia.org/wiki/UBASIC
10 XA=48.7815144526:'669595.708
20 YA=-117.2847245001:'2495736.332
30 XB=48.7815093807:'669533.412
40 YB=-117.2901673467:'2494425.458
50 XC=48.7824947147:'669595.708
60 YC=-117.28751374:'2495736.332
70 XD=48.77996737:'669331.214
80 YD=-117.2922957:'2494260.804
90 print "THE TWO LINES ARE:"
100 print "YAB=";YA-XA*((YB-YA)/(XB-XA));"+X*";((YB-YA)/(XB-XA))
110 print "YCD=";YC-XC*((YD-YC)/(XD-XC));"+X*";((YD-YC)/(XD-XC))
120 X=((YC-XC*((YD-YC)/(XD-XC)))-(YA-XA*((YB-YA)/(XB-XA))))/(((YB-YA)/(XB-XA))-((YD-YC)/(XD-XC)))
130 print "Lat = ";X
140 Y=YA-XA*((YB-YA)/(XB-XA))+X*((YB-YA)/(XB-XA))
150 print "Lon = ";Y
160 'print "YCD=";YC-XC*((YD-YC)/(XD-XC))+X*((YD-YC)/(XD-XC))
Given 3 points in space (3D): A = (x1, y1, z1), B = (x2, y2, z2) C = (x3, y3, z3); then how to find the center and radius of the circle (arc) that passes through these three points, i.e. find circle equation? Using Python and Numpy here is my initial code
import numpy as np
A = np.array([x1, y1, z1])
B = np.array([x2, y2, z2])
C = np.array([x3, y3, z3])
#Find vectors connecting the three points and the length of each vector
AB = B - A
BC = C - B
AC = C - A
# Triangle Lengths
a = np.linalg.norm(AB)
b = np.linalg.norm(BC)
c = np.linalg.norm(AC)
From the Circumradius definition, the radius can be found using:
R = (a * b * c) / np.sqrt(2.0 * a**2 * b**2 +
2.0 * b**2 * c**2 +
2.0 * c**2 * a**2 -
a**4 - b**4 - c**4)
However, I am having problems finding the Cartesian coordinates of the center. One possible solution is to use the "Barycentric Coordinates" of the triangle points to find the trilinear coordinates of the circumcenter (Circumcenter).
First (using this source) we find the circumcenter barcyntric coordinates:
#barcyntric coordinates of center
b1 = a**2 * (b**2 + c**2 - a**2)
b2 = b**2 * (c**2 + a**2 - b**2)
b3 = c**2 * (a**2 + b**2 - c**2)
Then the Cartesian coordinates of the center (P) would be:
Px = (b1 * A[0]) + (b2 * B[0]) + (b3 * C[0])
Py = (b1 * A[1]) + (b2 * B[1]) + (b3 * C[1])
Pz = (b1 * A[2]) + (b2 * B[2]) + (b3 * C[2])
However, the barcyntric coordinates values above do not seem to be correct. When solved with an example of known values, the radius is correct, but the coordinates of the center are not.
Example: For these three points:
A = np.array([2.0, 1.5, 0.0])
B = np.array([6.0, 4.5, 0.0])
C = np.array([11.75, 6.25, 0.0])
The radius and center coordinates are:
R = 15.899002930062595
P = [13.4207317073, -9.56097560967, 0]
Any ideas on how to find the center coordinates?
There are two issues with your code.
The first is in the naming convention. For all the formulas you are using to hold, the side of length a has to be the one opposite the point A, and similarly for b and B and c and C. You can solve that by computing them as:
a = np.linalg.norm(C - B)
b = np.linalg.norm(C - A)
c = np.linalg.norm(B - A)
The second has to do with the note in your source for the barycentric coordinates of the circumcenter: not necessarily homogeneous. That is, they need not be normalized in any way, and the formula you are using to compute the Cartesian coordinates from the barycentric ones is only valid when they add up to one.
Fortunately, you only need to divide the resulting Cartesian coordinates by b1 + b2 + b3 to get the result you are after. Streamlining a little bit your code for efficiency, I get the results you expect:
>>> A = np.array([2.0, 1.5, 0.0])
>>> B = np.array([6.0, 4.5, 0.0])
>>> C = np.array([11.75, 6.25, 0.0])
>>> a = np.linalg.norm(C - B)
>>> b = np.linalg.norm(C - A)
>>> c = np.linalg.norm(B - A)
>>> s = (a + b + c) / 2
>>> R = a*b*c / 4 / np.sqrt(s * (s - a) * (s - b) * (s - c))
>>> b1 = a*a * (b*b + c*c - a*a)
>>> b2 = b*b * (a*a + c*c - b*b)
>>> b3 = c*c * (a*a + b*b - c*c)
>>> P = np.column_stack((A, B, C)).dot(np.hstack((b1, b2, b3)))
>>> P /= b1 + b2 + b3
>>> R
15.899002930062531
>>> P
array([ 13.42073171, -9.56097561, 0. ])
As an extension to the original problem: Now suppose we have an arc of known length (e.g. 1 unit) extending from point A as defined above (away from point B). How to find the 3D coordinates of the end point (N)? The new point N lies on the same circle passing through points A, B & C.
This can be solved by first finding the angle between the two vectors PA & PN:
# L = Arc Length
theta = L / R
Now all we need to do is rotate the vector PA (Radius) by this angle theta in the correct direction. In order to do that, we need the 3D rotation matrix. For that we use the Euler–Rodrigues formula:
def rotation_matrix_3d(axis, theta):
axis = axis / np.linalg.norm(axis)
a = np.cos(theta / 2.0)
b, c, d = axis * np.sin(theta / 2.0)
rot = np.array([[a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c)],
[ 2*(b*c+a*d), a*a+c*c-b*b-d*d, 2*(c*d-a*b)],
[ 2*(b*d-a*c), 2*(c*d+a*b), a*a+d*d-b*b-c*c]])
return rot
Next, we need to find the rotation axis to rotate PA around. This can be achieved by finding the axis normal to the plane of the circle passing through A, B & C. The coordinates of point N can then be found from the center of the circle.
PA = P - A
PB = P - B
xx = np.cross(PB, PA)
r3d = rotation_matrix_3d(xx, theta)
PN = np.dot(r3d, PA)
N = P - PN
as an example, we want to find coordinates of point N that are 3 degrees away from point A, the coordinates would be
N = (1.43676498, 0.8871264, 0.)
Which is correct! (manually verified using CAD program)
I plan on writing this code in python, but this is more of a general algorithm-design problem than one having to do with any particular language. I'm writing code to simulate a hybrid rocket engine, and long story short, the problem involves finding both the perimeter and the area of a figure composed of many (possibly thousands) of overlapping circles.
I saw this on stackexchange:
Combined area of overlapping circles
except I need to find the perimeter also. Someone in that thread mentioned Monte Carlo (random point guess) methods, but can that be used to find the perimeter as well as the area?
Thanks in advance.
I am adding a second answer instead of expanding the first one, because I am quite positive that the other answer is correct, but I am not so sure if this answer is as correct. I have done some simple testing, and it seems to work, but please point out my errors.
This is basically a quick-n-dirty implementation of what I stated before:
from math import atan2, pi
def intersectLine (a, b):
s0, e0, s1, e1 = a [0], a [1], b [0], b [1]
s = max (s0, s1)
e = min (e0, e1)
if e <= s: return (0, 0)
return (s, e)
class SectorSet:
def __init__ (self, sectors):
self.sectors = sectors [:]
def __repr__ (self):
return repr (self.sectors)
def __iadd__ (self, other):
acc = []
for mine in self.sectors:
for others in other.sectors:
acc.append (intersectLine (mine, others) )
self.sectors = [x for x in acc if x [0] != x [1] ]
return self
class Circle:
CONTAINS = 0
CONTAINED = 1
DISJOINT = 2
INTERSECT = 3
def __init__ (self, x, y, r):
self.x = float (x)
self.y = float (y)
self.r = float (r)
def intersect (self, other):
a, b, c, d, r0, r1 = self.x, self.y, other.x, other.y, self.r, other.r
r0sq, r1sq = r0 ** 2, r1 ** 2
Dsq = (c - a) ** 2 + (d - b) ** 2
D = Dsq ** .5
if D >= r0 + r1:
return Circle.DISJOINT, None, None
if D <= abs (r0 - r1):
return Circle.CONTAINED if r0 < r1 else Circle.CONTAINS, None, None
dd = .25 * ( (D + r0 + r1) * (D + r0 - r1) * (D - r0 + r1) * (-D + r0 + r1) ) ** .5
x = (a + c) / 2. + (c - a) * (r0sq - r1sq) / 2. / Dsq
x1 = x + 2 * (b - d) / Dsq * dd
x2 = x - 2 * (b - d) / Dsq * dd
y = (b + d) / 2. + (d - b) * (r0sq - r1sq) / 2. / Dsq
y1 = y - 2 * (a - c) / Dsq * dd
y2 = y + 2 * (a - c) / Dsq * dd
return Circle.INTERSECT, (x1, y1), (x2, y2)
def angle (self, point):
x0, y0, x, y = self.x, self.y, point [0], point [1]
a = atan2 (y - y0, x - x0) + 1.5 * pi
if a >= 2 * pi: a -= 2 * pi
return a / pi * 180
def sector (self, other):
typ, i1, i2 = self.intersect (other)
if typ == Circle.DISJOINT: return SectorSet ( [ (0, 360) ] )
if typ == Circle.CONTAINS: return SectorSet ( [ (0, 360) ] )
if typ == Circle.CONTAINED: return SectorSet ( [] )
a1 = self.angle (i1)
a2 = self.angle (i2)
if a1 > a2: return SectorSet ( [ (0, a2), (a1, 360) ] )
return SectorSet ( [ (a1, a2) ] )
def outline (self, others):
sectors = SectorSet ( [ (0, 360) ] )
for other in others:
sectors += self.sector (other)
u = 2 * self.r * pi
total = 0
for start, end in sectors.sectors:
total += (end - start) / 360. * u
return total
def outline (circles):
total = 0
for circle in circles:
others = [other for other in circles if circle != other]
total += circle.outline (others)
return total
a = Circle (0, 0, 2)
b = Circle (-2, -1, 1)
c = Circle (2, -1, 1)
d = Circle (0, 2, 1)
print (outline ( [a, b, c, d] ) )
Formula for calculating the intersecting points of two circles shamelessly stolen from here.
Let's say your have the circles c1, c2, ..., cn.
Take c1 and intersect it with each other circle. Initialize an empty list of circle sectors for c1.
If the intersection is zero points or one point and c1 is outside the other circle, then add a 360 degree sector to the list.
If the intersection is zero points or one point and c1 is inside the other circle, then the effective outline of c1 is 0, stop processing c1 with an outline of 0 and take the next circle.
If the intersection is two points, add the sector of c1, which is not in the other circle to the list of circle sectors of c1.
The effective outline of c1, is the length of the intersection of all sectors within the list of sectors of c1. This intersection of sectors can consist of various disjoint sectors.
Rinse and repeat for all circles and then sum up the resulting values.