#!/usr/bin/python3
# ==================================================================
# Calculate points (x,y coords) on an ellipse. The formula works
# with ints and floats, but this code limits it self to
# integers (pixels).
#
# from: www.physicsforums.com/threads/
# calculating-angle-of-circle-to-produce-given-ellipse.625187/
# ==================================================================
import numpy as np
# ------------------------------------------------------------------
# calculate the x,y coordinates of a circle tilted towards the
# viewer (assumed to be at +z = infinity). An ellipse appears
# (to the viewer) by tilting a circle (of radius r) around the X
# axis. Tilted (angle a) towards or away from the viewer.
# ------------------------------------------------------------------
# This function only does 1/4 of an ellipse. Input parameters are
# converted to positive values. This is not a problem because
# the ellipse is symmetrical around the x and y axes.
#
# +y
# (-x,y) | (x,y)
# |
# -x ----+---- +x
# |
# (-x,-y) | (x,-y)
# -y
#
# ------------------------------------------------------------------
# circle drawn using '*' in the x,y plane with z = 0
#
# +y -z
# | /
# *** /
# *** | ***
# ** |/ **
# -x --*----+----*---- +x
# ** /| **
# **/ | ***
# / ***
# / |
# +z -y
#
# ------------------------------------------------------------------
# input parameters
#
# x pixel coordinates along the +x (0 to r)
# r circle radius
# a tilt angle (degrees)
# ------------------------------------------------------------------
def circle_to_ellipse(x,r,a):
# ---- no negative input values allowed
x = int(np.abs(x))
r = int(np.abs(r))
a = np.abs(a) # not necessary but consistent
# ---- x can not exceed r
if x > r:
print('Error: X exceeds Radius')
return (0,0)
# ---- given circle radius and a circle's x pixel coordinate
# ---- along the +X axis, calculate the circle's y pixel
# ---- coordinate. the circle formula is x**2 + y**2 = r**2
y = round(np.sqrt(r**2 - x**2)) # convert to integer
# ---- calculate the cosine of the tilt angle
rad = np.radians(a) # convert angle to radians
c = np.cos(rad) # cos of angle
# ---- ellipse formula
# ---- (xx/rr) + (yy/(rr*cc)) = 1
# ---- yy = cc * (rr - xx)
# ---- calculate ellipse x,y coordinates (given x)
xe = x
if y == 0:
ye = y
else:
xx = x*x # squared
rr = r*r # squared
cc = c*c # cos of angle squared
ye = round(np.sqrt(cc*(rr - xx)))
# ---- return ellipse x,y coordinates
return (xe,ye)
# ------------------------------------------------------------------
# ---- main
# ------------------------------------------------------------------
if __name__ == '__main__':
import graphics as gr
import user_interface as ui
import coordinate_conversion as cc
import draw_axes as ax
def calc_and_draw(win,color,r,a):
for x in [0,25,50,75,100,125,150,175,190,200]:
xe,ye = circle_to_ellipse(x, r, a)
##print(f'xe = {xe}, ye = {ye}, a = {a}')
# ---- draw the ellipse x,y coordinates
wx,wy = cc.center_to_win_coords(xe,ye,wwidth,wheight)
c = gr.Circle(gr.Point(wx,wy),4)
c.setWidth(1)
c.setFill(color)
c.draw(win)
wx,wy = cc.center_to_win_coords(xe,-ye,wwidth,wheight)
c = gr.Circle(gr.Point(wx,wy),4)
c.setWidth(1)
c.setFill(color)
c.draw(win)
wx,wy = cc.center_to_win_coords(-xe,ye,wwidth,wheight)
c = gr.Circle(gr.Point(wx,wy),4)
c.setWidth(1)
c.setFill(color)
c.draw(win)
wx,wy = cc.center_to_win_coords(-xe,-ye,wwidth,wheight)
c = gr.Circle(gr.Point(wx,wy),4)
c.setWidth(1)
c.setFill(color)
c.draw(win)
raidus = 200 # circle radius
wwidth = 601 # window width
wheight = 601 # window height
win = gr.GraphWin(" Ellipse Test",wwidth,wheight)
win.setBackground("white")
ax.draw_xy_axes(win,True)
calc_and_draw(win,"black",raidus,60.0)
calc_and_draw(win,"red",raidus,30.0)
calc_and_draw(win,"green",raidus,0.0)
ui.pause()