#!/usr/bin/python3 # =============================================================== # manipulate a point by rotating it around the X,Y,Z axes # using transformation matrices # # rotation about the XYZ axes can be somewhat weird. # With 3D rotations I am never sure where things will end up. # # There are modification that could be done to consolidate this # code to make it less verbose. I "opened up" this code to make # it easier (I hope) to follow what it is doing. # =============================================================== import sys import math as m import numpy as np import graphics as gr import draw_xy_axes as ax import user_interface as ui import coordinate_conversion as cc import transformation_matrix as tm menu1 = ''' ============================================================ This program allows a user to manipulate a point (small circle) displayed in graphics window using transformation matrices to rotate it around the X,Y,Z axes The graphics window is 801x801 pixels + rotation from the x axis is counter clockwise when viewed from +z infinity. + rotation from the y axis is counter clockwise when viewed from +x infinity. + rotation from the z axis is counter clockwise when viewed from +y infinity. ============================================================ ''' menu2 = ''' option description ====== ==================================================== 0 reset to initial coordinates (x=300.0,y=300.0,z=0.0) 1 rotate around the X axis 2 rotate around the Y axis 3 rotate around the Z axis 7 display current coordinates 8 change current XYZ coordinates 9 exit program ''' # --------------------------------------------------------------- # rotate XYZ coordinates around the X axis # return the new XYZ coordinates # --------------------------------------------------------------- def rotate_around_x_axis(x,y,z,deg): C = np.array([x,y,z,1]) # current point coordinate array R = tm.get_x_rotation_matrix_3d(deg) NC = R@C # new point coordinates array print(f'X axis rotation {deg} degrees') print(f'old coords: x={C[0]:.4f}, y={C[1]:.4f}, z={C[2]:.4f}') print(f'new coords: x={NC[0]:.4f}, y={NC[1]:.4f}, z={NC[2]:.4f}') return (NC[0],NC[1],NC[2]) # --------------------------------------------------------------- # rotate XYZ coordinates around the Y axis # return the new XYZ coordinates # --------------------------------------------------------------- def rotate_around_y_axis(x,y,z,deg): C = np.array([x,y,z,1]) # current point coordinate array R = tm.get_y_rotation_matrix_3d(deg) NC = R@C # new point coordinates array print(f'Y axis rotation {deg} degrees') print(f'old coords: x={C[0]:.4f}, y={C[1]:.4f}, z={C[2]:.4f}') print(f'new coords: x={NC[0]:.4f}, y={NC[1]:.4f}, z={NC[2]:.4f}') return (NC[0],NC[1],NC[2]) # --------------------------------------------------------------- # rotate XYZ coordinates around the Zaxis # return the new XYZ coordinates # --------------------------------------------------------------- def rotate_around_z_axis(x,y,z,deg): C = np.array([x,y,z,1]) # current point coordinate array R = tm.get_z_rotation_matrix_3d(deg) NC = C@R # new point coordinates array print(f'Z axis rotation {deg} degrees') print(f'old coords: x={C[0]:.4f}, y={C[1]:.4f}, z={C[2]:.4f}') print(f'new coords: x={NC[0]:.4f}, y={NC[1]:.4f}, z={NC[2]:.4f}') return (NC[0],NC[1],NC[2]) # --------------------------------------------------------------- # ---- draw a point (small circle) using XYZ coordinates # ---- # ---- Note: only two of the axes are used to define the plane of # ---- the graphics window. the third axis defines the viewers # ---- location (towards the origin (0,0) or away from # ---- the origin) this code defines the viewer to be located # ---- +z infinity and the other two (x and y) define the plane # ---- of the graphics window. this a standard configuration # ---- (but not the only one). # --------------------------------------------------------------- def draw_point(win,x,y,z,color='red',size=4): rx = round(x) # convert to integer ry = round(y) # convert to integer rz = round(z) # convert to integer wx,wy = cc.center_to_win_coords(rx,ry,win.width,win.height) p = gr.Circle(gr.Point(wx,wy),size) p.setFill(color) p.setOutline('black') p.setWidth(1) p.draw(win) return p # --------------------------------------------------------------- # ---- un-draw graphics objects # --------------------------------------------------------------- def undraw_graphics_objects(objlst): for o in objlst: o.undraw() # --------------------------------------------------------------- # ---- main # --------------------------------------------------------------- if __name__ == '__main__': # ---- create a graphics window window_title = 'Rotation Using Transformation Matrices' win = gr.GraphWin(window_title,801,801) win.setBackground('white') # ---- draw X,Y axes with the origin (0,0) in the # ---- center of the graphics window ax.draw_xy_axes(win) # ---- starting conditions graphics_objs = [] initial_x = 300.0 initial_y = 300.0 initial_z = 0.0 current_x = initial_x current_y = initial_y current_z = initial_z p = draw_point(win,initial_x,initial_y,initial_z) graphics_objs.append(p) print(menu1) # ---- menu and menu actions while True: print(menu2) # ---- get user's option selection ---------------------- opt = ui.get_user_input('Enter option: ') if not opt: break opt = opt[0].lower() # ---- reset -------------------------------------------- if opt == 0 or opt == 'r': current_x = initial_x current_y = initial_y current_z = initial_z undraw_graphics_objects(graphics_objs) graphics_objs = [] p = draw_point(win,initial_x,initial_y,initial_z) graphics_objs.append(p) continue # ---- rotate around the X axis ------------------------- if opt == '1' or opt == 'x': print() prompt = 'Enter rotation around X axis (degrees): ' sdeg = ui.get_user_input(prompt) if not sdeg: continue # empty string tf,deg = ui.is_float(sdeg) if not tf: print() print(f'Error: illegal X rotation angle)') continue # ---- rotate around X axis x,y,z = rotate_around_x_axis(current_x,current_y, current_z,deg) current_x = x current_y = y current_z = z # ---- draw new point undraw_graphics_objects(graphics_objs) graphics_objs = [] p = draw_point(win,current_x,current_y,current_z) graphics_objs.append(p) continue # ---- rotate around the Y axis ------------------------- if opt == '2' or opt == 'y': print() prompt = 'Enter rotation around Y axis (degrees): ' sdeg = ui.get_user_input(prompt) if not sdeg: continue # empty string tf,deg = ui.is_float(sdeg) if not tf: print() print(f'Error: illegal X axis rotation angle)') continue # ---- rotate around y axis x,y,z = rotate_around_y_axis(current_x,current_y, current_z,deg) current_x = x current_y = y current_z = z # ---- draw new point undraw_graphics_objects(graphics_objs) graphics_objs = [] p = draw_point(win,current_x,current_y,current_z) graphics_objs.append(p) continue # ---- rotate around the Z axis ------------------------- if opt == '3' or opt == 'z': print() prompt = 'Enter rotation around Z axis (degrees): ' sdeg = ui.get_user_input(prompt) if not sdeg: continue # empty string tf,deg = ui.is_float(sdeg) if not tf: print() print(f'Error: illegal Z axis rotation angle)') continue # ---- rotate around Z axis x,y,z = rotate_around_z_axis(current_x,current_y, current_z,deg) current_x = x current_y = y current_z = z # ---- draw new point undraw_graphics_objects(graphics_objs) graphics_objs = [] p = draw_point(win,current_x,current_y,current_z) graphics_objs.append(p) continue # ---- display the current coordinates ------------------ if opt == '7' or opt == 'd': print() print(f'current coordinates: X={current_x:.4f}, ' +\ f'Y={current_y:.4f}, Z={current_z:.4f}') continue # ---- change initial XYZ coordinates ------------------- if opt == '8' or opt == 'c': print() prompt = 'Enter XYZ coordinates: ' s = ui.get_user_input(prompt) s = s.replace(',', ' ') s = s.replace(r'/', ' ') s = s.replace(':', ' ') lst = s.split() if len(lst) != 3: print() print(f'Error: 3 XYZ coordinates required ({s})') tf,x = ui.is_float(lst[0]) if not tf: print() print(f'Error: illegal X coordinate ({x})') continue tf,y = ui.is_float(lst[1]) if not tf: print() print(f'Error: illegal Y coordinate ({y})') continue tf,z = ui.is_float(lst[2]) if not tf: print() print(f'Error: illegal Z coordinate ({z})') continue current_x = x current_y = y current_z = z undraw_graphics_objects(graphics_objs) graphics_objs = [] p = draw_point(win,current_x,current_y,current_y) graphics_objs.append(p) continue # ---- exit program ------------------------------------- if opt == '9' or opt == 'e': break # ---- option error ------------------------------------- print() print(f'unknown option ({opt})') continue win.close()