Advertisement
Guest User

Untitled

a guest
Feb 8th, 2013
192
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.90 KB | None | 0 0
  1. # full credit: http://www.pygame.org/project-Quadtree+test-1691-.html
  2. #only edit : cast to draw to int.
  3.  
  4.  
  5. import pygame
  6. from pygame.locals import *
  7. pygame.init()
  8.  
  9. import time, math, random
  10.  
  11. ### METHODS ###
  12.  
  13. def n(x):
  14.   y=1
  15.   for z in xrange(x):
  16.     y*=z+1
  17.   return y
  18.  
  19. def rect_quad_split(rect):
  20.   w=rect.width/2.0
  21.   h=rect.height/2.0
  22.   rl=[]
  23.   rl.append(pygame.Rect(float(rect.left), float(rect.top), float(w), float(h)))
  24.   rl.append(pygame.Rect(float(rect.left+w), float(rect.top), float(w), float(h)))
  25.   rl.append(pygame.Rect(float(rect.left), float(rect.top+h), float(w), float(h)))
  26.   rl.append(pygame.Rect(float(rect.left+w), float(rect.top+h), float(w), float(h)))
  27.  
  28.   return rl
  29.  
  30. def lerp(a, b, p):
  31.   return(a+(b-a)*p)
  32.  
  33.  
  34.  
  35. ### CLASSES ###
  36.  
  37. class QuadLeaf(object):
  38.   def __init__(self, world, rect, quadtree, level):
  39.     self.world = world
  40.     self.rect=rect
  41.     self.quadtree = quadtree
  42.     self.level=level
  43.  
  44.     quadtree.levels[level].append(self)
  45.     self.branches=None
  46.     self.entities=[]#a list of collidable objects on this leaf
  47.    
  48.   def subdivide(self):
  49.     self.branches=[]
  50.     for rect in rect_quad_split(self.rect):
  51.       b=QuadLeaf(self.world, rect, self.quadtree, self.level+1)
  52.       self.branches.append(b)
  53.      
  54.   def test_collisions(self, obj):
  55.     for obj2 in self.entities:
  56.       if obj2.rect.colliderect(obj.rect):
  57.         if [obj,obj2] not in self.quadtree.collisions and [obj2,obj] not in self.quadtree.collisions:
  58.           self.quadtree.collisions.append([obj, obj2])
  59.    
  60.     if self.level==self.quadtree.level_limit-1:
  61.         return
  62.    
  63.     if self.branches!=None:
  64.         for b in self.branches:
  65.             b.test_collisions(obj)
  66.  
  67.   def add_entity(self, obj):
  68.     for obj2 in self.entities:
  69.       if obj2.rect.colliderect(obj.rect):
  70.         if [obj,obj2] not in self.quadtree.collisions and [obj2,obj] not in self.quadtree.collisions:
  71.           self.quadtree.collisions.append([obj, obj2])
  72.      
  73.     if self.level==self.quadtree.level_limit-1:#if it's hit the limit of levels
  74.       self.entities.append(obj)
  75.       obj.level=int(self.level)
  76.       return
  77.  
  78.     if self.branches==None:
  79.       self.subdivide()
  80.      
  81.     #the first thing it has to check is if the object fits in any branches
  82.     fits=None
  83.     for b in self.branches:
  84.       if b.rect.contains(obj.rect):
  85.         fits=b
  86.     if fits==None:
  87.       #doesn't fit in any of the branches, stays on this level
  88.       self.entities.append(obj)
  89.       obj.level=int(self.level)
  90.       for b in self.branches:
  91.         b.test_collisions(obj)
  92.     else:
  93.       #it DOES fit in a branch, hand it to the branch
  94.       fits.add_entity(obj)
  95.      
  96.   def render(self):
  97.     pygame.draw.rect(self.world.screen, [127,127,127], self.rect, 2)
  98.     if self.branches!=None:
  99.         for b in self.branches:
  100.             b.render()
  101.    
  102.  
  103.  
  104.  
  105. class QuadTree(object):
  106.   def __init__(self, world, world_rect):
  107.     self.world = world
  108.     self.world_rect=world_rect
  109.  
  110.     self.level_limit=3
  111.     self.reset()
  112.  
  113.   def add_entity(self, obj):
  114.     for obj2 in self.entities:
  115.       if obj2.rect.colliderect(obj.rect):
  116.         if [obj,obj2] not in self.collisions and [obj2,obj] not in self.collisions:
  117.           self.collisions.append([obj, obj2])
  118.  
  119.     if self.level_limit<=0:
  120.       self.entities.append(obj)
  121.       obj.level=-1
  122.       return
  123.      
  124.     #the first thing it has to check is if the object fits in any branches
  125.     fits=None
  126.     for b in self.branches:
  127.       if b.rect.contains(obj.rect):
  128.         fits=b
  129.     if fits==None:
  130.       #doesn't fit in any of the branches, stays on main level
  131.       self.entities.append(obj)
  132.       obj.level=-1
  133.       for b in self.branches:
  134.         b.test_collisions(obj)
  135.     else:
  136.       #it DOES fit in a branch, hand it to the branch
  137.       fits.add_entity(obj)
  138.  
  139.   def test_collisions(self):
  140.     #this means that each object will need a collide command
  141.     for c in self.collisions:
  142.       c[0].collide(c[1])
  143.       c[1].collide(c[0])
  144.  
  145.   def reset(self):
  146.     self.levels=[]
  147.     for x in xrange(self.level_limit):
  148.       self.levels.append([])
  149.     self.branches=[]
  150.     if self.level_limit>0:
  151.       for rect in rect_quad_split(self.world_rect):
  152.         b=QuadLeaf(self.world, rect, self, 0)
  153.         self.branches.append(b)
  154.     self.entities=[]#a list of all collidable objects on the main level
  155.     self.collisions=[]
  156.  
  157.   def render(self):
  158.     for branch in self.branches:
  159.         branch.render()
  160.    
  161.    
  162. class Ball(object):
  163.   def __init__(self, world, pos, radius):
  164.     self.world = world
  165.     self.pos = pos
  166.     self.vector = [0,0]
  167.     self.radius=radius
  168.     self.set_rect()
  169.    
  170.     self.level=-1
  171.  
  172.   def set_rect(self):
  173.     self.rect = pygame.Rect(self.pos[0]-self.radius, self.pos[1]-self.radius,self.radius*2,self.radius*2)
  174.  
  175.   def collide(self, obj):
  176.     x=(self.pos[0]-obj.pos[0])
  177.     y=(self.pos[1]-obj.pos[1])
  178.     dist = math.sqrt(x**2+y**2)
  179.     if dist<=self.radius+obj.radius and dist!=0:
  180.       #we collided, act apon it
  181.       x/=dist
  182.       y/=dist
  183.       collide_amount=(self.radius+obj.radius)-dist
  184.      
  185.       p1=collide_amount/float(self.radius+obj.radius)
  186.       p2=math.sin(math.radians(  lerp(0,90,p1)  ))
  187.  
  188.       p=lerp(1,p2,0.5)#the bigger this number, the more squishy the balls are
  189.      
  190.       collide_amount*=p
  191.       self.vector[0]+=x*collide_amount
  192.       self.vector[1]+=y*collide_amount
  193.  
  194.   def update(self):
  195.     ## HEY LOOK AT THIS!!!!
  196.     #you can change these variables!
  197.     self.level=-1
  198.     self.vector[1]=self.vector[1]+0.0#<-- simulates gravity
  199.    
  200.     self.vector[0]=self.vector[0]*1.0#<-- simulates air resistance
  201.     self.vector[1]=self.vector[1]*1.0#<--
  202.  
  203.   def move(self):
  204.     dist = math.sqrt(self.vector[0]**2+self.vector[1]**2)
  205.     if dist>self.radius:
  206.       self.vector[0]/=dist
  207.       self.vector[1]/=dist
  208.       self.vector[0]*=self.radius
  209.       self.vector[1]*=self.radius
  210.      
  211.     self.pos[0]+=self.vector[0]
  212.     self.pos[1]+=self.vector[1]
  213.  
  214.     if self.pos[0]-self.radius<self.world.quadtree.world_rect.left  or  self.pos[0]+self.radius>self.world.quadtree.world_rect.right:
  215.         self.vector[0]*=-1
  216.         self.pos[0]+=self.vector[0]
  217.         self.vector[0]*=0.8
  218.  
  219.     if self.pos[1]-self.radius<self.world.quadtree.world_rect.top  or  self.pos[1]+self.radius>self.world.quadtree.world_rect.bottom:
  220.         self.vector[1]*=-1
  221.         self.pos[1]+=self.vector[1]
  222.         self.vector[1]*=0.8
  223.  
  224.     self.set_rect()
  225.     self.world.quadtree.add_entity(self)
  226.  
  227.   def render(self, showLevel=False):
  228.     if showLevel:
  229.       if self.level==-1:
  230.           c=[255,0,0]
  231.       elif self.level==0:
  232.           c=[255,255,0]
  233.       elif self.level==1:
  234.           c=[0,255,0]
  235.       elif self.level==2:
  236.           c=[0,255,255]
  237.       elif self.level==3:
  238.           c=[0,0,255]
  239.       else:
  240.           c=[255,0,255]
  241.     else:
  242.       c=[127,127,0]
  243.  
  244.     pos = (int(self.pos[0]), int(self.pos[1]))
  245.     self.world.rects.append(pygame.draw.circle(self.world.screen, c, pos, int(self.radius)))
  246.     pygame.draw.circle(self.world.screen, [0,0,0], pos, self.radius,1)
  247.    
  248. ##########################################################
  249. ###    MAIN    ###########################################
  250. ##########################################################
  251.  
  252. class World(object):
  253.   def __init__(self):
  254.     #SET UP __INIT__
  255.       self.screen_size = (800,600)
  256.       self.screen = pygame.display.set_mode(self.screen_size)
  257.    
  258.       self.clock = pygame.time.Clock()
  259.       self.framerate=1000
  260.  
  261.       self.font = pygame.font.Font(pygame.font.get_default_font(),30)
  262.  
  263.       self.quadtree=QuadTree(self,pygame.Rect(0,0,self.screen_size[0], self.screen_size[1]))
  264.       self.quadtree.level_limit=0
  265.  
  266.       self.balls=[]
  267.       for x in xrange(600):
  268.         radius=(x%3)*2+6
  269.         self.balls.append(Ball(self, [random.randint(50,self.screen_size[0]-50), random.randint(50,self.screen_size[1]-50)], radius))
  270.  
  271.       self.bg_color = [0,0,0]
  272.       self.screen.fill(self.bg_color)
  273.  
  274.  
  275.       #SET UP ENTITIES
  276.       self.reset()
  277.       self.run()
  278.      
  279.   def reset(self):
  280.     pass
  281.  
  282.   def run(self):
  283.       stage=0
  284.       print "The tree will start turned off. You will see the frame rate will be very low"
  285.       next=time.time()+10.0
  286.       avg_frame_rate=0
  287.       prev_frame_rate=0
  288.       avg_checks=0
  289.      
  290.       # RUN MAIN LOOP
  291.       while True:
  292.         self.clock.tick(self.framerate)
  293.         self.events = pygame.event.get()
  294.         self.keys=pygame.key.get_pressed()
  295.         self.mouse_pos=pygame.mouse.get_pos()
  296.         self.mouse_but = pygame.mouse.get_pressed()
  297.         self.fps=self.clock.get_fps()
  298.         self.rects=[]
  299.  
  300.  
  301.         #UPDATE
  302.         for b in self.balls:
  303.           b.update()
  304.  
  305.         #MOVE
  306.         for b in self.balls:
  307.           b.move()
  308.  
  309.         #TEST COLLISIONS
  310.         self.quadtree.test_collisions()
  311.  
  312.         #RENDER
  313.         if stage!=None:
  314.           self.quadtree.render()
  315.        
  316.         for b in self.balls:
  317.           b.render(stage!=None)
  318.  
  319.         #renders framerate
  320.         img=self.font.render(str(int(self.fps)),True, [255,255,255])
  321.         rect = img.get_rect(topleft=[0,0])
  322.         self.rects.append(rect)
  323.         self.screen.blit(img, rect)
  324.  
  325.         #renders collision checks
  326.         img=self.font.render(str(int(avg_checks)),True, [255,255,255])
  327.         rect = img.get_rect(bottomleft=[0,self.screen_size[1]])
  328.         self.rects.append(rect)
  329.         self.screen.blit(img, rect)
  330.  
  331.         pygame.display.flip()
  332.         self.screen.fill(self.bg_color)
  333.  
  334.         for rect in self.rects:
  335.           self.screen.fill(self.bg_color, rect)
  336.  
  337.         avg_frame_rate=lerp(avg_frame_rate, self.fps,0.05)
  338.         if stage!=None:
  339.           avg_checks=lerp(avg_checks, len(self.quadtree.collisions),0.1)
  340.           if time.time()>=next:
  341.             print stage+1
  342.             if avg_frame_rate>prev_frame_rate:
  343.               #continues to test
  344.               prev_frame_rate=avg_frame_rate
  345.               next=time.time()+10.0
  346.               stage+=1
  347.               self.quadtree.level_limit=stage
  348.             else:
  349.               #found peak of preformance
  350.               print "Found peak. Preformance for this computer will not be any better then it is now."
  351.               M=(len(self.balls)-1)*len(self.balls)
  352.               print "Would normally have",M,"collision checks,"
  353.               print "but this quadtree reduces it to roughly",int(avg_checks),"collision checks"
  354.               self.quadtree.level_limit=stage-1
  355.               stage=None
  356.  
  357.  
  358.         #EVENT HANDLER UPDATE
  359.         for event in self.events:
  360.           if event.type==MOUSEBUTTONDOWN:
  361.             for b in self.balls:
  362.               b.vector[0]+=random.randint(-10,10)
  363.               b.vector[1]+=random.randint(-10,10)
  364.           if event.type==KEYDOWN or event.type==QUIT:
  365.             if event.type==QUIT or event.key==K_ESCAPE:
  366.                 pygame.quit()
  367.                 return
  368.        
  369.         self.quadtree.reset()
  370.  
  371.  
  372. world = World()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement