solution_050j.py

#!/usr/bin/python3
# ===================================================================
# draw a wire frame object's surfaces animation
#
# each surface has a different color. use a normal vector to
# determine if a surface should be drawn or not.
# ===================================================================

import coordinate_conversion as cc
import transformation_matrix as tm
import user_interface as ui
import normal_vector as nv
import draw_xy_axes as ax
import graphics as gr
import numpy as np
import time, re, os, platform, sys

win_height = 801               # window height  
win_width  = 801               # window width

# ---- each surface has a different color

surcolor= [ 'red','green','blue','cyan','yellow','white' ]

# ---- wire frame cube (points, lines, surfaces, normal vectors)
#
#         5+----------+6
#         /|         /|
#        / |        / |
#      1+----------+2 |
#       |  |       |  |
#       | 4+-------|--+7
#       | /        | /
#       |/         |/
#      0+----------+3
#
#
# ---- wire frame (cube) corner points
pts = [ (-50,-50,50),  (-50,50,50),             # point 0,1
        (50,50,50),    (50,-50,50),             # point 2,3
        (-50,-50,-50), (-50,50,-50),            # point 4,5
        (50,50,-50),   (50,-50,-50) ]           # point 5,7
#
# ---- pivot point
pivot = (0, 0, 0)
#
# ---- wire frame (cube) lines
lns = [ (pts[0],pts[1]), (pts[1],pts[2]),       # line 0,1
        (pts[2],pts[3]), (pts[3],pts[0]),       # line 2,3
        (pts[4],pts[5]), (pts[5],pts[6]),       # line 4,5
        (pts[6],pts[7]), (pts[7],pts[4]),       # line 6,7
        (pts[0],pts[4]), (pts[1],pts[5]),       # line 8,9
        (pts[2],pts[6]), (pts[3],pts[7]) ]      # line 10,11
#
# ---- wire frame (cube) surfaces
sur = [ (pts[0],pts[1],pts[2],pts[3]),          # surface 0  red
        (pts[7],pts[6],pts[5],pts[4]),          # surface 1  green
        (pts[4],pts[5],pts[1],pts[0]),          # surface 2  blue
        (pts[3],pts[2],pts[6],pts[7]),          # surface 3  cyan
        (pts[1],pts[5],pts[6],pts[2]),          # surface 4  yellow
        (pts[0],pts[3],pts[7],pts[4]) ]         # surface 5  white
#
# ---- wire frame (cube) normal vector points for each surface
# ---- (points used to calculate normal vector)
nvpts = [ (pts[0],pts[1],pts[2]),               # nv surface 0
          (pts[7],pts[6],pts[5]),               # nv surface 1
          (pts[4],pts[5],pts[1]),               # nv surface 2
          (pts[3],pts[2],pts[6]),               # nv surface 3
          (pts[1],pts[5],pts[6]),               # nv surface 4
          (pts[0],pts[3],pts[7]) ]              # nv surface 5


# -------------------------------------------------------------------
# ---- test normal vector
# ---- return True  if it leaning towards   the viewer (+z)
# ---- return False if is leaning away from the viewer (-z)
# -------------------------------------------------------------------

def test_normal_vector(pts,mtrx):

    # ---- convert normal vector original points to arrays

    orgpt0 = np.array([pts[0][0],pts[0][1],pts[0][2],1])
    orgpt1 = np.array([pts[1][0],pts[1][1],pts[1][2],1])
    orgpt2 = np.array([pts[2][0],pts[2][1],pts[2][2],1])

    # ---- transform original points using transformation matrix

    newpt0 = mtrx @ orgpt0
    newpt1 = mtrx @ orgpt1
    newpt2 = mtrx @ orgpt2

    # ---- create normal vector from new points

    v = nv.create_normal_vector(newpt0,newpt1,newpt2,1000)

    ##print(f'nv    = {v}')

    if v[2] > 0:               # z greater than zero?
        return True
    return False

# -------------------------------------------------------------------
# ---- draw surface
# -------------------------------------------------------------------

def draw_surface(win,pts,mtrx,color='red'):

    points = []                # list of points defining
                               # surface (polygon)

    # ---- create a list of graphics point
    # ---- (required by graphics polygon function)

    for p in pts:

        # ---- original coordinates (as an array)

        orgcord = np.array( [ p[0],p[1],p[2],1] )

        # ---- transform coordinates using transformation matrix

        newcord = mtrx @ orgcord

        # ---- convert new coordinates to window coordinates
        # ---- so the can be drawn

        x,y = cc.center_to_win_coords(newcord[0],newcord[1],
                                      win.width,win.height)

        # ---- add window coordinates to the list of graphics points

        points.append(gr.Point(x,y))

    # ---- draw the polygon using the list of graphics points

    pobj = gr.Polygon(points)
    pobj.setFill(color)
    pobj.draw(win)

   # ---- return the graphics object in case we need it later

    return pobj

# -------------------------------------------------------------------
# ---- draw surfaces
# ----
# ---- win    the window to draw in
# ---- sur    a list of the surfaces to be drawn
# ---- nvpts  points used to calc normal vector
# ---- mtrx   transformation matrix
# -------------------------------------------------------------------

def draw_surfaces(win,sur,nvpts,mtrx):

    i = 0                      # surface color index
    l = len(surcolor)          # length of color list

    j = 0                      # normal vector list index 

    pobjs = []

    for s in sur:              # for each surface

        if i >= l:             # end of color list
            i = 0              # start over at 0

        if test_normal_vector(nvpts[j],mtrx):
            ##print(f'surface {j}  {surcolor[i]:6}  draw')
            pobjs.append(draw_surface(win,s,mtrx,surcolor[i]))
        ##else:
            ##print(f'surface {j}  {surcolor[i]:6}  don\'t draw')

        i += 1                 # next color
        j += 1                 # next normal vector

    return pobjs

# -------------------------------------------------------------------
# ---- main
# -------------------------------------------------------------------

# ---- running Python3?

if not ui.running_python3:
    print()
    print('Must run Python3 - exit program')
    print()
    sys.exit()

# ---- create drawing window

win = gr.GraphWin("Solid Object", win_width, win_height)
win.setBackground("white")

# ---- draw X,Y coordinate axes

ax.draw_xy_axes(win,True)

# ---- loop

degx = 10
degy = 20
degz = 30

for _ in range(20):

    # ---- create transformation matrix

    m0 = tm.get_identity_matrix_3d()
    m1 = tm.get_x_rotation_matrix_3d(degx)
    m2 = tm.get_y_rotation_matrix_3d(degy)
    m3 = tm.get_z_rotation_matrix_3d(degz)
    m4 = tm.get_translation_matrix_3d(100,100,0)

    mtrx = m4 @ m3 @ m2 @ m1

    # ---- draw (cube) surfaces

    pobjs = draw_surfaces(win,sur,nvpts,mtrx)

    time.sleep(0.3)

    for p in pobjs:
        p.undraw()

    degx += 20
    degy += 20
    degz += 20

# ---- pause program

ui.pause()