I'm trying to replicate a function used in a study, but don't really have the mathematical background to fully appreciate how this ought to be done. The measure takes three points from a tongue contour and uses these three points to calculate the radius of a circle that would pass through them. I have looked here and found something that does this in python. I've tried to modify the code so it would work in R with my own data. (Posted at the bottom)
The problem is, based on the study I am reading, I then need to calculate the concavity of the circumference of the circle and find the inverse of the radius of the circle passing through the three points. I'm googling and googling but honestly this means nothing to me. The only thing I have found is that I seem to need to calculate the first and second derivatives of the tongue surface curve. I'm really hoping somebody might be able to help explore how I would do this in R. To be brutally honest, I am not overly interested in understanding the mathematics here, just how to actually implement it.
Edit: I thought below was the formula that I need to replicate. As MBo points out, this isn't the case.
I'll repeat something from another study that used a very, very similar method in case that helps.
'Any three points (A, B, C) can be conceived as lying on the circumference of a circle. The circle will have a radius, the inverse of which represents the curvature of the circle passing through those three points.' The set of three points 'yields a curvature numeber which is the inverse of the radius of the circle passing through them. Three points which lie along a straight line have a curvature of zero, since their concavity is zero and this becomes the numerator of of the curvature equation'. It's this that I need to do, but don't know where to begin operationalising it in R.
The code below is the python code I'm attempting to replicate for my purposes in R to obtain the radius from three points. I have no idea how to proceed after that.
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)
Here's my R attempt.
I haven't written the function yet, but I will be looking at three points along a curve, A, B and C. The function will extract x and y values for each of these three points (called x_value_a, y_value_a etc.). Once this is done. I will run the code that follows. It's after this that I am properly stumped.
temp = x_value_b ^ 2 + y_value_b ^ 2
bc = (x_value_a ^ 2 + y_value_a ^ 2 - temp) / 2
cd = (temp - x_value_c ^ 2 - y_value_c ^ 2) / 2
det = (x_value_a - x_value_b) * (y_value_b - y_value_c) - (x_value_b - x_value_c) * (y_value_a - y_value_b)
cx = (bc * (y_value_b - y_value_c) - cd * (y_value_a - y_value_b)) / det
cy = ((x_value_a - x_value_b) * cd - (x_value_b - x_value_c) * bc) / det
radius = sqrt((cx - x_value_a)^2 + (cy - y_value_a)^2)
Any help would be greatly appreciated. I'm sorry for my mathematical ignorance.
If you only want the Python script translated into R, that's pretty straightforward (I don't quite understand why you split it up in the R code you added).
define_circle = function(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 warning.
temp = p2[1] * p2[1] + p2[2] * p2[2]
bc = (p1[1] * p1[1] + p1[2] * p1[2] - temp) / 2
cd = (temp - p3[1] * p3[1] - p3[2] * p3[2]) / 2
det = (p1[1] - p2[1]) * (p2[2] - p3[2]) - (p2[1] - p3[1]) * (p1[2] - p2[2])
if (abs(det) < 1.0e-6) {
return(c("Three points form a line"))
} else {
# Center of circle
cx = (bc*(p2[2] - p3[2]) - cd*(p1[2] - p2[2])) / det
cy = ((p1[1] - p2[1]) * cd - (p2[1] - p3[1]) * bc) / det
radius = sqrt((cx - p1[1])**2 + (cy - p1[2])**2)
return(list("center" = c(cx, cy), "radius" = radius))
}
}
Note that p1-3 represents a vector containing an x- and y-coordinate. I have to trust the original Python code here but a quick check using desmos.com seems to indicate it works:
> define_circle(c(0,1), c(2,2), c(0.5,5))
$center
[1] 0.25 3.00
$radius
[1] 2.015564
Example circle plot
By leaving the function intact, you can calculate the inverse radius for any set of points you want. I agree that the inverse radius simply means 1/radius.
Here's a geometric approach. Suppose I have three random points in a data frame:
set.seed(1)
df <- setNames(as.data.frame(matrix(rnorm(6), nrow = 3)), c("x", "y"))
df
#> x y
#> 1 -0.6264538 1.5952808
#> 2 0.1836433 0.3295078
#> 3 -0.8356286 -0.8204684
plot(df$x, df$y, xlim = c(-3, 2), ylim = c(-2, 2))
Now, I can draw lines between these points and find the mid-point arithmetically:
lines(df$x, df$y)
mid_df <- data.frame(x = diff(df$x)/2 + df$x[-3],
y = diff(df$y)/2 + df$y[-3],
slope = -diff(df$x)/diff(df$y))
mid_df$intercept <- mid_df$y - mid_df$slope * mid_df$x
points(mid_df$x, mid_df$y)
If I draw lines perpendicular to these lines through the midpoint, then the resulting point should be equidistant from my three starting points:
abline(a = mid_df$intercept[1], b = mid_df$slope[1], col = "red", lty = 2)
abline(a = mid_df$intercept[2], b = mid_df$slope[2], col = "red", lty = 2)
center_x <- (mid_df$intercept[2] - mid_df$intercept[1]) /
(mid_df$slope[1] - mid_df$slope[2])
center_y <- mid_df$slope[1] * center_x + mid_df$intercept[1]
points(center_x, center_y)
As is indeed the case:
distances <- sqrt((center_x - df$x)^2 + (center_y - df$y)^2)
distances
#> [1] 1.136489 1.136489 1.136489
So, the radius of the circle is given by distances[1], and its center is at center_x, center_y. The curvature that is your end result is given by 1/distances[1]
To prove this, let's draw the circle this describes:
xvals <- seq(center_x - distances[1], center_x + distances[1], length.out = 100)
yvals <- center_y + sqrt(distances[1]^2 - (xvals - center_x)^2)
yvals <- c(yvals, center_y - sqrt(distances[1]^2 - (xvals - center_x)^2))
xvals <- c(xvals, rev(xvals))
lines(xvals, yvals)
My favorite resolution:
subtract the coordinates of one point from the two others;
now your circle is through the origin and has the simplified equation
2 Xc X + 2 Yc Y = X² + Y²
you have a standard and easy system of two equations in two unknowns.
X1 Xc + Y1 Yc = (X1² + Y1²) / 2 = Z1
X2 Xc + Y2 Yc = (X2² + Y2²) / 2 = Z2
when you have computed Xc and Yc, the radius is √Xc²+Yc².
Using complex numbers:
We map the points Z1, Z2 to -1 and 1 by the transformation Z = (2Z - Z1 - Z2) / (Z2 - Z1). Now the center of the circle is on the imaginary axis, let iH. We express that the center is equidistant to 1 and to the third point (2 Z3 - Z0 - Z1) / (Z1 - Z0) = X + iY,
H² + 1 = X² + (Y - H)²
or
H = (X² + Y² - 1) / 2Y
and
R = √H²+1.
Related
I am working on a python script and I am sure there is an easier way to approach this problem then my solution so far.
Give two coordinates, say (0,0) and (40,40) and a set distance to travel, say 5, how would I find a new coordinate pair for the point that is 5 units from (0,0) heading along the line that connects (0,0) and (40,40)?
I am doing the following given the distance_to_travel and points p0, p1:
ang = math.atan2(p1.y - p0.y, p1.x - p0.x)
xx = node0.x + (distance_to_travel * math.cos(ang))
yy = node0.y + (distance_to_travel * math.sin(ang))
Following the method outlined in my comment:
# find the vector a-b
ab.x, ab.y = p1.x - p0.x, p1.y - p0.y
# find the length of this vector
len_ab = (ab.x * ab.x + ab.y * ab.y) ** 0.5
# scale to length 5
ab5.x, ab5.y = ab.x *5 / len_ab, ab.y *5 / len_ab
# add to a (== p0)
fin.x, fin.y = p0.x + ab5.x, p0.y + ab5.y
You should find the slope of the line between a and b |ab|.
Then you can use 2D spherical (polar) coordinate system to get r mount of distance with a given slope (The slope you found earlier).
Now you can convert from spherical to Cartesian to find x, y coordinates of new point. Then you add this values to values of point a:
import math
def slope(p1, p2):
return math.atan2(
(p2[1] - p1[1]), (p2[0] - p1[0])
)
def spherical_to_cartesian(r, theta):
x = r * math.cos(theta)
y = r * math.sin(theta)
return x, y
def add_points(p1, p2):
return p1[0] + p2[0], p1[1] + p2[1]
if __name__ == '__main__':
a = [0, 0]
b = [40, 40]
slp = slope(a, b)
r = 5
new_p = spherical_to_cartesian(r, slp)
res = add_points(a, new_p)
print(res)
I would like to draw cycloid that is going on other cycloid but I don't know exactly how to do this. Here is my code.
import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib import animation
#r = float(input('write r\n'))
#R = float(input('write R\n'))
r = 1
R = 1
x = []
y = []
x2 = []
y2 = []
x3 = []
y3 = []
length=[0]
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'r', animated=True)
f = np.linspace(0, 10*r*math.pi, 1000)
def init():
ax.set_xlim(-r, 12*r*math.pi)
ax.set_ylim(-4*r, 4*r)
return ln,
def update2(frame):
#parametric equations of cycloid
x0 = r * (frame - math.sin(frame))
y0 = r * (1 - math.cos(frame))
x.append(x0)
y.append(y0)
#derivative of cycloid
dx = r * (1 - math.cos(frame))
dy = r * math.sin(frame)
#center of circle
a = dy * dy + dx * dx
b = (-2 * x0 * dy) - (2 * frame * dy * dy) + (2 * y0 * dx) - (2 * frame * dx * dx)
c = (x0 * x0) + (2 * frame * x0 * dy) + (frame * frame * dy * dy) + (y0 * y0) - (2 * frame * y0 * dx) + (frame * frame * dx * dx) -1
t1 = (-b - math.sqrt(b * b - 4 * a * c)) / (2 * a)
#t2 = (-b + math.sqrt(b * b - 4 * a * c)) / (2 * a)
center1x=(x0-dy*(t1-x0))*R
center1y=(y0+dx*(t1-x0))*R
#center2x=(x0-dy*(t2-x0))*R
#center2y=(y0+dx*(t2-x0))*R
#length of cycloid
length.append(math.sqrt(x0*x0 + y0*y0))
dl=sum(length)
param = dl / R
W1x = center1x + R * math.cos(-param)
W1y = center1y + R * math.sin(-param)
#W2x = center2x + R * math.cos(-param)
#W2y = center2y + R * math.sin(-param)
x2.append(W1x)
y2.append(W1y)
#x3.append(W2x)
#y3.append(W2y)
ln.set_data([x, x2], [y, y2])
return ln,
ani = animation.FuncAnimation(fig, update2, frames=f,init_func=init, blit=True, interval = 0.1, repeat = False)
plt.show()
In my function update2 I created parametric equations of first cycloid and then tried to obtain co-ordinates of points of second cycloid that should go on the first one.
My idea is based on that that typical cycloid is moving on straight line, and cycloid that is moving on other curve must moving on tangent of that curve, so center of circle that's creating this cycloid is always placed on normal of curve. From parametric equations of normal I have tried to obtain center of circle that creating cycloid but I think that isn't good way.
My goal is to get something like this:
Here is one way. Calculus gives us the formulas to find the direction angles at any point on the cycloid and the arc lengths along the cycloid. Analytic Geometry tells us how to use that information to find your desired points.
By the way, a plot made by rolling a figure along another figure is called a roulette. My code is fairly simple and could be optimized, but it works now, can be used for other problems, and is broken up to make the math and algorithm easier to understand. To understand my code, use this diagram. The cycloid is the blue curve, the black circles are the rolling circle on the cycloid, point A is an "anchor point" (a point where the rim point touches the cycloid--I wanted to make this code general), and point F is the moving rim point. The two red arcs are the same length, which is what we mean by rolling the circle along the cycloid.
And here is my code. Ask if you need help with the source of the various formulas, but the direction angles and arc lengths use calculus.
"""Numpy-compatible routines for a standard cycloid (one caused by a
circle of radius r above the y-axis rolling along the positive x-axis
starting from the origin).
"""
import numpy as np
def x(t, r):
"""Return the x-coordinate of a point on the cycloid with parameter t."""
return r * (t - np.sin(t))
def y(t, r):
"""Return the y-coordinate of a point on the cycloid with parameter t."""
return r * (1.0 - np.cos(t))
def dir_angle_norm_in(t, r):
"""Return the direction angle of the vector normal to the cycloid at
the point with parameter t that points into the cycloid."""
return -t / 2.0
def dir_angle_norm_out(t, r):
"""Return the direction angle of the vector normal to the cycloid at
the point with parameter t that points out of the cycloid."""
return np.pi - t / 2.0
def arclen(t, r):
"""Return the arc length of the cycloid between the origin and the
point on the cycloid with parameter t."""
return 4.0 * r * (1.0 - np.cos(t / 2.0))
# Roulette problem
def xy_roulette(t, r, T, R):
"""Return the x-y coordinates of a rim point on a circle of radius
R rolling on a cycloid of radius r starting at the anchor point
with parameter T currently at the point with parameter t. (Such a
rolling curve on another curve is called a roulette.)
"""
# Find the coordinates of the contact point P between circle and cycloid
px, py = x(t, r), y(t, r)
# Find the direction angle of PC from the contact point to circle's center
a1 = dir_angle_norm_out(t, r)
# Find the coordinates of the center C of the circle
cx, cy = px + R * np.cos(a1), py + R * np.sin(a1)
# Find cycloid's arc distance AP between anchor and current contact points
d = arclen(t, r) - arclen(T, r) # equals arc PF
# Find the angle φ the circle turned while rolling from the anchor pt
phi = d / R
# Find the direction angle of CF from circle's center to rim point
a2 = dir_angle_norm_in(t, r) - phi # subtract: circle rolls clockwise
# Find the coordinates of the final point F
fx, fy = cx + R * np.cos(a2), cy + R * np.sin(a2)
# Return those coordinates
return fx, fy
import matplotlib.pyplot as plt
r = 1
R = 0.75
T = np.pi / 3
t_array = np.linspace(0, 2*np.pi, 201)
cycloid_x = x(t_array, r)
cycloid_y = y(t_array, r)
roulette_x, roulette_y = xy_roulette(t_array, r, T, R)
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.axhline(y=0, color='k')
ax.axvline(x=0, color='k')
ax.plot(cycloid_x, cycloid_y)
ax.plot(roulette_x, roulette_y)
plt.show()
And here is the resulting graphic. You can pretty this up as you choose. Note that this only has the circle rolling along one arch of the cycloid. If you clarify what should happen at the cusps, this could be extended.
Or, if you want a smaller circle and a curve that ends at the cusps (here r = 1, T = 0 n = 6 (the number of little arches), and R = 4 * r / np.pi / n),
You can generate coordinates of the center of rolling circle using parallel curve definition. Parametric equations for this center are rather simple (if I did not make mistakes):
for big cycloid:
X = R(t - sin(t))
Y = R(1 - cos(t))
X' = R(1 - cos(t))
Y' = R*sin(t)
parallel curve (center of small circle):
Sqrt(X'^2+Y'^2)=R*Sqrt(1-2*cos(t)+cos^2(t)+sin^2(t)) =
R*Sqrt(2-2*cos(t))=
R*Sqrt(4*sin^2(t/2))=
2*R*sin(t/2)
x(t) = X(t) + r*R*sin(t)/(2R*sin(t/2)) =
R(t - sin(t)) + r*2*sin(t/2)*cos(t/2) / (2*sin(t/2)) =
R(t - sin(t)) + r*cos(t/2)
y(t) = Y(t) - r*R*(1-cos(t))/(2*R*sin(t/2)) =
R(1 - cos(t)) - r*(2*sin^2(t/2)/(2*sin(t/2)) =
R(1 - cos(t)) - r*sin(t/2)
But trajectory of point on the circumference is superposition of center position and rotation around it with angular velocity that depends on length of main cycloid plus rotation of main tangent.
Added from dicussion in comments:
cycloid arc length
L(t) = 4R*(1-cos(t/2))
to use it for small circle rotation, divide by r
tangent rotation derivation
fi(t) = atan(Y'/X') = atan(sin(t)/(1-cos(t)) =
atan(2*sin(t/2)*cos(t/2)/(2(sin^2(t/2))) =
atan(ctg(t/2)) = Pi/2 - t/2
so tangent direction change is proportional to big cycloid parameter
and final result is (perhaps some signs are not correct)
theta(t) = L(t)/r + t/2 + Phase
ox(t) = x(t) + r * cos(theta(t))
oy(t) = y(t) + r * sin(theta(t))
Thanks everyone. Somehow I have managed to accomplish that. Solution is maybe ugly but sufficient for me.
import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib import animation
r = float(input('write r\n'))
R = float(input('write R\n'))
#r=1
#R=0.1
x = []
y = []
x2 = []
y2 = []
x_1=0
x_2=0
lengthX=[0]
lengthY=[0]
lengthabs=[0]
fig, ax = plt.subplots()
ln, = plt.plot([], [], 'r', animated=True)
f = np.linspace(0, 2*math.pi, 1000)
def init():
ax.set_xlim(-r, 4*r*math.pi)
ax.set_ylim(0, 4*r)
return ln,
def update2(frame):
#cycloid's equations
x0 = r * (frame - math.sin(frame))
y0 = r * (1 - math.cos(frame))
x.append(r * (frame - math.sin(frame)))
y.append(r * (1 - math.cos(frame)))
#arc's length
lengthabs.append(math.sqrt((x0-lengthX[-1])*(x0-lengthX[-1])+(y0-lengthY[-1])*(y0-lengthY[-1])))
lengthX.append(x0)
lengthY.append(y0)
dl=sum(lengthabs)
param = dl / R
#center of circle
center1x = r * (frame - math.sin(frame)) + R * math.cos((frame+2*math.pi) / 2)
center1y = r * (1 - math.cos(frame)) - R * math.sin((frame+2*math.pi) / 2)
if(frame<2*math.pi):
W1x = center1x + R * math.cos(-param)
W1y = center1y + R * math.sin(-param)
else:
W1x = center1x + R * math.cos(param)
W1y = center1y + R * math.sin(param)
x2.append(W1x)
y2.append(W1y)
ln.set_data([x,x2], [y,y2])
return ln,
ani = animation.FuncAnimation(fig, update2, frames=f,init_func=init, blit=True, interval = 0.1, repeat = False)
plt.show()
This question already has answers here:
Finding the smallest circle that encompasses other circles?
(8 answers)
Closed 6 years ago.
I'm trying to find an algorithm that computes the coordinates of the center and the radius of the smallest circle that covers two smaller circles. I know the radius and x, y coordinates of the center of the 2 smaller circles.
I tried this algorithm below, it works even when the two circles overlap or far apart, but that's only if circle1 and circle2 are on the same x or y axis.
Let c1, c2 be circle1 and circle 2;
r be radius:
if c1x >= c2x:
if c1y >= c2y:
c = ((c1x + c1r) + (c2x - c2r))/2, ((c1y + c1r) + (c2y - c2r))/2
r = max(((c1x + c1r) - (c2x - c2r))/2,
((c1y + c1r) - (c2y - c2r))/2)
elif c1y < c2y:
c = ((c1x + c1r) + (c2x - c2r))/2, ((c2y + c2r) + (c1y - c1r))/2
r = max(((c1x + c1r) - (c2x - c2r))/2,
((c2y + c2r) - (c1y - c1r))/2)
elif if c1x < c2x:
if c1y >= c2y:
c = ((c2x + c2r) + (c1x - c1r))/2, ((c1y + c1r) + (c2y - c2r))/2
r = max(((c2x + c2r) - (c1x - c1r))/2,
((c1y + c1r) - (c2y - c2r))/2)
elif c1y < c2y:
c = ((c2x + c2r) + (c1x - c1r))/2, ((c2y + c2r) + (c1y - c1r))/2
r = max(((c2x + c2r) - (c1x - c1r))/2,
((c2y + c2r) - (c1y - c1r))/2)
Covering circle parameters (pseudocode):
dx = c2x - c1x
dy = c2y - c1y
//center-center distance
dc = Sqrt(dx**2 + dy**2)
rmin = Min(r1, r2)
rmax = Max(r1, r2)
if rmin + dc < rmax then
covercenter = center of larger circle
R = rmax
else
R = 0.5 * (r1 + r2 + dc)
x = c1x + (R - r1) * dx / dc
y = c1y + (R - r1) * dy / dc
work examples (blue is covering):
If your algo works when both centers have same x or same y, then you could replace (x1, y1) and (x2, y2) by (0, 0) and (sqrt((x2-x1)**2+(y2-y1)**2), 0) (just applying the appropriate translation and rotation on the plane).
You should follow the DRY principle (Don't repeat yourself) in your code and don't duplicate your formulas multiple times. You're going to make copy-paste errors or forget to modify one of the copies.
I'm grouping all those variations of the formula together by sorting the small circles so that I can directly access e.g. the leftmost circle's x (cax) and its radius (caxr) or the lowest circle's y (cby) and its radius (cbyr).
from math import sqrt
(cax, caxr), (cbx, cbxr) = sorted([(c1x, c1r), (c2x, c2r)], key=lambda t:t[0])
(cay, cayr), (cby, cbyr) = sorted([(c1y, c1r), (c2y, c2r)], key=lambda t:t[0])
x = (cax-caxr + cbx+cbxr) / 2
y = (cay-cayr + cby+cbyr) / 2
r = sqrt( ((cax-caxr) - (cbx+cbxr))**2 + ((cay-cayr) + (cby+cbyr))**2 ) / 2
See this code running on IDEone.com
I've been trying to rotate a bunch of lines by 90 degrees (that together form a polyline). Each line contains two vertices, say (x1, y1) and (x2, y2). What I'm currently trying to do is rotate around the center point of the line, given center points |x1 - x2| and |y1 - y2|. For some reason (I'm not very mathematically savvy) I can't get the lines to rotate correctly.
Could someone verify that the math here is correct? I'm thinking that it could be correct, however, when I set the line's vertices to the new rotated vertices, the next line may not be grabbing the new (x2, y2) vertex from the previous line, causing the lines to rotate incorrectly.
Here's what I've written:
def rotate_lines(self, deg=-90):
# Convert from degrees to radians
theta = math.radians(deg)
for pl in self.polylines:
self.curr_pl = pl
for line in pl.lines:
# Get the vertices of the line
# (px, py) = first vertex
# (ox, oy) = second vertex
px, ox = line.get_xdata()
py, oy = line.get_ydata()
# Get the center of the line
cx = math.fabs(px-ox)
cy = math.fabs(py-oy)
# Rotate line around center point
p1x = cx - ((px-cx) * math.cos(theta)) - ((py-cy) * math.sin(theta))
p1y = cy - ((px-cx) * math.sin(theta)) + ((py-cy) * math.cos(theta))
p2x = cx - ((ox-cx) * math.cos(theta)) - ((oy-cy) * math.sin(theta))
p2y = cy - ((ox-cx) * math.sin(theta)) + ((oy-cy) * math.cos(theta))
self.curr_pl.set_line(line, [p1x, p2x], [p1y, p2y])
The coordinates of the center point (cx,cy) of a line segment between points (x1,y1) and (x2,y2) are:
cx = (x1 + x2) / 2
cy = (y1 + y2) / 2
In other words it's just the average, or arithmetic mean, of the two pairs of x and y coordinate values.
For a multi-segmented line, or polyline, its logical center point's x and y coordinates are just the corresponding average of x and y values of all the points. An average is just the sum of the values divided by the number of them.
The general formulas to rotate a 2D point (x,y) θ radians around the origin (0,0) are:
x′ = x * cos(θ) - y * sin(θ)
y′ = x * sin(θ) + y * cos(θ)
To perform a rotation about a different center (cx, cy), the x and y values of the point need to be adjusted by first subtracting the coordinate of the desired center of rotation from the point's coordinate, which has the effect of moving (known in geometry as translating) it is expressed mathematically like this:
tx = x - cx
ty = y - cy
then rotating this intermediate point by the angle desired, and finally adding the x and y values of the point of rotation back to the x and y of each coordinate. In geometric terms, it's the following sequence of operations: Tʀᴀɴsʟᴀᴛᴇ ─► Rᴏᴛᴀᴛᴇ ─► Uɴᴛʀᴀɴsʟᴀᴛᴇ.
This concept can be extended to allow rotating a whole polyline about any arbitrary point—such as its own logical center—by just applying the math described to each point of each line segment within it.
To simplify implementation of this computation, the numerical result of all three sets of calculations can be combined and expressed with a pair of mathematical formulas which perform them all simultaneously. So a new point (x′,y′) can be obtained by rotating an existing point (x,y), θ radians around the point (cx, cy) by using:
x′ = ( (x - cx) * cos(θ) + (y - cy) * sin(θ) ) + cx
y′ = ( -(x - cx) * sin(θ) + (y - cy) * cos(θ) ) + cy
Incorporating this mathematical/geometrical concept into your function produces the following:
from math import sin, cos, radians
def rotate_lines(self, deg=-90):
""" Rotate self.polylines the given angle about their centers. """
theta = radians(deg) # Convert angle from degrees to radians
cosang, sinang = cos(theta), sin(theta)
for pl in self.polylines:
# Find logical center (avg x and avg y) of entire polyline
n = len(pl.lines)*2 # Total number of points in polyline
cx = sum(sum(line.get_xdata()) for line in pl.lines) / n
cy = sum(sum(line.get_ydata()) for line in pl.lines) / n
for line in pl.lines:
# Retrieve vertices of the line
x1, x2 = line.get_xdata()
y1, y2 = line.get_ydata()
# Rotate each around whole polyline's center point
tx1, ty1 = x1-cx, y1-cy
p1x = ( tx1*cosang + ty1*sinang) + cx
p1y = (-tx1*sinang + ty1*cosang) + cy
tx2, ty2 = x2-cx, y2-cy
p2x = ( tx2*cosang + ty2*sinang) + cx
p2y = (-tx2*sinang + ty2*cosang) + cy
# Replace vertices with updated values
pl.set_line(line, [p1x, p2x], [p1y, p2y])
Your center point is going to be:
centerX = (x2 - x1) / 2 + x1
centerY = (y2 - y1) / 2 + y1
because you take half the length (x2 - x1) / 2 and add it to where your line starts to get to the middle.
As an exercise, take two lines:
line1 = (0, 0) -> (5, 5)
then: |x1 - x2| = 5, when the center x value is at 2.5.
line2 = (2, 2) -> (7, 7)
then: |x1 - x2| = 5, which can't be right because that's the center for
the line that's parallel to it but shifted downwards and to the left
Hey trying to learn how to code and I cant figure this exercise out.
Specifically getting the precise y axis intercept points.
The formula given works for getting the x axis points but I cant figure out how to get the y axis points.
Exercise :
Input : Radius of circle and the y - intercept of the line.
Output : Circle drawn with a horizontal line across the window with the given y intercept. Mark two points of the intersection.
Print the x values of the points of intersection *Formula : x = ± √r^2 - y^2
Code::
from graphics import *
from math import *
def main():
# enter radius and the y intercept of the line
radius = eval(input("Put in radius:: "))
yinter = eval(input("Put in y intersec:: "))
#Draw window + circle + line
win = GraphWin()
win.setCoords(-10.0, -10.0, 10.0, 10.0)
circle = Circle(Point(0.0,0.0), radius)
mcircle = Circle(Point(0.0,0.0), 0.5)
circle.draw(win)
mcircle.draw(win)
line = Line(Point(-10, 0), Point(10, yinter))
line.draw(win)
#Calculate x axis points of intersept
xroot1 = sqrt(radius * radius - yinter * yinter)
xroot2 = -abs(xroot1)
print("Xroot 1 : ", xroot1)
print("Xroot 2 : ", xroot2)
x = 0
yroot1 = sqrt(radius * radius - x * x)
yroot2 = -abs(yroot1)
print("Yroot 1 : ", yroot1)
print("Yroot 2 : ", yroot2)
#mark two points of intersept in red
sc1 = Circle(Point(xroot1, yroot1), 0.3)
sc1.setFill('red')
sc2 = Circle(Point(xroot2, yroot2), 0.3)
sc2.setFill('red')
sc1.draw(win)
sc2.draw(win)
main()
Answer - With Radius of 8 and Y intersect point of 2
Yroot1 = 7.75
Yroot2 = -7.75
Xroot1 = 8.0
Xroot2 = -8.0
I just came up with a subroutine to find intersection points while solving another Zelle-graphics related SO question. There may be ways to simplify the math but I'm going the long way around:
from graphics import *
def intersection(center, radius, p1, p2):
""" find the two points where a secant intersects a circle """
dx, dy = p2.x - p1.x, p2.y - p1.y
a = dx**2 + dy**2
b = 2 * (dx * (p1.x - center.x) + dy * (p1.y - center.y))
c = (p1.x - center.x)**2 + (p1.y - center.y)**2 - radius**2
discriminant = b**2 - 4 * a * c
assert (discriminant > 0), 'Not a secant!'
t1 = (-b + discriminant**0.5) / (2 * a)
t2 = (-b - discriminant**0.5) / (2 * a)
return Point(dx * t1 + p1.x, dy * t1 + p1.y), Point(dx * t2 + p1.x, dy * t2 + p1.y)
def main(win):
center = Point(0.0, 0.0)
# Enter radius
radius = float(input("Put in radius: "))
# Draw circle and center dot
Circle(center, radius).draw(win)
Circle(center, 0.3).draw(win)
# Enter the y intercept of the line
yinter = float(input("Put in y intercept: "))
# Draw line
p1, p2 = Point(-10.0, 0.0), Point(10.0, yinter)
Line(p1, p2).draw(win)
# Mark two points of intercept in red
for i, root in enumerate(intersection(center, radius, p1, p2), start=1):
print("Root {}:".format(i), root)
dot = Circle(root, 0.3)
dot.setFill('red')
dot.draw(win)
win = GraphWin()
win.setCoords(-10.0, -10.0, 10.0, 10.0)
main(win)
win.getMouse()
OUTPUT
NOTE
You can get values input that do not produce a secant, e.g. a radius of 2 and an intercept of 8. Your code doesn't account for this -- the above will simply throw an assertion error if it occurs. But you can upgrade that to an error you can catch and fix.
For the y coordinates you can use a similar formula:
y = ± sqrt(r^2 - x^2)
and the do everything the same with marking the roots.
You should write the code like this:
x = sqrt(r ** 2 - y ** 2)
line = Line(Point(-10, 0), Point(10, yinter))
line.draw(win)
Line(Point(-10,0) is wrong, it should read:
Line(Point(-10,yinter).
Also set Xroot1 and Xroot2 = 0 or -x=0, x=0
def intersection(center, radius, p1, p2):
dx, dy = p2[0] - p1[0], p2[1] - p1[1]
a = dx**2 + dy**2
b = 2 * (dx * (p1[0]- center[0]) + dy * (p1[1] - center[1]))
c = (p1[0] - center[0])**2 + (p1[1] - center[1])**2 - radius**2
K = b**2 - 4 * a * c
return True if K>0 else False
if __name__ == "__main__":
p1 = 10,50
p2 = 100,50
center= 50,150
radius = 600
print(intersection(center, radius, p1, p2))