Middlepoint on circle between 2 points - python

I'm trying to find middlepoint on the circle between 2 points, pictorial drawing
There are given radius, p1, p2 and middle of the circle.
Distance betweeen p1 and p2 is an diameter, and I'm trying to make up python formula that returns point on the circle between those 2 points. I know this is rather silly question but I'm trying to make this for 3 hours now and all I can find on web is distance between those 2 points.
I'm trying to find formula for p3 (like in the picture)
That's what I ended up making so far:
import math
points = [[100, 200], [250, 350]]
midpoint = (int(((points[0][0] + points[1][0]) / 2)), int(((points[0][1] + points[1][1]) / 2)))
radius = int(math.sqrt(((points[1][0] - points[0][0])**2) + ((points[1][1] - points[0][1])**2))) // 2
# This below is wrong
print(int(midpoint[0] - math.sqrt((points[0][1] - midpoint[1]) ** 2)),
int(midpoint[1] - math.sqrt((points[0][0] - midpoint[1]) ** 2)))

There is no need for trigonometry, which is total overkill.
The center of the circle is Xs= (X1 + X2) / 2, Ys= (Y1 + Y2) / 2.
The two opposite "middlepoints" are given by X3 = Xs - (Y1 - Ys), Y3 = Ys + (X1 - Xs) and X4 = Xs + (Y1 - Ys), Y4 = Ys - (X1 - Xs).
If you prefer direct formulas, X3 = (X1 + X2 - Y1 + Y2) / 2, Y3 = (X1 - X2 + Y1 + Y2) / 2...

To generate points on the circle use polar equation (https://math.stackexchange.com/questions/154550/polar-equation-of-a-circle)
import math
import random
points = [[100, 200], [250, 350]]
midpoint = (int(((points[0][0] + points[1][0]) / 2)), int(((points[0][1] + points[1][1]) / 2)))
radius = int(math.sqrt(((points[1][0] - points[0][0])**2) + ((points[1][1] - points[0][1])**2))) // 2
angle = random.uniform(0, 2 * math.pi)
x_relative = radius * math.cos(angle)
y_relative = radius * math.sin(angle)
x = midpoint[0] + x_relative
y = midpoint[1] + y_relative
print(f"{x} {y}")
To find middle point on the circle between points:
import math
points = [[100, 200], [250, 350]]
midpoint = (int(((points[0][0] + points[1][0]) / 2)), int(((points[0][1] + points[1][1]) / 2)))
radius = int(math.sqrt(((points[1][0] - points[0][0])**2) + ((points[1][1] - points[0][1])**2))) // 2
points_relative = [[points[0][0] - midpoint[0], points[0][1] - midpoint[1]], [points[1][0] - midpoint[0], points[1][1] - midpoint[1]]]
midpoint_points_relative = [[points_relative[0][1], - points_relative[0][0]], [points_relative[1][1], - points_relative[1][0]]]
midpoint_points = [[midpoint_points_relative[0][0] + midpoint[0], midpoint_points_relative[0][1] + midpoint[1]], [midpoint_points_relative[1][0] + midpoint[0], midpoint_points_relative[1][1] + midpoint[1]]]
print(points)
print(midpoint)
print(points_relative)
print(midpoint_points)
You can use this page to test results: https://www.desmos.com/calculator/mhq4hsncnh

Related

Tkinter: Label lines of triangle (create_polygon)

I have this python function:
def draw(a, b, c):
A = (0, 0)
B = (c, 0)
hc = (2 * (a**2*b**2 + b**2*c**2 + c**2*a**2) - (a**4 + b**4 + c**4))**0.5 / (2.*c)
dx = (b**2 - hc**2)**0.5
if abs((c - dx)**2 + hc**2 - a**2) > 0.01: dx = -dx
C = (dx, hc)
coords = [float((x + 1) * 75) for x in A+B+C]
canvas.delete("all")
canvas.create_polygon(*coords, outline='black', fill='white')
I can enter a, b and c and a triangle is drawed by Tkinter.
So it looks like this:
The problem
Now I want to label the lines a, b and c. Like this:
And the label should always be in the middle of the lines.
How can I do this?
I looked at a lot of posts, but none is for dynamically generated triangles.
This is a more a math problem than a Tkinter question. You have the vertices coordinates, so you can compute the coordinates of middle of each edge. However, this would put the text on the edge instead of a bit aside. To offset a bit the text, you can do a barycenter between the middle of the edge and the opposite vertex, giving the x coordinates of the 'c' label:
xc = (xA + xB)/2 * (1 - w) + xC
If you take a slightly negative weight w, then the text will be slightly outside the edge, however, the offset from the edge is relative to the distance between the middle of the edge and the vertex. To avoid this, we need to divide the wanted absolute offset by this distance:
def text_coords(x1, y1, x2, y2, offset):
d = sqrt((x1-x2)**2 + (y1-y2)**2)
w = - offset / d
xt = (1 - w) * x1 + w * x2
yt = (1 - w) * y1 + w * y2
return xt, yt
(x1, y1) are the coordinates of the middle of the edge and (x2, y2) the coordinates of the opposite vertex. Then we use this function in draw() to add the edge labels:
def draw(a, b, c):
A = (0, 0)
B = (c, 0)
hc = (2 * (a**2*b**2 + b**2*c**2 + c**2*a**2) - (a**4 + b**4 + c**4))**0.5 / (2.*c)
dx = (b**2 - hc**2)**0.5
if abs((c - dx)**2 + hc**2 - a**2) > 0.01: dx = -dx
C = (dx, hc)
coords = [float((x + 1) * 75) for x in A+B+C]
canvas.delete("all")
canvas.create_polygon(*coords, outline='black', fill='white')
xA, yA = coords[:2]
xB, yB = coords[2:4]
xC, yC = coords[4:]
xAB, yAB = (xA + xB)/2, (yA + yB)/2
xAC, yAC = (xA + xC)/2, (yA + yC)/2
xCB, yCB = (xC + xB)/2, (yC + yB)/2
dc = distance(xAB, yAB, xC, yC)
db = distance(xAC, yAC, xB, yB)
da = distance(xCB, yCB, xA, yA)
xc, yc = text_coords(xAB, yAB, xC, yC, 10)
xb, yb = text_coords(xAC, yAC, xB, yB, 10)
xa, ya = text_coords(xCB, yCB, xA, yA, 10)
canvas.create_text(xc, yc, text='c')
canvas.create_text(xa, ya, text='a')
canvas.create_text(xb, yb, text='b')
For instance, draw(3, 4, 5) gives:

Meshgrid/lattice/matrix of an ellipse area

I'm likely using the wrong terms but seeking some help.
I would like to generate an array of x,y values for a grid that sits within the perimeter of an ellipse shape.
There is code here: http://people.sc.fsu.edu/~jburkardt/c_src/ellipse_grid/ellipse_grid.html to accomplish this in Python.
However, for my purpose the ellipses have been rotated to a certain degree. The current equation does not account for this and need some help to account for this transformation, unsure how to change the code to do this?
I've been looking into np.meshrid function as well, so if there are better ways to do this, please say.
Many thanks.
Given an ellipse in the Euclidean plane in its most general form as quadratic curve in the form
f(x,y) = a x^2 + 2b x y + c y^2 + 2d x + 2f y + g,
one can compute the center (x0,y0) by
((cd-bf)/(b^2-ac), (af-bd)/(b^2-ac))
(see equations 19 and 20 at Ellipse on MathWorld). The length of the major axis a_m can be computed by equation 21 on the same page.
Now it suffices to find all grid points (x,y) inside the circle with center (x0,y0) and radius a_m with
sign(f(x,y)) = sign(f(x0,y0)).
To generate lattice points inside ellipse, we have to know where horizontal line intersects that ellipse.
Equation of zero-centered ellipse, rotated by angle Theta:
x = a * Cos(t) * Cos(theta) - b * Sin(t) * Sin(theta)
y = a * Cos(t) * Sin(theta) + b * Sin(t) * Cos(theta)
To simplify calculations, we can introduce pseudoangle Fi and magnitude M (constants for given ellipse)
Fi = atan2(a * Sin(theta), b * Cos(theta))
M = Sqrt((a * Sin(theta))^2 + (b * Cos(theta))^2)
so
y = M * Sin(Fi) * Cos(t) + M * Cos(Fi) * Sin(t)
y/M = Sin(Fi) * Cos(t) + Cos(Fi) * Sin(t)
y/M = Sin(Fi + t)
and solution for given horizontal line at position y are
Fi + t = ArcSin( y / M)
Fi + t = Pi - ArcSin( y / M)
t1 = ArcSin( y / M) - Fi //note two values
t2 = Pi - ArcSin( y / M) - Fi
Substitute both values of t in the first equation and get values of X for given Y, and generate one lattice point sequence
To get top and bottom coordinates, differentiate y
y' = M * Cos(Fi + t) = 0
th = Pi/2 - Fi
tl = -Pi/2 - Fi
find corresponding y's and use them as starting and ending Y-coordinates for lines.
import math
def ellipselattice(cx, cy, a, b, theta):
res = []
at = a * math.sin(theta)
bt = b * math.cos(theta)
Fi = math.atan2(at, bt)
M = math.hypot(at, bt)
ta = math.pi/2 - Fi
tb = -math.pi/2 - Fi
y0 = at * math.cos(ta) + bt *math.sin(ta)
y1 = at * math.cos(tb) + bt *math.sin(tb)
y0, y1 = math.ceil(cy + min(y0, y1)), math.floor(cy + max(y0, y1))
for y in range(y0, y1+1):
t1 = math.asin(y / M) - Fi
t2 = math.pi - math.asin(y / M) - Fi
x1 = a * math.cos(t1) * math.cos(theta) - b* math.sin(t1) * math.sin(theta)
x2 = a * math.cos(t2) * math.cos(theta) - b* math.sin(t2) * math.sin(theta)
x1, x2 = math.ceil(cx + min(x1, x2)), math.floor(cx + max(x1, x2))
line = [(x, y) for x in range(x1, x2 + 1)]
res.append(line)
return res
print(ellipselattice(0, 0, 4, 3, math.pi / 4))

python: interpolation: finding a rectangle that minimally include a point

I am implementing a bilinear interpolation as in How to perform bilinear interpolation in Python
I have a sorted list of points that are the vertexes of my regular grid.
[[x1,y1,z1],[x2,y2,z2],[x3,y3,z3],[x4,y4,z4],[x5,y5,z5],...]
I want to interpolate linearly on the point (x,y). I have written the following code
def f(x, y, points):
for i in range(len(points)-1, -1, -1):
if (x>points[i][0])and(y>points[i][1]):
break
try:
pp = [points[i], points[i+1]]
except IndexError:
pp = [points[i], points[i-1]]
for j in range(len(points)):
if (x<points[j][0])and(y<points[j][1]):
break
pp.append(points[j-1])
pp.append(points[j])
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = pp
return (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1))
but this code doesn't work on the boundaries. I would think this is common problem in interpolation, so I was wondering how I should select the smallest rectangle of points around (x,y) from my regular grid.
Your grid is regular, so you don't need to traverse all points to determine cell indexes. Just divide coordinates by cell size and round result to smaller integer. 1D example: if first point has coordinate 1 and cell size is 2, point 6 lies at int (6-1)/2 = 2-nd interval
Restrict result index to ensure that it is in grid limits - so points outside grid will use border cells
i = int((x - points[i][0]) / xsize) #not sure what is the best way in Python
if (i < 0):
i = 0
if (i >= XCount):
i = XCount - 1
// same for j and y-coordinate
Following the suggestions in the comments I have written the following code:
def f(x, y, points):
points = sorted(points)
xunique = np.unique([point[0] for point in points])
yunique = np.unique([point[1] for point in points])
xmax = np.max(xunique)
ymax = np.max(yunique)
deltax = xunique[1] - xunique[0]
deltay = yunique[1] - yunique[0]
x0 = xunique[0]
y0 = yunique[0]
ni = len(xunique)
nj = len(yunique)
i1 = int(np.floor((x-x0)/deltax))
if i1 == ni:
i1 = i1 - 1
i2 = int(np.ceil((x-x0)/deltax))
if i2 == ni:
i2 = i2 - 1
j1 = int(np.floor((y-y0)/deltay))
if j1 == nj:
j1 = j1 - 1
j2 = int(np.ceil((y-y0)/deltay))
if j2 == ni:
j2 = j2 - 1
pp=[]
if (i1==i2):
if i1>0:
i1=i1-1
else:
i2=i2+1
if (j1==j2):
if j1>0:
j1=j1-1
else:
j2=j2+1
pp=[points[i1 * nj + j1], points[i1 * nj + j2],
points[i2 * nj + j1], points[i2 * nj + j2]]
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = pp
return (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1))

How to get the x,y coordinates of a offset spline from a x,y list of points and offset distance

I need to make an offset parallel enclosure of an airfoil profile curve, but I cant figure out how to make all the points be equidistant to the points on the primary profile curve at desired distance.
this is my example airfoil profile
this is my best and not good approach
EDIT #Patrick Solution for distance 0.2
You'll have to special-case slopes of infinity/zero, but the basic approach is to use interpolation to calculate the slope at a point, and then find the perpendicular slope, and then calculate the point at that distance.
I have modified the example from here to add a second graph. It works with the data file you provided, but you might need to change the sign calculation for a different envelope.
EDIT As per your comments about wanting the envelope to be continuous, I have added a cheesy semicircle at the end that gets really close to doing this for you. Essentially, when creating the envelope, the rounder and more convex you can make it, the better it will work. Also, you need to overlap the beginning and the end, or you'll have a gap.
Also, it could almost certainly be made more efficient -- I am not a numpy expert by any means, so this is just pure Python.
def offset(coordinates, distance):
coordinates = iter(coordinates)
x1, y1 = coordinates.next()
z = distance
points = []
for x2, y2 in coordinates:
# tangential slope approximation
try:
slope = (y2 - y1) / (x2 - x1)
# perpendicular slope
pslope = -1/slope # (might be 1/slope depending on direction of travel)
except ZeroDivisionError:
continue
mid_x = (x1 + x2) / 2
mid_y = (y1 + y2) / 2
sign = ((pslope > 0) == (x1 > x2)) * 2 - 1
# if z is the distance to your parallel curve,
# then your delta-x and delta-y calculations are:
# z**2 = x**2 + y**2
# y = pslope * x
# z**2 = x**2 + (pslope * x)**2
# z**2 = x**2 + pslope**2 * x**2
# z**2 = (1 + pslope**2) * x**2
# z**2 / (1 + pslope**2) = x**2
# z / (1 + pslope**2)**0.5 = x
delta_x = sign * z / ((1 + pslope**2)**0.5)
delta_y = pslope * delta_x
points.append((mid_x + delta_x, mid_y + delta_y))
x1, y1 = x2, y2
return points
def add_semicircle(x_origin, y_origin, radius, num_x = 50):
points = []
for index in range(num_x):
x = radius * index / num_x
y = (radius ** 2 - x ** 2) ** 0.5
points.append((x, -y))
points += [(x, -y) for x, y in reversed(points)]
return [(x + x_origin, y + y_origin) for x, y in points]
def round_data(data):
# Add infinitesimal rounding of the envelope
assert data[-1] == data[0]
x0, y0 = data[0]
x1, y1 = data[1]
xe, ye = data[-2]
x = x0 - (x0 - x1) * .01
y = y0 - (y0 - y1) * .01
yn = (x - xe) / (x0 - xe) * (y0 - ye) + ye
data[0] = x, y
data[-1] = x, yn
data.extend(add_semicircle(x, (y + yn) / 2, abs((y - yn) / 2)))
del data[-18:]
from pylab import *
with open('ah79100c.dat', 'rb') as f:
f.next()
data = [[float(x) for x in line.split()] for line in f if line.strip()]
t = [x[0] for x in data]
s = [x[1] for x in data]
round_data(data)
parallel = offset(data, 0.1)
t2 = [x[0] for x in parallel]
s2 = [x[1] for x in parallel]
plot(t, s, 'g', t2, s2, 'b', lw=1)
title('Wing with envelope')
grid(True)
axes().set_aspect('equal', 'datalim')
savefig("test.png")
show()
If you are willing (and able) to install a third-party tool, I'd highly recommend the Shapely module. Here's a small sample that offsets both inward and outward:
from StringIO import StringIO
import matplotlib.pyplot as plt
import numpy as np
import requests
import shapely.geometry as shp
# Read the points
AFURL = 'http://m-selig.ae.illinois.edu/ads/coord_seligFmt/ah79100c.dat'
afpts = np.loadtxt(StringIO(requests.get(AFURL).content), skiprows=1)
# Create a Polygon from the nx2 array in `afpts`
afpoly = shp.Polygon(afpts)
# Create offset airfoils, both inward and outward
poffafpoly = afpoly.buffer(0.03) # Outward offset
noffafpoly = afpoly.buffer(-0.03) # Inward offset
# Turn polygon points into numpy arrays for plotting
afpolypts = np.array(afpoly.exterior)
poffafpolypts = np.array(poffafpoly.exterior)
noffafpolypts = np.array(noffafpoly.exterior)
# Plot points
plt.plot(*afpolypts.T, color='black')
plt.plot(*poffafpolypts.T, color='red')
plt.plot(*noffafpolypts.T, color='green')
plt.axis('equal')
plt.show()
And here's the output; notice how the 'bowties' (self-intersections) on the inward offset are automatically removed:

Check if two lines (each with start-end positions) overlap in python

I have the following two sets of position each correspond to the start and end position:
line T: t1-t2 (t1 = start_pos, t2 = end_pos)
line S: s1-s2 (s1 = start_pos, t2 = end_pos)
I want to write the algorithm in Python to check if T intersect with S.
Example 1:
t1-t2=55-122 and s1-s2=58-97
s1------------s2
t1-----------------t2
This should return True
Example 2:
t1-t2=4-66 / t1-t2=143-166 and s1-s2=80-141
s1----s2
t1--t2 t1---t2
Both instances of T should return False
But why this code failed:
def is_overlap(pos, dompos):
"""docstring for is_overlap"""
t1,t2 = [int(x) for x in pos.split("-")]
s1,s2 = [int(x) for x in dompos.split("-")]
# Here we define the instance of overlapness
if (t1 >= s1 and t2 >= s2) or \
(t1 >= s1 and t2 <= s2) or \
(t1 <= s1 and t2 >= s2) or \
(t1 <= s1 and t2 <= s2):
return True
else:
return False
which output this:
In [2]: is_overlap('55-122', '58-97')
Out[2]: True
In [3]: is_overlap('4-66', '80-141')
Out[3]: True
In [4]: is_overlap('143-166', '80-141')
Out[4]: True
What's the right way to do it?
Consider a span [a, b] and another span [x, y]. They either overlap or they are separate.
If they are separate, one of two things must be true:
[a, b] is to the left of [x, y], or
[x, y] is to the left of [a, b].
If [a, b] is to the left of [x, y], we have b < x.
If [x, y] is to the left of [a, b], we have y < a.
If neither of these is true, the spans cannot be separate. They must overlap.
This logic is implemented in the following function.
def are_separate(r, s): # r and s are ordered pairs
(a, b) = r
(x, y) = s
if b < x or y < a:
return True
else:
return False
More concisely:
def are_separate(r, s):
(a, b) = r
(x, y) = s
return b < x or y < a
Even more concisely:
def are_separate(r, s):
return r[1] < s[0] or s[1] < r[0]
If you want the contrary function, are_overlapping, just negate the expression:
def are_overlapping(r, s):
return not(r[1] < s[0] or s[1] < r[0])
Which is logically equivalent to:
def are_overlapping(r, s):
return r[1] >= s[0] and s[1] >= r[0]
Your conditions are wrong; check them again. For instance, the first condition implies that (t_1, t_2) = (100, 200) and (s_1, s_2) = (50, 60) to be a valid set of overlapping lines. But clearly, they aren't.
Something else you might want to consider is if the user inputs the coordinates in backwards. What if he puts in something like '80-30'?
For those who want two lines in two dimensions, #michael's answer is not enough. The following code is based on the equation found in the Wikipedia page listed just above the code as well as a little Pythagoras to check the intersection lies between the appropriate points. https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
x1, y1, x2, y2 = 200, 200, 300, 300
x3, y3, x4, y4 = 200, 150, 300, 350
px = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / \
((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
py = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / \
((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
if ((x1 - px) ** 2 + (y1 - py) ** 2) ** 0.5 + ((x2 - px) ** 2 + (y2 - py) ** 2) ** 0.5 == \
((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 and \
((x3 - px) ** 2 + (y3 - py) ** 2) ** 0.5 + ((x4 - px) ** 2 + (y4 - py) ** 2) ** 0.5 == \
((x3 - x4) ** 2 + (y3 - y4) ** 2) ** 0.5:
print("Overlap at point: (%s, %s)" % (px, py))
else:
print("No overlap.")

Categories