Thursday, November 11, 2010

pyGTK - Events & Basic 3d

Below is the code for a little exercise in basic pyGTK event-handling and basic 3-d, overhauling some code I wrote yonks ago in Borland Turbo Pascal.

dependencies: pyGTK



--- --- rot.py --- --- --- --- --- --- --- --- --- ---

import math

class Point2D(object):
def __init__(self, x=None,y=None):
self.x = x
self.y = y

class Point3D(object):
def __init__(self, x=None,y=None,z=None):
self.x = x
self.y = y
self.z = z

class Rot(object):

rad_const = math.pi / 180.0

def rad(self, theta):
return theta * Rot.rad_const

def __init__(self):

self.sin_table = []
self.cos_table = []

for d in range(360):
self.sin_table.append(math.sin(self.rad(d)))
self.cos_table.append(math.cos(self.rad(d)))

for i in [1]: # points
self.points = []
for i in range(24):
self.points.append(Point3D())

self.points[0].x = 10.0
self.points[0].z = 4.0
self.points[1].x = 4.0
self.points[1].z = 10.0
self.points[2].x = -4.0
self.points[2].z = 10.0
self.points[3].x = -10.0
self.points[3].z = 4.0
self.points[4].x = -10.0
self.points[4].z = -4.0
self.points[5].x = -4.0
self.points[5].z = -10.0
self.points[6].x = 4.0
self.points[6].z = -10.0
self.points[7].x = 10.0
self.points[7].z = -4.0

for i in range(8):
self.points[i].y = 30.0

for i in range(8, 16):
self.points[i].x = self.points[i - 8].x
self.points[i].z = self.points[i - 8].z
self.points[i].y = -30.0

self.points[16].x = 20.0
self.points[16].z = 20.0
self.points[17].x = -20.0
self.points[17].z = 20.0
self.points[18].x = -20.0
self.points[18].z = -20.0
self.points[19].x = 20.0
self.points[19].z = -20.0

for i in range(16, 20):
self.points[i].y = 30.0

for i in range(20, 24):
self.points[i].x = self.points[i - 4].x
self.points[i].z = self.points[i - 4].z
self.points[i].y = -30.0


self.x_off = 320.0
self.y_off = 240.0
self.z_off = 50.0

self.z_deg = 0
self.x_deg = 0
self.y_deg = 0

def flatten(self, cube):
'''
320 : 200 => 1 : 5 / 8 | a.r of 4 : 3, height = 4 / 3 of width, therefore
x : y => 1 : 5 / 6
'''

n = len(self.points)

x_scale = 100.0
y_scale = 100.0 # 83.0

flat = []
for i in range(n):
x = cube[i].x
y = cube[i].y
z = self.z_off - cube[i].z
nx = int(self.x_off + (x / z * x_scale))
ny = int(self.y_off + (y / z * y_scale))
flat.append(Point2D(nx, ny))

return flat


def rotate(self, points, z, x, y):
'''
to rotate a point (oX, oY) through Alpha radians, counter-clockwise, about
the z axis:
nX = [Cos(Alpha) * oX] - [Sin(Alpha) * oY]
nY = [Sin(Alpha) * oX] + [Cos(Alpha) * oY]
'''

rotated = []
for i in range(len(points)):
oX = points[i].x
oY = points[i].y
oZ = points[i].z

# about z axis
nX = (self.cos_table[z] * oX) - (self.sin_table[z] * oY)
nY = (self.sin_table[z] * oX) + (self.cos_table[z] * oY)
oX = nX
oY = nY

# about x axis
nZ = (self.cos_table[x] * oZ) - (self.sin_table[x] * oY)
nY = (self.sin_table[x] * oZ) + (self.cos_table[x] * oY)
oZ = nZ

# about y axis
nX = (self.cos_table[y] * oX) - (self.sin_table[y] * oZ)
nZ = (self.sin_table[y] * oX) + (self.cos_table[y] * oZ)

rotated.append(Point3D(x=nX, y=nY, z=nZ))

return rotated

def draw_to_pixmap(self, points, pixmap, gc, style):
'''
'''
# pixmap.draw_line(self.gc, x, y, self.w/2, self.h/2)

# octal inlay flush with end plate
for i in range(7):
pixmap.draw_line(gc, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y)

pixmap.draw_line(gc, points[7].x, points[7].y, points[0].x, points[0].y)

# connections between octal inlays'pipe'
for i in range(8):
pixmap.draw_line(gc, points[i].x, points[i].y, points[i + 8].x, points[i + 8].y)

# octal inlay flush with end plate
for i in range(8, 15):
pixmap.draw_line(gc, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y)

pixmap.draw_line(gc, points[15].x, points[15].y, points[8].x, points[8].y)

# end plate of column
for i in range(16, 19):
pixmap.draw_line(gc, points[i].x, points[i].y, points[i + 1].x, points[i + 1].y)

pixmap.draw_line(gc, points[19].x, points[19].y, points[16].x, points[16].y)

# end plate of column
for i in range(20, 23):
pixmap.draw_line(gc, points[i].x, points[i].y, points[i + 1].x,points[i + 1].y)

pixmap.draw_line(gc, points[23].x, points[23].y, points[20].x, points[20].y)


def iterate(self, pixmap, gc, style):
'''
'''
rotated = self.rotate(self.points, self.z_deg, self.x_deg, self.y_deg)
flattened = self.flatten(rotated)
self.draw_to_pixmap(flattened, pixmap, gc, style)

def move(self, x, y, z):

self.z_deg = self.z_deg + int(z)
self.x_deg = self.x_deg + int(y)
self.y_deg = self.y_deg - int(x)

self.shake()

def shake(self):

if (self.z_deg >= 360):
self.z_deg = self.z_deg - 360
if (self.z_deg < 0):
self.z_deg = self.z_deg + 360

if (self.x_deg >= 360):
self.x_deg = self.x_deg - 360
if (self.x_deg < 0):
self.x_deg = self.x_deg + 360

if (self.y_deg >= 360):
self.y_deg = self.y_deg - 360
if (self.y_deg < 0):
self.y_deg = self.y_deg + 360


--- --- run.py --- --- --- --- --- --- ---

import gtk
import gobject
import random
# ---
from rot import Rot

class Gfx(object):
'''
'''
def __init__(self):
'''
'''
self.b1_down = False
self.b2_down = False
self.b3_down = False

self.b1_x = None
self.b1_y = None

self.rot = Rot()

self.started = False

self.i = 0

self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.win.set_title('Fractured Nature')
self.gw = 640
self.gh = 480 #int(float(self.gw) / 1.618)
self.win.resize(self.gw, self.gh)
self.win.set_position(gtk.WIN_POS_CENTER)
self.win.connect('destroy', gtk.main_quit)
#self.win.realize()

self.da = gtk.DrawingArea()

self.win.add(self.da)
self.da.set_size_request(self.gw, self.gh)
self.win.set_resizable(False)

self.da.connect("expose-event", self.area_expose_cb)
self.da.connect("button_press_event", self.button_press_event)
self.da.connect("button_release_event", self.button_released_event)
self.da.connect("motion_notify_event", self.motion_notify_event)
#self.da.connect("scroll_event", self.scroll_event)

self.da.set_events(
gtk.gdk.EXPOSURE_MASK |
gtk.gdk.BUTTON_PRESS_MASK |
gtk.gdk.BUTTON_RELEASE_MASK |
gtk.gdk.POINTER_MOTION_MASK #|
# gtk.gdk.SCROLL_MASK
)

self.da.show()
self.win.show_all()

def area_expose_cb(self, area, event):
'''
'''
self.area = area

self.style = self.da.get_style()
self.gc = self.style.fg_gc[gtk.STATE_NORMAL]

self.w, self.h = area.window.get_size()

#for i in dir(area.window): print(i)
self.started = True

return True

def button_press_event(self, widget, event):

if (event.button == 1):
self.b1_x = event.x
self.b1_y = event.y
self.b1_down = True
elif (event.button == 3):
self.b3_z = event.x
self.b3_down = True
elif (event.button == 2):
self.b2_down = True
elif (event.button == 4):
print(4)
elif (event.button == 5):
print(5)

return True

def motion_notify_event(self, widget, event):

if (self.b1_down == True):
x = event.x
y = event.y

d_x = x - self.b1_x
d_y = y - self.b1_y
d_z = 0

self.b1_x = x
self.b1_y = y

self.rot.move(d_x, d_y, d_z)

elif (self.b3_down == True):

z = event.x

d_z = z - self.b3_z

d_y = 0
d_x = 0

self.b3_z = z

self.rot.move(d_x, d_y, d_z)

return True

def button_released_event(self, widget, event):

if (event.button == 1):
self.b1_down = False
elif (event.button == 2):
self.b2_down = False
elif (event.button == 3):
self.b3_down = False

return True


def nop(self):
'''
'''
if (self.started != True):
return True

pixmap = gtk.gdk.Pixmap(self.da.window, self.gw, self.gh, depth=-1)
pixmap.draw_rectangle(self.style.white_gc, True, 0, 0, self.gw, self.gh)

self.rot.iterate(pixmap, self.gc, self.style)

self.area.window.draw_drawable(self.gc, pixmap, 0, 0, 0, 0, -1, -1)
self.area.show()

return True # repeat





g = Gfx()

delay = 50
g.timer = gobject.timeout_add(delay, g.nop)

gtk.main()


1 comment:

  1. Thanks for this, David. I'm new to pygtk as well as python and have been trying to write some kind of rotating-cube-program by scouring the internet for close-to-what-I-want examples. Yours did the trick. Thank you!

    ReplyDelete

comment: