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()
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