#! /usr/bin/python3 # =================================================================== # draw/rotate a graphics object; a solid or wireframe T # =================================================================== # There is something wrong with this code. It does not distinguish # between which polygon is in front of or behind another polygon. # It just draws them in the order they appear in the data structure # (polys). # # Z-buffering aka Depth Buffering (Wikipedia) # draws polygons based on where they fall along the Z axis. It draws the # farthest away first. This way the nearer polygons are drawn on top of # the farther ones. This assumes the viewer will always be at +Z infinity. # Not a bad assumption. # # Another way is to calculate a normal vector for each polygon # and testing to see if it points toward the viewer or away # from the viewer. For solid objects, polygons with vectors that # point away from the viewer can not be seen and do not need to # be drawn. # =================================================================== # When drawing solid objects, it is sometimes useful to color # surfaces. Colors "white", "black", "red", "green", "blue", "cyan", # "yellow", and "magenta" will always be available. For a more # complete list go here. # # In graphics.py you can also specify colors using 'color_rgb(r,g,b)' # anywhere a color can be specified. For example: # c = Circle(Point(100,100),25) # c.setFill(color_rgb(130,0,130)) # =================================================================== import coordinate_conversion as cc import transformation_matrix as tm from draw_xy_axes import draw_xy_axes import user_interface as ui from graphics import * import numpy as np from copy import deepcopy import re, os, platform, sys win_height = 801 # window height win_width = 801 # window width # ------------------------------------------------------------------- # ---- graphics object - 3D character T # ------------------------------------------------------------------- # # 15------------------------8 # /| /| # 7-------------------------0 | # | | | | # | 14-------13 10------|-9 # |/ /| /| |/ # 6---------5 | 2---------1 # | | | | # | | | | # | | | | # | | | | # | | | | # | 12--|11 # |/ |/ # 4-----3 # 15 8 # +-------------------------+ # 7 /| 0 /| # +-------------------------+ | # | |14 13 10 | | # | +---------+ +-------|-+ 9 # |/ 5/| 2/| |/ # +---------+ | +---------+ # 6 | | | | 1 # | | | | # | | | | # | | | | # | |12 | |11 # | +---|-+ # |/ |/ # +-----+ # 4 3 # # +Y -Z # | / # | / # | / # |/ # -X ----------+---------- +X # /| # / | # / | # / | # +Z -Y # # ------------------------------------------------------------------ # ---- list of points defining an object # ---- in a format required by the transformation matrix (x,y,z,1) pts = [ (120,40,20,1), (120,0,20,1), (20,0,20,1), (20,-150,20,1), (-20,-150,20,1), (-20,0,20,1), (-120,0,20,1), (-120,40,20,1), (120,40,-20,1), (120,0,-20,1), (20,0,-20,1), (20,-150,-20,1), (-20,-150,-20,1), (-20,0,-20,1), (-120,0,-20,1), (-120,40,-20,1) ] pivot = (0,0,0) # ------------------------------------------------------------------- # ---- several versions of an object are define here (for testing) # ---- list of polygons (surfaces) that make up an object # ---- the format for each polygon (surface) is # ---- ((surface-points), surface-color) # ------------------------------------------------------------------- # ---- a single polygon (one surface) #polys = [ ((pts[0], pts[1], pts[2], pts[3], # pts[4], pts[5], pts[6], pts[7]), "blue") # ] # ---- two polygons (front and back surface) #polys = [ ((pts[0], pts[1], pts[2], pts[3], # pts[4], pts[5], pts[6], pts[7]), "blue"), # ((pts[8], pts[9], pts[10], pts[11], # pts[12], pts[13], pts[14], pts[15]), "green") # ] # ---- several (but not all) surfaces polys = [ ((pts[0], pts[1], pts[2], pts[3], pts[4], pts[5], pts[6], pts[7]), "blue"), ((pts[8], pts[9], pts[10], pts[11], pts[12], pts[13], pts[14], pts[15]), "green"), ((pts[7], pts[15], pts[14], pts[6]), "red"), ((pts[6], pts[5], pts[13], pts[14]), "red"), ((pts[5], pts[13], pts[12], pts[4]), "yellow"), ((pts[4], pts[3], pts[11], pts[12]), "black"), ((pts[0], pts[8], pts[15], pts[7]), "grey") ] # ------------------------------------------------------------------- # ---- this class is used as a data store for the solid object # ---- it also accumulates the transformation matrices # ------------------------------------------------------------------- class SolidObject: def __init__(self,pivot,polys,solid=True): self.solid = solid self.polys = polys.deepcopy() # copy input poly list self.x = pivot[0] # wire frame x pivot point self.y = pivot[1] # wire frame y pivot point self.z = pivot[2] # wire frame z pivot point self.mtrx = np.identity(4) def reset(self): self.mtrx = np.identity(4) def rotate_around_x_axis(self,degrees): m = tm.get_x_rotation_matrix_3d(degrees) mm = m @ self.mtrx self.mtrx = mm def rotate_around_y_axis(self,degrees): m = tm.get_y_rotation_matrix_3d(degrees) mm = m @ self.mtrx self.mtrx = mm def rotate_around_z_axis(self,degrees): m = tm.get_z_rotation_matrix_3d(degrees) mm = m @ self.mtrx self.mtrx = mm def get_matrix(self): return copy.deepcopy(self.mtrx) # ------------------------------------------------------------------- # ---- Function: create a polygon graphics object # ------------------------------------------------------------------- def create_polygon(win, mtrx, poly, width=1): polypoints = [] points = poly[0] # list of points color = poly[1] # color for pnt in points: p = mtrx @ pnt # polygon point x,y = cc.center_to_win_coords(p[0],p[1],win.width,win.height) polypoints.append(Point(x,y)) pobj = Polygon(polypoints) pobj.setWidth(width) pobj.setOutline(color) pobj.setFill(color) return pobj # ------------------------------------------------------------------- # ---- function: draw solid object # ------------------------------------------------------------------- def draw_solid_object(win, so, polys, winobjs): clear_window(winobjs) mtrx = so.get_matrix() # get transformation matrix for poly in polys: pobj = create_polygon(win, mtrx, poly) pobj.draw(win) winobjs.append(pobj) # ------------------------------------------------------------------- # ---- Function: create a line graphics object # ------------------------------------------------------------------- def create_line(win, mtrx, start, end, c="black", w=2): p0 = mtrx @ start # start of line p1 = mtrx @ end # end of line sx,sy = cc.center_to_win_coords(p0[0],p0[1],win.width,win.height) ex,ey = cc.center_to_win_coords(p1[0],p1[1],win.width,win.height) lobj = Line(Point(sx,sy),Point(ex,ey)) lobj.setWidth(w) lobj.setFill(c) return lobj # ------------------------------------------------------------------- # ---- draw wireframe using line graphics objects # ------------------------------------------------------------------- def draw_wireframe(win, so, polys, winobjs): mtrx = so.get_matrix() # get transformation matrix for poly in polys: # for each polygon points = poly[0] # get polygon's point list color = poly[1] # get polygon's color l = len(points) - 1 # index of last point in polygon i = 0 # list index j = 1 # list index while(True): # process the points in the polygon if i > l: # line start point exists? break if j > l: # line end point exist? lobj = create_line(win, mtrx, points[i], points[0], c=color) lobj.draw(win) winobjs.append(lobj) break lobj = create_line(win, mtrx, points[i], points[j], c=color) lobj.draw(win) winobjs.append(lobj) i += 1 j += 1 # ------------------------------------------------------------------- # ---- Function: fast (continuous) rotation # ------------------------------------------------------------------- def continuous_rotation(win, so, polys, winobjs): ui.clear_screen() while True: clear_window(winobjs) if so.solid: draw_solid_object(win, so, polys, winobjs) else: draw_wireframe(win, so, polys, winobjs) cmd = ui.get_user_input('Enter x, y, z, or 0: ') if not cmd: # empty string break if cmd[0] =='x': so.rotate_around_x_axis(9.0) elif cmd[0] == 'y': so.rotate_around_y_axis(9.0) elif cmd[0] == 'z': so.rotate_around_z_axis(9.0) elif cmd[0] == '0': so.reset() else: break # ------------------------------------------------------------------- # ---- Function: erase graphics objects from window # ------------------------------------------------------------------- def clear_window(objs): for o in objs: o.undraw() objs.clear() # ------------------------------------------------------------------- # ---- Function: display list of graphics objects # ------------------------------------------------------------------- def display_objects(objs,title=None): print('---------------------------------------------') if title: print(f'List Title : {title}') print(f'List Length: {len(objs)}') print(f'List ID : {id(objs)}') for o in objs: print(o) print('---------------------------------------------') # ------------------------------------------------------------------- # ---- main # ------------------------------------------------------------------- # ---- running Python3 if not ui.running_python3: print() print('Must run Python3 - exit program') print() sys.exit() # ---- create window win = GraphWin("Solid Object", win_width, win_height) win.setBackground("white") # ---- draw X,Y coordinate axes draw_xy_axes(win,True) # ---- loop forever so = SolidObject(pivot,polys) winobjs = [] while True: # ---- ask the user to make a selection ui.clear_screen() print('--------------------------------------------') print('--------------- Solid Object ---------------') print('--------------------------------------------') print('[qQ] = quit') if so.solid: print('[tT] = change to draw wireframe') else: print('[tT] = change to draw solid object') print('re = reset to initial conditions') print('dr = draw object') print('cr = continuous rotation') # ---- ask the user to make a selection print() cmd = ui.get_user_input('Enter command: ') if not cmd: # empty string? break # ---- quit if cmd == 'q'or cmd == 'Q': break # ---- toggle draw-solid-object flag if cmd == 't' or cmd == 'T': so.solid = not so.solid continue # ---- draw object if cmd == 'dr': clear_window(winobjs) if so.solid: draw_solid_object(win, so, polys, winobjs) else: draw_wireframe(win, so, polys, winobjs) continue # ---- reset object if cmd == 're': clear_window(winobjs) so.reset() continue if cmd == 'cw': clear_window(winobjs) continue if cmd == 'cr': continuous_rotation(win, so, polys, winobjs) continue # ---- oops! print() print(f'Unknown selection ({cmd})') ui.pause() # ---- close window and exit win.close()