#!/usr/bin/python3 # ================================================================== # Calculate points (x,y coords) on an ellipse. The formula works # with ints and floats, but this code limited 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 raidus 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 consistant # ---- 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("Elipse 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()