| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- # Rotate a cube with a quaternion
- # Demo program
- # Pat Hickey, 27 Dec 10
- # This code is in the public domain.
- import pygame
- import pygame.draw
- import pygame.time
- from math import sin, cos, acos
- from euclid import *
- class Screen (object):
- def __init__(self,x=320,y=280,scale=1):
- self.i = pygame.display.set_mode((x,y))
- self.originx = self.i.get_width() / 2
- self.originy = self.i.get_height() / 2
- self.scale = scale
- def project(self,v):
- assert isinstance(v,Vector3)
- x = v.x * self.scale + self.originx
- y = v.y * self.scale + self.originy
- return (x,y)
- def depth(self,v):
- assert isinstance(v,Vector3)
- return v.z
- class PrespectiveScreen(Screen):
- # the xy projection and depth functions are really an orthonormal space
- # but here i just approximated it with decimals to keep it quick n dirty
- def project(self,v):
- assert isinstance(v,Vector3)
- x = ((v.x*0.957) + (v.z*0.287)) * self.scale + self.originx
- y = ((v.y*0.957) + (v.z*0.287)) * self.scale + self.originy
- return (x,y)
- def depth(self,v):
- assert isinstance(v,Vector3)
- z = (v.z*0.9205) - (v.x*0.276) - (v.y*0.276)
- return z
- class Side (object):
- def __init__(self,a,b,c,d,color=(50,0,0)):
- assert isinstance(a,Vector3)
- assert isinstance(b,Vector3)
- assert isinstance(c,Vector3)
- assert isinstance(d,Vector3)
- self.a = a
- self.b = b
- self.c = c
- self.d = d
- self.color = color
- def centroid(self):
- return ( self.a + self.b + self.c + self.d ) / 4
- def draw(self,screen):
- assert isinstance(screen,Screen)
- s = [ screen.project(self.a)
- , screen.project(self.b)
- , screen.project(self.c)
- , screen.project(self.d)
- ]
- pygame.draw.polygon(screen.i,self.color,s)
- def erase(self,screen,clear_color = (0,0,0)):
- c = self.color
- self.color = clear_color
- self.draw(screen)
- self.color = c
- class Edge (object):
- def __init__(self,a,b,color=(0,0,255)):
- assert isinstance(a,Vector3)
- assert isinstance(b,Vector3)
- self.a = a
- self.b = b
- self.color = color
- def centroid(self):
- return (self.a + self.b) / 2
- def draw(self,screen):
- assert isinstance(screen,Screen)
- aa = screen.project(self.a)
- bb = screen.project(self.b)
- pygame.draw.line(screen.i, self.color, aa,bb)
- def erase(self,screen,clear_color = (0,0,0)):
- c = self.color
- self.color = clear_color
- self.draw(screen)
- self.color = c
-
-
- class Cube (object):
- def __init__(self,a=10,b=10,c=10):
- self.a = a
- self.b = b
- self.c = c
- self.pts = [ Vector3(-a,b,c) , Vector3(a,b,c)
- , Vector3(a,-b,c) , Vector3(-a,-b,c)
- , Vector3(-a,b,-c) , Vector3(a,b,-c)
- , Vector3(a,-b,-c) , Vector3(-a,-b,-c) ]
- def origin(self):
- """ reset self.pts to the origin, so we can give them a new rotation """
- a = self.a; b = self.b; c = self.c
- self.pts = [ Vector3(-a,b,c) , Vector3(a,b,c)
- , Vector3(a,-b,c) , Vector3(-a,-b,c)
- , Vector3(-a,b,-c) , Vector3(a,b,-c)
- , Vector3(a,-b,-c) , Vector3(-a,-b,-c) ]
- def sides(self):
- """ each side is a Side object of a certain color """
- # leftright = (80,80,150) # color
- # topbot = (30,30,150)
- # frontback = (0,0,150)
- one = (255, 0, 0)
- two = (0, 255, 0)
- three = (0, 0, 255)
- four = (255, 255, 0)
- five = (0, 255, 255)
- six = (255, 0, 255)
- a, b, c, d, e, f, g, h = self.pts
- sides = [ Side( a, b, c, d, one) # front
- , Side( e, f, g, h, two) # back
- , Side( a, e, f, b, three) # bottom
- , Side( b, f, g, c, four) # right
- , Side( c, g, h, d, five) # top
- , Side( d, h, e, a, six) # left
- ]
- return sides
- def edges(self):
- """ each edge is drawn as well """
- ec = (0,0,255) # color
- a, b, c, d, e, f, g, h = self.pts
- edges = [ Edge(a,b,ec), Edge(b,c,ec), Edge(c,d,ec), Edge(d,a,ec)
- , Edge(e,f,ec), Edge(f,g,ec), Edge(g,h,ec), Edge(h,e,ec)
- , Edge(a,e,ec), Edge(b,f,ec), Edge(c,g,ec), Edge(d,h,ec)
- ]
- return edges
- def erase(self,screen):
- """ erase object at present rotation (last one drawn to screen) """
- assert isinstance(screen,Screen)
- sides = self.sides()
- edges = self.edges()
- erasables = sides + edges
- [ s.erase(screen) for s in erasables]
- def draw(self,screen,q=Quaternion(1,0,0,0)):
- """ draw object at given rotation """
- assert isinstance(screen,Screen)
- self.origin()
- self.rotate(q)
- sides = self.sides()
- edges = self.edges()
- drawables = sides + edges
- drawables.sort(key=lambda s: screen.depth(s.centroid()))
- [ s.draw(screen) for s in drawables ]
- def rotate(self,q):
- assert isinstance(q,Quaternion)
- R = q.get_matrix()
- self.pts = [R*p for p in self.pts]
- if __name__ == "__main__":
- pygame.init()
- screen = Screen(480,400,scale=1.5)
- cube = Cube(40,30,60)
- q = Quaternion(1,0,0,0)
- incr = Quaternion(0.96,0.01,0.01,0).normalized()
-
- while 1:
- q = q * incr
- cube.draw(screen,q)
- event = pygame.event.poll()
- if event.type == pygame.QUIT \
- or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
- break
- pygame.display.flip()
- pygame.time.delay(50)
- cube.erase(screen)
|