homework help? for making a spirograph - python

SO over break week our teacher gave us a little project to do requiring a Spirograph, here is the code he helped us write before
from graphics import *
from math import *
def ar(a):
return a*3.141592654/180
def main():
x0 = 100
y0 = 100
startangle = 60
stepangle = 120
radius = 50
win = GraphWin()
p1 = Point(x0 + radius * cos(ar(startangle)), y0 + radius * sin(ar(startangle)))
for i in range(stepangle+startangle,360+stepangle+startangle,stepangle):
p2 = Point(x0 + radius * cos(ar(i)), y0 + radius * sin(ar(i)))
Line(p1,p2).draw(win)
p1 = p2
input("<ENTER> to quit...")
win.close()
main()
he then wants us to develop the program that consecutively draws 12 equilateral triangles (rotating the triangle each time by 30 degrees through a full 360 circle). This can be achieved by “stepping” the STARTANGLE parameter. My question I am stuck on where to go from here, what does he mean by "stepping?" I assume making some sort of loop, is it possible someone can give me a push in the right step?

This is a solution using matplotlib. The general procedure would be the same. But you will have to modify it for using the libraries you're allowed to use.
from math import radians, sin, cos
import matplotlib.pyplot as plt
startAngle = 0
stepAngle = 30
origin = (0,0)
points = []
points.append(origin)
points.append((cos(radians(startAngle)), sin(radians(startAngle))))
for i in range(startAngle + stepAngle, 360 + stepAngle, stepAngle):
x = cos(radians(i))
y = sin(radians(i))
points.append((x,y))
points.append(origin)
points.append((x,y))
x,y = zip(*points) #separate the tupples into x and y coordinates.
plt.plot(x,y) #plots the points, drawing lines between each point
plt.show()
plt.plot draw lines between each point in the list. We're adding inn the origin points so we get triangles instead of just a polygon around the center.

Related

python move circle around another circle in graphics.py

I am writing a program in Python using graphics.py library. I want to draw two circles, and then in loop move one of them around another one. I know I have to use sin and cos function, but I have no idea what is a mathematical formula for that.
That's my code:
from graphics import *
from math import sin, cos, pi
from time import sleep
win = GraphWin('Program', 500, 500)
win.setBackground('white')
c = Circle(Point(250, 250), 50)
c.draw(win)
c1 = Circle(Point(250, 175), 25)
c1.draw(win)
while True:
c1.move() #there I have to use some formula for moving circle c1 around circle c
sleep(1)
win.getMouse()
win.close()
A bit of mathematics have to be used. Based on your example, you want the circles to be adjacent to each other.
Because of that, distance between their centres will always be r1+r2. This is a length of our vector. We need to split that vector into x axis and y axis parts. This is where sine and cosine functions come in, value it of it at a given angle will mean how far along that axis we have to move our center.
After calculating new position all we have to do is subtract that from current position to see how far we need to move our other circle.
from graphics import *
from math import sin, cos, pi
from time import sleep
win = GraphWin('Program', 500, 500)
win.setBackground('white')
c_origin_x = 250
c_origin_y = 250
c_radius = 50
c = Circle(Point(c_origin_x, c_origin_y), c_radius)
c.draw(win)
c1_oldpos_x = c_origin_x # doesn't actually matter, it gets moved immediately
c1_oldpos_y = c_origin_y
c1_radius = 25
c1 = Circle(Point(c1_oldpos_x, c1_oldpos_y), c1_radius)
c1.draw(win)
angle = 0 # initial angle
while True:
c1_newpos_x = sin(angle) * (c_radius + c1_radius) + c_origin_x
c1_newpos_y = cos(angle) * (c_radius + c1_radius) + c_origin_y
c1.move(c1_newpos_x - c1_oldpos_x, c1_newpos_y - c1_oldpos_y)
sleep(1)
angle += 0.1 * pi # this is what will make a position different in each iteration
c1_oldpos_x = c1_newpos_x
c1_oldpos_y = c1_newpos_y
win.getMouse()
win.close()

plotting n number of equal points in circular direction in python

I am working on the task in which I have to make a circle which is having n number of equal parts. I am provided with centre and radius of the circle which is (0,0) and 4 respectively. To achieve this task, I have written below code,
parts = 36 # desire number of parts of the circle
theta_zero = 360/parts
R = 4 # Radius
x_p = []
y_p = []
n = 0
for n in range(0,36):
x_p.append(R * math.cos(n*theta_zero))
y_p.append(R * math.sin(n*theta_zero))
However, after running this code, I got output like below which does not seem a coorect which I am suppose to have.
Kindly let me know what I am doing wrong and give some suggestion of correct code. Thank you
Aside from the fact that you are generating numbers in degrees and passing them to a function that expects radians, there's a much simpler and less error-prone method for generating evenly-spaced coordinates:
t0 = np.linspace(0, 2 * np.pi, parts, endpoint=False)
x0 = R * np.cos(t0)
y0 = R * np.sin(t0)
endpoint=False ensures that you end up with 36 partitions rather than 35, since otherwise 0 and 2 * np.pi would overlap.
If you wanted to connect the dots for your circle, you would want the overlap. In that case, you would do
t1 = np.linspace(0, 2 * np.pi, parts + 1)
x1 = R * np.cos(t1)
y1 = R * np.sin(t1)
Here is how you would plot a circle with the 36 sectors delineated:
plt.plot(x1, y1)
plt.plot(np.stack((np.zeros(parts), x0), 0),
np.stack((np.zeros(parts), y0), 0))
Finally, if you want your circle to look like a circle, you may want to run plt.axis('equal') after plotting.
Your circle is weird because math.cos and math.sin accept radians while you are passing degrees. You just need to convert the degrees to radians when calling the math functions.
Like this:
parts = 36
theta_zero = 360/parts
R = 4
x_p = []
y_p = []
for n in range(0,36):
x_p.append(R * math.cos(n*theta_zero /180*math.pi))
y_p.append(R * math.sin(n*theta_zero /180*math.pi))
Result:
Alternatively changing theta_zero to 2*math.pi/parts would also work and would be slightly faster but it might be a little less intuitive to work with.
Also as #Mad Physicist mentioned you should probably add plt.axis('equal') to unstretch the image.

How do I draw this shape in Turtle? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
The community reviewed whether to reopen this question 8 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I got a challenge in a learning group today to draw this shape in python turtle library.
I cannot figure out a way to express the geometrical solution to find the angles to turn and the size of the line I need.
Can you please tell me how to draw the first polygon alone? I already know how to make the pattern.
I am in fifth grade. So please give me a solution that I can understand.
Here is the solution I came up with. It is based on this diagram:
Math background
My solution uses "trigonometry", which is a method for calculating the length of one side of a triangle from the length of another side and the angles of the triangle. This is advanced math which I would expect to be taught maybe in 9th or 10th grade. I do not expect someone in 5th grade to know trigonometry. Also I cannot explain every detail of trigonometry, because I would have to write a lot and I do not think I have the teaching skills to make it clear. I would recommend you to look at for example this video to learn about the method:
https://www.youtube.com/watch?v=5tp74g4N8EY
You could also ask your teacher for more information, or research about it on the internet on your own.
Step 1: Calculating the angles
We can do this without trigonometry.
First, we see there is a "pentagon" (5-sided polygon) in the middle. I want to know the inner angle of a corner in this "pentagon". I call this angle X:
How can we calculate the angle X? We first remember that the sum of the inner angles in a triangle is 180°. We see that we can divide a 5-sides polygon into 5-2 triangles like this:
The sum of the inner angle of each of these 5-2 triangles is 180°. So for the whole 5-sided polygon, the sum of the inner angles is 180° * (5-2). Since all angles have the same size, each angle is 180°*(5-2) / 5 = 108°. So we have X = 108°.
The angle on the other side is the same as X. This allows us the calculate the angle between the two X. I will call this angle Y:
Since a full circle is 360°, we know that 360° = 2*X + 2*Y. Therefore, Y = (360° - 2*X) / 2. We know that X = 108°, so we get Y = 72°.
Next, we see there is a triangle containing the Y angle. I want to know the angle Z at the other corner of the triangle:
The inner angles of a triangle sum up to 180°*(3-2) = 180°. Therefore, we know that 180° = 2*Y + Z, so Z = 180° - 2*Y. We know that Y = 72°, so we get Z = 36°.
We will use the angle Z a lot. You can see that every corner of the green star has angle Z. The blue star is the same as the green star except it is rotated, so all blue corners also have angle Z. The corners of the red star are twice as wide as the corners of the green and blue stars, so the corners of the red star have the angle 2*Z.
Step 2: Calculating the lengths
First, we observe that all outer corners are on a circle. We call the radius of this circle R. We do not have to calculate R. Instead, we can take any value we want for R. We will always get the same shape but in different sizes. We could call R a "parameter" of the shape.
Given some value for R, I want to know the following lengths:
Calculating A:
We start with A. We can see the following triangle:
The long side of the triangle is our radius R. The other side has length A/2 and we do not care about the third side. The angle in the right-most corner is Z/2 (with Z = 36° being the angle we calculated in the previous section). The angle S is a right-angle, so S = 90°. We can calculate the third angle T because we know that the inner angles of a triangle sum up to 180°. Therefore, 180° = S + Z/2 + T. Solving for T, we get T = 180° - S - Z/2 = 180° - 90° - 36°/2 = 72°.
Next, we use trigonometry to calculate A/2. Trigonometry teaches us that A/2 = R * sin(T). Putting in the formula for T, we get A/2 = R * sin(72°). Solving for A, we get A = 2*R*sin(72°).
If you pick some value for R, for example R = 100, you can now calculate A with this formula. You would need a calculator for sin(72°), because it would be extremely difficult to calculate this in your head. Putting sin(72) into my calculator gives me 0.951056516. So for our choice R = 100, we know that A = 2 * R * sin(72°) = 2 * 100 * 0.951056516 = 190.211303259.
Calculating B:
We use the same technique to find a formula for B. We see the following triangle:
So the bottom side is the length of our radius R. The right side is B/2. We do not care about the third side. The right-most angle is three times Z/2. The angle S is a right-angle, so we have S = 90°. We can calculate the remaining angle T with 180° = S + T + 3*Z/2. Solving for T, we get T = 180° - S - 3*Z/2 = 180° - 90° - 3*36°/2 = 36°. Ok so T = Z, we could have also seen this from the picture, but now we have calculated it anyways.
Using trigonometry, we know that B/2 = R * sin(T), so we get the formula B = 2 * R * sin(36°) to calculate B for some choice of R.
Calculating C:
We see the following triangle:
So the bottom side has length A/2 and the top side has length B. We already have formulas for both of these sides. The third side is C, for which we want to find a formula. The right-most angle is Z. The angle S is a right-angle, so S = 90°. The top-most angle is three times Z/2.
Using trigonometry, we get C = sin(Z) * B.
Calculating D:
We see the following triangle:
We already have a formula for C. We want to find a formula for D. The top-most angle is Z/2 (I could not fit the text into the triangle). The bottom-left angle S is a right-angle.
Using trigonometry, we know that D = tan(Z/2) * C. The tan function is similar to the sin from the previous formulas. You can again put it into your calculator to compute the value, so for Z = 36°, I can put tan(36/2) into my calculator and it gives me 0.324919696.
Calculating E:
Ok this is easy, E = 2*D.
Halfway done already!
Calculating F:
This is similar to A and B:
We want to find a formula for F. The top side has length F/2. The bottom side has the length of our radius R. The right-most corner has angle Z. S is a right-angle. We can calculate T = 180° - S - Z = 180° - 90° - Z = 90° - Z.
Using trigonometry, we get F/2 = R * sin(T). Putting in the formula for T gives us F/2 = R*sin(90° - Z). Solving for F gives us F = 2*R*sin(90°-Z).
Calculating G:
We see the following triangle:
The top side has length F, we already know a formula for it. The right side has length G, we want to find a formula for it. We do not care about the bottom side. The left-most corner has angle Z/2. The right-most corner has angle 2*Z. The bottom corner has angle S, which is a right-angle, so S = 90°. It was not immediately obvious to me that the red line and the green line are perfectly perpendicular to each other so that S really is a right-angle, but you can verify this by using the formula for the inner angles of a triangle, which gives you 180° = Z/2 + 2*Z + S. Solving for S gives us S = 180° - Z/2 - 2*Z. Using Z = 36°, we get S = 180° - 36°/2 - 2* 36° = 90°.
Using trigonometry, we get G = F * sin(Z/2).
Calculating H:
We see the following triangle:
The right side has length G, we already have formula for that. The bottom side has length H, we want to find a formula for that. We do not care about the third side. The top corner has angle Z, the bottom-right corner has angle S. We already know that S is a right-angle from the last section.
Using trigonometry, we get H = G * tan(Z).
Calculating I:
This is easy, I is on the same line as A. We can see that A can be divided into A = I + H + E + H + I. We can simplify this to A = 2*I + 2*H + E. Solving for I gives us I = (A - 2*H - E)/2.
Calculating J:
Again this is easy, J is on the same line as F. We can see that F can be divided into F = G + J + G. We can simplify that to F = 2*G + J. Solving for J gives us J = F - 2*G.
Writing the Python program
We now have formulas for all the lines we were interested in! We can now put these into a Python program to draw the picture.
Python gives you helper functions for computing sin and tan. They are contained in the math module. So you would add import math to the top of your program, and then you can use math.sin(...) and math.tan(...) in your program. However, there is one problem: These Python functions do not use degrees to measure angles. Instead they use a different unit called "radians". Fortunately, it is easy to convert between degrees and radians: In degrees a full circle is 360°. In radians, a full circle is 2*pi, where pi is a special constant that is approximately 3.14159265359.... Therefore, we can convert an angle that is measured in degrees into an angle that is measured in radians, by dividing the angle by 360° and then multiplying it by 2*pi. We can write the following helper functions in Python:
import math
def degree_to_radians(angle_in_degrees):
full_circle_in_degrees = 360
full_circle_in_radians = 2 * math.pi
angle_in_radians = angle_in_degrees / full_circle_in_degrees * full_circle_in_radians
return angle_in_radians
def sin_from_degrees(angle_in_degrees):
angle_in_radians = degree_to_radians(angle_in_degrees)
return math.sin(angle_in_radians)
def tan_from_degrees(angle_in_degrees):
angle_in_radians = degree_to_radians(angle_in_degrees)
return math.tan(angle_in_radians)
We can now use our functions sin_from_degrees and tan_from_degrees to compute sin and tan from angles measured in degrees.
Putting it all together:
from turtle import *
import math
# Functions to calculate sin and tan ###########################################
def degree_to_radians(angle_in_degrees):
full_circle_in_degrees = 360
full_circle_in_radians = 2 * math.pi
angle_in_radians = angle_in_degrees / full_circle_in_degrees * full_circle_in_radians
return angle_in_radians
def sin_from_degrees(angle_in_degrees):
angle_in_radians = degree_to_radians(angle_in_degrees)
return math.sin(angle_in_radians)
def tan_from_degrees(angle_in_degrees):
angle_in_radians = degree_to_radians(angle_in_degrees)
return math.tan(angle_in_radians)
# Functions to calculate the angles ############################################
def get_X():
num_corners = 5
return (num_corners-2)*180 / num_corners
def get_Y():
return (360 - 2*get_X()) / 2
def get_Z():
return 180 - 2*get_Y()
# Functions to calculate the lengths ###########################################
def get_A(radius):
Z = get_Z()
return 2 * radius * sin_from_degrees(90 - Z/2)
def get_B(radius):
Z = get_Z()
return 2 * radius * sin_from_degrees(90 - 3*Z/2)
def get_C(radius):
Z = get_Z()
return sin_from_degrees(Z) * get_B(radius)
def get_D(radius):
Z = get_Z()
return tan_from_degrees(Z/2) * get_C(radius)
def get_E(radius):
return 2 * get_D(radius)
def get_F(radius):
Z = get_Z()
return 2 * radius * sin_from_degrees(90 - Z)
def get_G(radius):
Z = get_Z()
return get_F(radius) * sin_from_degrees(Z/2)
def get_H(radius):
Z = get_Z()
return get_G(radius) * tan_from_degrees(Z)
def get_I(radius):
A = get_A(radius)
E = get_E(radius)
H = get_H(radius)
return (A - E - 2*H) / 2
def get_J(radius):
F = get_F(radius)
G = get_G(radius)
return F - 2*G
# Functions to draw the stars ##################################################
def back_to_center():
penup()
goto(0, 0)
setheading(0)
pendown()
def draw_small_star(radius):
penup()
forward(radius)
pendown()
Z = get_Z()
left(180)
right(Z/2)
E = get_E(radius)
H = get_H(radius)
I = get_I(radius)
for i in range(0,5):
penup()
forward(I)
pendown()
forward(H)
penup()
forward(E)
pendown()
forward(H)
penup()
forward(I)
left(180)
right(Z)
back_to_center()
def draw_green_star(radius):
pencolor('green')
draw_small_star(radius)
def draw_blue_star(radius):
pencolor('blue')
Z = get_Z()
left(Z)
draw_small_star(radius)
def draw_red_star(radius):
pencolor('red')
Z = get_Z()
penup()
forward(radius)
pendown()
left(180)
right(Z)
G = get_G(radius)
J = get_J(radius)
for i in range(0,10):
pendown()
forward(G)
penup()
forward(J)
pendown()
forward(G)
left(180)
right(2*Z)
back_to_center()
def draw_shape(radius):
draw_green_star(radius)
draw_blue_star(radius)
draw_red_star(radius)
radius = 400
draw_shape(radius)
done()
Output:
Here's a different solution. It's based on a kite polygon where the upper portion is a pair of 3-4-5 right triangles and the lower portion is a pair of 8-15-17 right triangles:
from turtle import Screen, Turtle
KITES = 10
RADIUS = 100
def kite(t):
t.right(37)
t.forward(100)
t.right(81)
t.forward(170)
t.right(124)
t.forward(170)
t.right(81)
t.forward(100)
t.right(37)
turtle = Turtle()
turtle.penup()
turtle.sety(-RADIUS)
for _ in range(KITES):
turtle.circle(RADIUS, extent=180/KITES)
turtle.pendown()
kite(turtle)
turtle.penup()
turtle.circle(RADIUS, extent=180/KITES)
turtle.hideturtle()
screen = Screen()
screen.exitonclick()
(Yes, I'm obsessed with this puzzle.) I was sufficiently impressed by the brevity of #AnnZen's solution, I decided to see if I could come up with an even shorter one. The only unique structure in this polygon is the side of the kite:
So the problem becomes drawing ten of these in a circular fashion, and then reversing the code to draw them again in the opposite direction:
from turtle import *
for _ in range(2):
for _ in range(10):
fd(105)
lt(90)
fd(76.5)
pu()
bk(153)
rt(54)
pd()
lt(72)
lt, rt = rt, lt
done()
My Short code:
from turtle import *
for f, t in [(0,-72),(71,108),(71,0)]*10+[(29,90),(73,72),(73,90),(29,72)]*10:fd(f),rt(t)
Here's the solution:
import turtle
#turtle.tracer(0)
a = turtle.Turtle()
for _ in range(10):
a.forward(100)
a.right(90)
a.forward(73)
a.right(72)
a.forward(73)
a.backward(73)
a.right(108)
a.forward(73)
a.right(90)
a.penup()
a.forward(100)
a.pendown()
a.forward(100)
a.right(108)
#turtle.update()
Let's look at a yet another approach to drawing this shape. We'll start with the same diagram as #f9c69e9781fa194211448473495534
Using a ruler (1" = 100px) and protractor on the OP's original image, we can approximate this diagram with very simple code:
from turtle import Screen, Turtle
turtle = Turtle()
turtle.hideturtle()
turtle.penup() # center on the screen
turtle.setposition(-170, -125)
turtle.pendown()
for _ in range(10):
turtle.forward(340)
turtle.left(126)
turtle.forward(400)
turtle.left(126)
screen = Screen()
screen.exitonclick()
This is equivalent to drawing with a pencil and not lifting it nor overdrawing (backing up) on any line.
To make the shape we want pop out, we divide the two lines that we draw above into segments that are thinner and thicker. The first line breaks into three segments and the second into five:
To do this, we simply break the forward() calls in our loop into wide and narrow segments:
for _ in range(10):
turtle.width(4)
turtle.forward(105)
turtle.width(1)
turtle.forward(130)
turtle.width(4)
turtle.forward(105)
turtle.left(126)
turtle.width(1)
turtle.forward(76.5)
turtle.width(4)
turtle.forward(76.5)
turtle.width(1)
turtle.forward(94)
turtle.width(4)
turtle.forward(76.5)
turtle.width(1)
turtle.forward(76.5)
turtle.left(126)
Finally, we replace the thickness changes with lifting and lowering the pen:
Which is simply a matter of replacing the width() calls with penup() and pendown():
for _ in range(10):
turtle.pendown()
turtle.forward(105)
turtle.penup()
turtle.forward(130)
turtle.pendown()
turtle.forward(105)
turtle.left(126)
turtle.penup()
turtle.forward(76.5)
turtle.pendown()
turtle.forward(76.5)
turtle.penup()
turtle.forward(94)
turtle.pendown()
turtle.forward(76.5)
turtle.penup()
turtle.forward(76.5)
turtle.left(126)

Draw a quarter circle at the end of a straight line

I have coordinates of some points. My task is to get the direction of those points and find where future possible points will be located in the calculated direction. To do so, I have planned the following-
Fit a line to the points
Draw a quarter circle at the end of the fitted line. In common sense, the quarter circle might not be the right option to go for. However, it is a part of another problem and has to be solved this way.
I am using the following codes to fit a line
from matplotlib import pyplot as plt
from scipy import stats
x = [1,2,3,2,5,6,7,8,9,10]
y = [2,4,11,8,8,18,14,11,18,20]
slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
line = [slope*i+intercept for i in x]
plt.plot(x, line)
Suppose, the two points on the fitted line is (9,17) and (10,19). How can I draw a quarter circle at (10,19) with a radius of 5 in the direction of the line?
Ultimately, I will have a point location and I have to check whether the point falls inside the quarter circle or not, I assume which could be done with shapely.
Instead of calculating all the math by yourself, you can delegate it to Shapely.
First, create a circle at the end of the line with the help of buffer:
from shapely.affinity import rotate
from shapely.geometry import LineString, Point
from shapely.ops import split
a = (10, 20)
b = (15, 30)
ab = LineString([a, b]) # the line you got from linear regression
circle = Point(b).buffer(5)
Now, let's get two new lines that will delimit the area of the sector we want. We will do it by rotating the line using rotate to 135º at each direction, so that the sector's central angle will be 360º - 135º * 2 = 90º, which is a quarter of circle:
left_border = rotate(ab, -135, origin=b)
right_border = rotate(ab, 135, origin=b)
Finally, use split to get the sector:
splitter = LineString([*left_border.coords, *right_border.coords[::-1]])
sector = split(circle, splitter)[1]
From here you can easily find out if a point lies inside of the sector using contains method. For example:
points_of_interest = [Point(16, 32), Point(12, 30)]
for point in points_of_interest:
print(sector.contains(point))
# True
# False
To check whether point P falls inside the quarter circle, you can find distance from line end B (length of BP) and cosine of angle between unit line direction vector d and vector BP
distance = sqrt(BP.x * BP.x + BP.y * BP.y)
cosine = (d.x * BP.x + d.y * BP.y) / (distance)
if (distance < radius) and (cosine >= sqrt(2)/2)
P in sector
Unit vector d might be calculated from data you already have:
d.x = sign(slope) * sqrt(1/(1+slope**2))
d.y = sqrt(slope**2/1+slope**2)
Note that sign of components is not defined clearly (because two opposite vectors have the same slope)
To address the main question - end points of arc might be calculated using rotated (by Pi/4) direction vector
cf = sqrt(2)/2
arcbegin.x = b.x + radius * d.x * cf - radius * d.y * cf
arcbegin.y = b.y + radius * d.x * cf + radius * d.y * cf
arcend.x = b.x + radius * d.x * cf + radius * d.y * cf
arcend.y = b.y - radius * d.x * cf + radius * d.y * cf
I think You should implement the arch as follows. (I just shown the your missing logic, You haft to add your plot ). Good luck
from matplotlib import pyplot as plt
from scipy import stats
x = [1,2,3,2,5,6,7,8,9,10]
y = [2,4,11,8,8,18,14,11,18,20]
slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
line = [slope*i + intercept for i in x]
# Logic Part *****************************************************
from matplotlib.patches import Arc
import math
# circuile parameters
R = 5
xEnd,yEnd = 10 , 20 #Your end point cords, in your case Point B
LowerThita = math.degrees(math.atan(slope)) - 45
UpperThita = math.degrees(math.atan(slope)) + 45
# Figure setup
fig, ax = plt.subplots()
ax.set_xlim(-R , (R+xEnd) * 1.05)
ax.set_ylim(-R , (R+yEnd) * 1.05)
# Arcs
ax.add_patch(Arc((xEnd, yEnd), R, R,
theta1=LowerThita, theta2=UpperThita, edgecolor='k'))
plt.show()
#NOTE : You Haft to add your line to the plot

Elliptic orbit with polar method tracing phases, axes and Earth direction

I would like to represent the elliptical orbit of a binary system of two stars. What I aim to, is something like this:
Where I have a grid of the sizes along the axes, an in-scale star at the focus, and the orbit of the secondary star. The decimal numbers along the orbit are the orbital phases. The arrow at the bottom is the Earth direction, and the thick part at the orbit is related to the observation for that specific case - I don't need it. What I want to change from this plot is:
Orbital phase: instead of numbers along the orbit, I would like "dashed rays" from the focus to the orbit, and the orbital phase above them:
I don't want the cross along (0, 0);
I would like to re-orient the orbit, in order that the 0.0 phase is around the top left part of the plot, and the Earth direction is an upward pointing straight arrow (the parameters of my system are different from the one plotted here).
I tried to look for python examples, but the only thing I came out with (from here), is a polar plot:
which is not really representative of what I want, but still is a beginning:
import numpy as np
import matplotlib.pyplot as plt
cos = np.cos
pi = np.pi
a = 10
e = 0.1
theta = np.linspace(0,2*pi, 360)
r = (a*(1-e**2))/(1+e*cos(theta))
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
ax.set_yticklabels([])
ax.plot(theta,r)
print(np.c_[r,theta])
plt.show()
Here's something that gets you very close. You do not need polar coordinates to plot a decent ellipse. There is a so-called artist you can readily utilize.
You probably have to customize the axis labels and maybe insert an arrow or two if you want:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
# initializing the figure:
fig = plt.figure()
# the (carthesian) axis:
ax = fig.add_subplot(111,aspect='equal')
ax.grid(True)
# parameters of the ellipse:
a = 5.0
e = 4.0
b = np.sqrt(a**2.0 - e**2.0)
# the center of the ellipse:
x = 6.0
y = 6.0
# the angle by which the ellipse is rotated:
angle = -45.0
#angle = 0.0
# plotting the ellipse, using an artist:
ax.add_artist(Ellipse(xy=[x,y], width=2.0*a, height=2.0*b, \
angle=angle, facecolor='none'))
ax.set_xlim(0,2.0*x)
ax.set_ylim(0,2.0*y)
# marking the focus (actually, both)
# and accounting for the rotation of the ellipse by angle
xf = [x - e*np.cos(angle * np.pi/180.0),
x + e*np.cos(angle * np.pi/180.0)]
yf = [y - e*np.sin(angle * np.pi/180.0),
y + e*np.sin(angle * np.pi/180.0)]
ax.plot(xf,yf,'xr')
# plotting lines from the focus to the ellipse:
# these should be your "rays"
t = np.arange(np.pi,3.0*np.pi,np.pi/5.0)
p = b**2.0 / a
E = e / a
r = [p/(1-E*np.cos(ti)) for ti in t]
# converting the radius based on the focus
# into x,y coordinates on the ellipse:
xr = [ri*np.cos(ti) for ri,ti in zip(r,t)]
yr = [ri*np.sin(ti) for ri,ti in zip(r,t)]
# accounting for the rotation by anlge:
xrp = [xi*np.cos(angle * np.pi/180.0) - \
yi*np.sin(angle * np.pi/180.0) for xi,yi in zip(xr,yr)]
yrp = [xi*np.sin(angle * np.pi/180.0) + \
yi*np.cos(angle * np.pi/180.0) for xi,yi in zip(xr,yr)]
for q in range(0,len(t)):
ax.plot([xf[0], xf[0]+xrp[q]],[yf[0], yf[0]+yrp[q]],'--b')
# put labels outside the "rays"
offset = 0.75
rLabel = [ri+offset for ri in r]
xrl = [ri*np.cos(ti) for ri,ti in zip(rLabel,t)]
yrl = [ri*np.sin(ti) for ri,ti in zip(rLabel,t)]
xrpl = [xi*np.cos(angle * np.pi/180.0) - \
yi*np.sin(angle * np.pi/180.0) for xi,yi in zip(xrl,yrl)]
yrpl = [xi*np.sin(angle * np.pi/180.0) + \
yi*np.cos(angle * np.pi/180.0) for xi,yi in zip(xrl,yrl)]
# for fancy label rotation reduce the range of the angle t:
tlabel = [(ti -np.pi)*180.0/np.pi for ti in t]
for q in range(0,len(tlabel)):
if tlabel[q] >= 180.0:
tlabel[q] -= 180.0
# convert the angle t from radians into degrees:
tl = [(ti-np.pi)*180.0/np.pi for ti in t]
for q in range(0,len(t)):
rotate_label = angle + tlabel[q]
label_text = '%.1f' % tl[q]
ax.text(xf[0]+xrpl[q],yf[0]+yrpl[q],label_text,\
va='center', ha='center',rotation=rotate_label)
plt.show()
The example above will result in this figure:
Explanations:
You can use an artist to plot the ellipse, instead of using polar coordinates
The nomenclature is based on the definitions available on Wikipedia
The angle in the artist setup rotates the ellipse. This angle is later used to rotate coordinates for the rays and labels (this is just math)
The rays are derived from the polar form of the ellipse relative to a focus.
The angle t runs from pi to 3.0*pi because I assumed that this would correspond to your idea of where the rays should start. You get the same rays for 0 to 2.0*pi. I used np.arange instead of linspace because I wanted a defined increment (pi/5.0, or 36 degrees) in this example.
The labels at the end of the rays are placed as text, the variable offset controls the distance between the ellipse and the labels. Adjust this as needed.
For the alignment of the label text orientation with the rays, I reduced the angle, t, to the range 0 to 180 degrees. This makes for better readability compared to the full range of 0 to 360 degrees.
For the label text I just used the angle, t, for simplicity. Replace this with whatever information better suits your purpose.
The angle, t, was converted from radians to degrees before the loop that places the label. Inside the loop, each element of tl is converted to a string. This allows for more formatting control (e.g. %.3f if you needed 3 decimals)

Categories