Exemplo n.º 1
0
    def updateLocations(self):
        """Move all circles, reconstruct QuadTree and repaint."""
        if self.ship is None:
            self.init()

        # schedule next update if we are still playing
        if self.status == PLAYING:
            self.master.after(frameDelay, self.updateLocations)

        if self.tree.root is None:
            self.bullets = []
            self.status = WON
            self.canvas.delete(BULLET)
            self.updateShip()
            return

        # check if any bullet has hit an asteroid. If so, remove and add two
        for idx in range(len(self.bullets) - 1, -1, -1):
            b = self.bullets[idx]
            for c in self.tree.collide(b):
                if b:
                    del self.bullets[idx]
                    b = None
                    self.tree.remove(c)
                    c[RADIUS] = c[RADIUS] // 2
                    if c[RADIUS] >= 8:
                        # perturb to avoid unique coordinates
                        c2 = [
                            c[X] + c[DY], c[Y], c[RADIUS], False, False, c[DY],
                            c[DX]
                        ]
                        c3 = [
                            c[X] - c[DY], c[Y], c[RADIUS], False, False,
                            -c[DY], c[DX]
                        ]
                        self.tree.add(c2)
                        self.tree.add(c3)

        # Destroy tree each time and reinsert all circles with new locations
        nodes = self.tree.root.preorder()
        self.tree = QuadTree(Region(0, 0, 512, 512))
        for n in nodes:
            for c in n.circles:

                # Move from one side to the other, top and bottom
                self.updateShape(c)
                self.tree.add(c)

        # Update our location and the bullets
        self.updateShip()
        self.updateBullets()

        # Final check to see if any asteroid intersects our ship
        for c in self.tree.collide(self.ship):
            self.status = DESTROYED

        # Redraw all asteroids
        self.canvas.delete(ASTEROID)
        self.visit(self.tree.root)
Exemplo n.º 2
0
    def setUp(self):
        self.qt = QuadTree(Region(0, 0, 1024, 1024))

        self.qt.add([22, 40, 10, False, False])
        self.qt.add([13, 59, 20, False, False])
        self.qt.add([57, 37, 30, False, False])
        self.qt.add([43, 21, 20, False, False])
        self.qt.add([33, 11, 10, False, False])
 def pause(self, event):
     """Pause or Reset to start state (if already paused)."""
     if self.paused:
         self.tree = QuadTree(Region(0, 0, 512, 512))
         self.canvas.delete(ALL)
         self.visit(self.tree.root)
         self.viz.clear()
         self.restart()
     else:
         self.paused = True
         self.master.title(QuadTreeFixedApp.pausedTitle)
Exemplo n.º 4
0
 def __init__(self, master):
     """App for creating Quad tree dynamically with fixed circles that detect collisions."""
     
     master.title("Click to add fixed circles for QuadTree collision detection.") 
     self.master = master 
     
     # QuadTree holds the events
     self.tree = QuadTree(Region(0,0,512,512))
     
     self.canvas = Canvas(master, width=512, height=512)        
     self.canvas.bind("<Button-1>", self.click)
     self.canvas.bind("<Button-2>", self.reset)      # needed for Mac
     self.canvas.bind("<Button-3>", self.reset)      # this is PC
     self.canvas.pack()
Exemplo n.º 5
0
    def test_remove_less_than_four(self):
        self.qt = QuadTree(Region(0, 0, 1024, 1024))

        self.qt.add([22, 40, 10, False, False])
        self.qt.add([13, 59, 20, False, False])
        self.qt.add([57, 37, 30, False, False])

        self.assertTrue(self.qt.remove([57, 37, 30]))
        self.assertTrue(self.qt.remove([13, 59, 20]))
        self.assertTrue(self.qt.remove([22, 40, 10]))

        self.assertFalse(self.qt.remove([57, 37, 30]))
        self.assertFalse(self.qt.remove([13, 59, 20]))
        self.assertFalse(self.qt.remove([22, 40, 10]))
 def __init__(self, master):
     """App for creating QuadTree with moving circles that detect collisions."""
     
     master.title("Left-click to add moving circles for collision detection. Right-click resets.") 
     self.master = master 
     
     # QuadTree holds the events
     self.tree = QuadTree(Region(0,0,512,512))
     
     self.canvas = Canvas(master, width=512, height=512)        
     self.canvas.bind("<Button-1>", self.click)
     self.canvas.bind("<Button-2>", self.reset)      # needed for Mac
     self.canvas.bind("<Button-3>", self.reset)      # This is PC
     self.master.after(frameDelay, self.updateLocations)
     self.canvas.pack()
    def __init__(self, master):
        """App for detect collisions between moving circles using QuadTree."""

        master.title(QuadTreeFixedApp.defaultTitle)
        self.master = master
        self.paused = False

        # QuadTree holds the events
        self.tree = QuadTree(Region(0, 0, 512, 512))

        self.canvas = Canvas(master, width=512, height=512)
        self.canvas.bind("<Button-1>", self.click)
        self.canvas.bind("<Button-2>", self.pause)  # Needed for Mac
        self.canvas.bind("<Button-3>", self.pause)  # This is PC
        self.master.after(frameDelay, self.updateLocations)
        self.canvas.pack()

        # no visualization just yet
        self.viz = None
    def updateLocations(self):
        """Move all circles, reconstruct QuadTree and repaint."""
        self.master.after(frameDelay, self.updateLocations)

        if self.tree.root is None: return
        
        # Destroy tree each time and reinsert all circles
        nodes = self.tree.root.preorder()
        self.tree = QuadTree(Region(0,0,512,512))
        for n in nodes:
            if n.circles is None: 
                continue
            
            for c in n.circles:
                c[HIT] = False
                
                if c[X] - c[RADIUS] + c[DX] <= self.tree.region.x_min:
                    c[DX] = -c[DX]
                elif c[X] + c[RADIUS] + c[DX] >= self.tree.region.x_max:
                    c[DX] = -c[DX]
                else:
                    c[X] = c[X] + c[DX]
                    
                if c[Y] - c[RADIUS] + c[DY] <= self.tree.region.y_min:
                    c[DY] = -c[DY]
                elif c[Y] + c[RADIUS] + c[DY] >= self.tree.region.y_max:
                    c[DY] = -c[DY]
                else:
                    c[Y] = c[Y] + c[DY]
                    
                # Update hit status for all colliding circles and insert
                for circ in self.tree.collide(c):
                    circ[HIT] = True
                    c[HIT] = True
                self.tree.add(c)
                
        self.canvas.delete(ALL)
        self.visit(self.tree.root)
Exemplo n.º 9
0
    def init(self):
        """
        Initialize by placing a number of random asteroids and put ship in
        middle, facing up. Clear all asteroids around the ship
        """
        self.tree = QuadTree(Region(0, 0, 512, 512))
        clearZone = [256, 256, MaxRadius * 2]
        numAdded = 0
        while numAdded < NumAsteroids:
            dx = random.randint(1, 4) * (2 * random.randint(0, 1) - 1)
            dy = random.randint(1, 4) * (2 * random.randint(0, 1) - 1)
            circle = [
                random.randint(0, 512),
                random.randint(0, 512), MaxRadius, False, False, dx, dy, None
            ]
            if not defaultCollision(clearZone, circle):
                self.tree.add(circle)
                numAdded += 1

        # place ship in center and clear bullets. [3] is orientation, [DX,DY] are velocity
        self.ship = [256, 256, ShipRadius, pi / 2, None, 0, 0]
        self.bullets = []
        self.status = PLAYING
        self.visit(self.tree.root)
Exemplo n.º 10
0
class AsteroidsApp:
    def __init__(self, master):
        """App for detect collisions between moving circles using QuadTree."""

        master.title('Asteroid playing field. Click to restart game.')
        self.master = master

        # QuadTree holds the events
        self.tree = None

        self.canvas = Canvas(master, width=512, height=512)
        self.canvas.bind("<Button-1>", self.start)

        master.bind("<Key>", self.action)
        master.bind("<KeyRelease>", self.clear)
        self.canvas.pack()
        self.ship = None
        self.bullets = []
        self.thrust = False
        self.status = NEUTRAL

    def init(self):
        """
        Initialize by placing a number of random asteroids and put ship in
        middle, facing up. Clear all asteroids around the ship
        """
        self.tree = QuadTree(Region(0, 0, 512, 512))
        clearZone = [256, 256, MaxRadius * 2]
        numAdded = 0
        while numAdded < NumAsteroids:
            dx = random.randint(1, 4) * (2 * random.randint(0, 1) - 1)
            dy = random.randint(1, 4) * (2 * random.randint(0, 1) - 1)
            circle = [
                random.randint(0, 512),
                random.randint(0, 512), MaxRadius, False, False, dx, dy, None
            ]
            if not defaultCollision(clearZone, circle):
                self.tree.add(circle)
                numAdded += 1

        # place ship in center and clear bullets. [3] is orientation, [DX,DY] are velocity
        self.ship = [256, 256, ShipRadius, pi / 2, None, 0, 0]
        self.bullets = []
        self.status = PLAYING
        self.visit(self.tree.root)

    def toCartesian(self, y):
        """Convert tkinter point into Cartesian."""
        return self.canvas.winfo_height() - y

    def toTk(self, y):
        """Convert Cartesian into tkinter point."""
        if y == maxValue: return 0
        tk_y = self.canvas.winfo_height()
        if y != minValue:
            tk_y -= y
        return tk_y

    def clear(self, key):
        """
        React to key up events by turning off thruster. Behaves differently on Mac
        than on PC.
        """
        if key.char == 'l':
            self.thrust = False

    def action(self, key):
        """
        Detect Thrust (L), Turn Right (D), Turn Left (A) and Fire (SPACE).
        """
        if self.ship is None:
            return

        if key.char == 'l':
            self.thrust = True
            newdy = self.ship[DY] + sin(self.ship[ANGLE])
            if abs(newdy) > MaxVelocity:
                return
            newdx = self.ship[DX] + cos(self.ship[ANGLE])
            if abs(newdx) > MaxVelocity:
                return

            self.ship[DX] = newdx
            self.ship[DY] = newdy
        elif key.char == 'd':
            self.ship[ANGLE] = self.ship[ANGLE] - pi / 18
            if self.ship[ANGLE] < 0:
                self.ship[ANGLE] += 2 * pi
        elif key.char == 'a':
            self.ship[ANGLE] = self.ship[ANGLE] + pi / 18
            if self.ship[ANGLE] > 2 * pi:
                self.ship[ANGLE] -= 2 * pi
        elif key.char == ' ':
            # If we have more bullets, create a new one with our current orientation.
            if len(self.bullets) < NumBullets:
                angle = self.ship[ANGLE]
                dx = cos(angle) * ShipRadius
                dy = sin(angle) * ShipRadius
                self.bullets.append([
                    self.ship[X], self.ship[Y], BulletLife, None, None, dx, dy
                ])

    def updateBullets(self):
        """Update location of bullets, and remove those that have expired."""
        self.canvas.delete(BULLET)
        for idx in range(len(self.bullets) - 1, -1, -1):
            b = self.bullets[idx]
            self.updateShape(b)
            b[LIFE] -= 1
            if b[LIFE] <= 0:
                del self.bullets[idx]
            else:
                self.canvas.create_oval(b[X] - 2,
                                        self.toTk(b[Y] - 2),
                                        b[X] + 2,
                                        self.toTk(b[Y] + 2),
                                        fill='black',
                                        tag=BULLET)

    def updateShip(self):
        """Draw ship and orientation, and the exhaust thrust if in use."""
        self.canvas.delete(SHIP)
        self.updateShape(self.ship)
        x = self.ship[X]
        y = self.ship[Y]

        angle = self.ship[ANGLE]
        size = 2 * pi / 3
        color = 'black'
        if self.status == DESTROYED: color = 'red'
        if self.status == WON: color = 'green'
        self.canvas.create_line(x + cos(angle) * ShipRadius,
                                self.toTk(y + sin(angle) * ShipRadius),
                                x + cos(angle + size) * ShipRadius,
                                self.toTk(y + sin(angle + size) * ShipRadius),
                                fill=color,
                                tag=SHIP)
        self.canvas.create_line(x + cos(angle + size) * ShipRadius,
                                self.toTk(y + sin(angle + size) * ShipRadius),
                                x,
                                self.toTk(y),
                                fill=color,
                                tag=SHIP)
        self.canvas.create_line(x + cos(angle - size) * ShipRadius,
                                self.toTk(y + sin(angle - size) * ShipRadius),
                                x,
                                self.toTk(y),
                                fill=color,
                                tag=SHIP)
        self.canvas.create_line(x + cos(angle - size) * ShipRadius,
                                self.toTk(y + sin(angle - size) * ShipRadius),
                                x + cos(angle) * ShipRadius,
                                self.toTk(y + sin(angle) * ShipRadius),
                                fill=color,
                                tag=SHIP)

        # On Mac, thrust won't appear because of difference in key released events.
        if self.thrust:
            self.canvas.create_oval(x - 2,
                                    self.toTk(y - 2),
                                    x + 2,
                                    self.toTk(y + 2),
                                    fill='black',
                                    tag=SHIP)

    def updateShape(self, shape):
        """Move a given shape based on velocity and move shapes through boundaries."""
        if shape[X] <= self.tree.region.x_min:
            shape[X] = self.tree.region.x_max + shape[DX]
        elif shape[X] >= self.tree.region.x_max:
            shape[X] = self.tree.region.x_min + shape[DX]
        else:
            shape[X] = shape[X] + shape[DX]

        if shape[Y] <= self.tree.region.y_min:
            shape[Y] = self.tree.region.y_max + shape[DY]
        elif shape[Y] >= self.tree.region.y_max:
            shape[Y] = self.tree.region.y_min + shape[DY]
        else:
            shape[Y] = shape[Y] + shape[DY]

    def start(self, event):
        """Restart."""
        # If we have won or lost, need to re-register timed handler
        if not self.status == PLAYING:
            self.master.after(frameDelay, self.updateLocations)
        self.init()

    def visit(self, node):
        """Visit nodes recursively."""
        if node == None:
            return

        # Draw each circle, colored appropriately
        for circle in node.circles:
            self.canvas.create_oval(circle[X] - circle[RADIUS],
                                    self.toTk(circle[Y]) - circle[RADIUS],
                                    circle[X] + circle[RADIUS],
                                    self.toTk(circle[Y]) + circle[RADIUS],
                                    tag=ASTEROID)

        for n in node.children:
            self.visit(n)

    def updateLocations(self):
        """Move all circles, reconstruct QuadTree and repaint."""
        if self.ship is None:
            self.init()

        # schedule next update if we are still playing
        if self.status == PLAYING:
            self.master.after(frameDelay, self.updateLocations)

        if self.tree.root is None:
            self.bullets = []
            self.status = WON
            self.canvas.delete(BULLET)
            self.updateShip()
            return

        # check if any bullet has hit an asteroid. If so, remove and add two
        for idx in range(len(self.bullets) - 1, -1, -1):
            b = self.bullets[idx]
            for c in self.tree.collide(b):
                if b:
                    del self.bullets[idx]
                    b = None
                    self.tree.remove(c)
                    c[RADIUS] = c[RADIUS] // 2
                    if c[RADIUS] >= 8:
                        # perturb to avoid unique coordinates
                        c2 = [
                            c[X] + c[DY], c[Y], c[RADIUS], False, False, c[DY],
                            c[DX]
                        ]
                        c3 = [
                            c[X] - c[DY], c[Y], c[RADIUS], False, False,
                            -c[DY], c[DX]
                        ]
                        self.tree.add(c2)
                        self.tree.add(c3)

        # Destroy tree each time and reinsert all circles with new locations
        nodes = self.tree.root.preorder()
        self.tree = QuadTree(Region(0, 0, 512, 512))
        for n in nodes:
            for c in n.circles:

                # Move from one side to the other, top and bottom
                self.updateShape(c)
                self.tree.add(c)

        # Update our location and the bullets
        self.updateShip()
        self.updateBullets()

        # Final check to see if any asteroid intersects our ship
        for c in self.tree.collide(self.ship):
            self.status = DESTROYED

        # Redraw all asteroids
        self.canvas.delete(ASTEROID)
        self.visit(self.tree.root)
 def reset(self, event):
     """Reset to start state."""
     self.tree = QuadTree(Region(0,0,512,512))
     self.canvas.delete(ALL)
class QuadTreeMovingApp:
    
    def __init__(self, master):
        """App for creating QuadTree with moving circles that detect collisions."""
        
        master.title("Left-click to add moving circles for collision detection. Right-click resets.") 
        self.master = master 
        
        # QuadTree holds the events
        self.tree = QuadTree(Region(0,0,512,512))
        
        self.canvas = Canvas(master, width=512, height=512)        
        self.canvas.bind("<Button-1>", self.click)
        self.canvas.bind("<Button-2>", self.reset)      # needed for Mac
        self.canvas.bind("<Button-3>", self.reset)      # This is PC
        self.master.after(frameDelay, self.updateLocations)
        self.canvas.pack()

    def toCartesian(self, y):
        """Convert tkinter point into Cartesian."""
        return self.canvas.winfo_height() - y

    def toTk(self,y):
        """Convert Cartesian into tkinter point."""
        if y == maxValue: return 0
        tk_y = self.canvas.winfo_height()
        if y != minValue:
            tk_y -= y
        return tk_y
         
    def click(self, event):
        """Add circle to QuadTree with random radius and moving direction."""
        dx = random.randint(1,4)*(2*random.randint(0,1)-1)
        dy = random.randint(1,4)*(2*random.randint(0,1)-1)
        circle = [event.x, self.toCartesian(event.y), 
                  random.randint(4, MaxRadius), False, False, dx, dy]
        self.tree.add(circle)
        
    def reset(self, event):
        """Reset to start state."""
        self.tree = QuadTree(Region(0,0,512,512))
        self.canvas.delete(ALL)

    def visit (self, node):
        """Visit node to paint properly."""
        if node == None: return

        # draw rectangular region and hashed cross-hairs
        r = node.region
        self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max, self.toTk(r.y_max))
        
        self.canvas.create_line(r.x_min, self.toTk(node.origin[Y]), r.x_max, self.toTk(node.origin[Y]),
                                dash=(2, 4)) 
        self.canvas.create_line(node.origin[X], self.toTk(r.y_min), node.origin[X], self.toTk(r.y_max),
                                dash=(2, 4))
        
        # Draw each circle, colored appropriately
        for circle in node.circles:
            markColor = 'black'
            if circle[MULTIPLE]: markColor = 'blue'
            if circle[HIT]: markColor = 'red'
            self.canvas.create_oval(circle[X] - circle[RADIUS], self.toTk(circle[Y]) - circle[RADIUS], 
                                 circle[X] + circle[RADIUS], self.toTk(circle[Y]) + circle[RADIUS], 
                                 fill=markColor)
            
        for n in node.children:
            self.visit(n)
        
    def updateLocations(self):
        """Move all circles, reconstruct QuadTree and repaint."""
        self.master.after(frameDelay, self.updateLocations)

        if self.tree.root is None: return
        
        # Destroy tree each time and reinsert all circles
        nodes = self.tree.root.preorder()
        self.tree = QuadTree(Region(0,0,512,512))
        for n in nodes:
            if n.circles is None: 
                continue
            
            for c in n.circles:
                c[HIT] = False
                
                if c[X] - c[RADIUS] + c[DX] <= self.tree.region.x_min:
                    c[DX] = -c[DX]
                elif c[X] + c[RADIUS] + c[DX] >= self.tree.region.x_max:
                    c[DX] = -c[DX]
                else:
                    c[X] = c[X] + c[DX]
                    
                if c[Y] - c[RADIUS] + c[DY] <= self.tree.region.y_min:
                    c[DY] = -c[DY]
                elif c[Y] + c[RADIUS] + c[DY] >= self.tree.region.y_max:
                    c[DY] = -c[DY]
                else:
                    c[Y] = c[Y] + c[DY]
                    
                # Update hit status for all colliding circles and insert
                for circ in self.tree.collide(c):
                    circ[HIT] = True
                    c[HIT] = True
                self.tree.add(c)
                
        self.canvas.delete(ALL)
        self.visit(self.tree.root)
Exemplo n.º 13
0
class QuadTreeFixedApp:
    def __init__(self, master):
        """App for creating QuadTree with fixed circles that detect collisions."""

        master.title(
            "Click to add fixed circles for QuadTree collision detection.")
        self.master = master

        # QuadTree holds the events
        self.tree = QuadTree(Region(0, 0, 512, 512))

        self.canvas = Canvas(master, width=512, height=512)
        self.canvas.bind("<Button-1>", self.click)
        self.canvas.bind("<Button-2>", self.reset)  # Needed for Mac
        self.canvas.bind("<Button-3>", self.reset)  # This is PC
        self.canvas.pack()

        # no visualization just yet
        self.viz = None

    def toCartesian(self, y):
        """Convert tkinter point into Cartesian."""
        return self.canvas.winfo_height() - y

    def toTk(self, y):
        """Convert Cartesian into tkinter point."""
        if y == maxValue: return 0
        tk_y = self.canvas.winfo_height()
        if y != minValue:
            tk_y -= y
        return tk_y

    def click(self, event):
        """Add circle to QuadTree with random radius."""
        circle = [
            event.x,
            self.toCartesian(event.y),
            random.randint(MinRadius, MaxRadius), False, False
        ]

        # Mark these circles to have their HIT status set to True
        for circ in self.tree.collide(circle):
            circ[HIT] = True
            circle[HIT] = True

        self.tree.add(circle)
        self.visit(self.tree.root)
        self.viz.plot(self.tree.root)

    def reset(self, event):
        """Reset to start state."""
        self.tree = QuadTree(Region(0, 0, 512, 512))
        self.canvas.delete(ALL)
        self.visit(self.tree.root)
        self.viz.clear()

    def visit(self, node):
        """Visit nodes recursively."""
        if node == None:
            return

        # draw rectangular region with criss-crossed hashed lines
        r = node.region
        self.canvas.create_rectangle(r.x_min, self.toTk(r.y_min), r.x_max,
                                     self.toTk(r.y_max))

        self.canvas.create_line(r.x_min,
                                self.toTk(node.origin[Y]),
                                r.x_max,
                                self.toTk(node.origin[Y]),
                                dash=(2, 4))
        self.canvas.create_line(node.origin[X],
                                self.toTk(r.y_min),
                                node.origin[X],
                                self.toTk(r.y_max),
                                dash=(2, 4))

        for circle in node.circles:
            markColor = 'black'
            if circle[MULTIPLE]: markColor = 'blue'
            if circle[HIT]: markColor = 'red'
            self.canvas.create_oval(circle[X] - circle[RADIUS],
                                    self.toTk(circle[Y]) - circle[RADIUS],
                                    circle[X] + circle[RADIUS],
                                    self.toTk(circle[Y]) + circle[RADIUS],
                                    fill=markColor)
        for n in node.children:
            self.visit(n)
class QuadTreeFixedApp:

    defaultTitle = 'Left-Click adds circle. Right click pauses motion.'
    pausedTitle = 'Left-Click resumes. Right-click resets.'

    def __init__(self, master):
        """App for detect collisions between moving circles using QuadTree."""

        master.title(QuadTreeFixedApp.defaultTitle)
        self.master = master
        self.paused = False

        # QuadTree holds the events
        self.tree = QuadTree(Region(0, 0, 512, 512))

        self.canvas = Canvas(master, width=512, height=512)
        self.canvas.bind("<Button-1>", self.click)
        self.canvas.bind("<Button-2>", self.pause)  # Needed for Mac
        self.canvas.bind("<Button-3>", self.pause)  # This is PC
        self.master.after(frameDelay, self.updateLocations)
        self.canvas.pack()

        # no visualization just yet
        self.viz = None

    def toCartesian(self, y):
        """Convert tkinter point into Cartesian."""
        return self.canvas.winfo_height() - y

    def toTk(self, y):
        """Convert Cartesian into tkinter point."""
        if y == maxValue: return 0
        tk_y = self.canvas.winfo_height()
        if y != minValue:
            tk_y -= y
        return tk_y

    def restart(self):
        """Restart motion."""
        self.master.after(frameDelay, self.updateLocations)
        self.master.title(QuadTreeFixedApp.defaultTitle)
        self.paused = False

    def click(self, event):
        """Add circle to QuadTree with random radius and direction."""
        if self.paused:
            self.restart()
        else:
            dx = random.randint(1, 4) * (2 * random.randint(0, 1) - 1)
            dy = random.randint(1, 4) * (2 * random.randint(0, 1) - 1)

            # make sure circle COMPLETELY fits within region to
            # avoid strange edge-cases with moving circles.
            radius = random.randint(4, MaxRadius)
            x, y = event.x, self.toCartesian(event.y)
            if x < radius: x = radius
            if y < radius: y = radius
            if x > 512 - radius: x = 512 - radius
            if y > 512 - radius: y = 512 - radius
            circle = [x, y, radius, False, False, dx, dy, None]

            self.tree.add(circle)

    def pause(self, event):
        """Pause or Reset to start state (if already paused)."""
        if self.paused:
            self.tree = QuadTree(Region(0, 0, 512, 512))
            self.canvas.delete(ALL)
            self.visit(self.tree.root)
            self.viz.clear()
            self.restart()
        else:
            self.paused = True
            self.master.title(QuadTreeFixedApp.pausedTitle)

    def visit(self, node):
        """Visit nodes recursively."""
        if node == None:
            return

        # draw rectangular region with criss-crossed hashed lines
        r = node.region
        self.canvas.create_rectangle(r.x_min,
                                     self.toTk(r.y_min),
                                     r.x_max,
                                     self.toTk(r.y_max),
                                     tag=LINE)

        self.canvas.create_line(r.x_min,
                                self.toTk(node.origin[Y]),
                                r.x_max,
                                self.toTk(node.origin[Y]),
                                dash=(2, 4),
                                tag=LINE)
        self.canvas.create_line(node.origin[X],
                                self.toTk(r.y_min),
                                node.origin[X],
                                self.toTk(r.y_max),
                                dash=(2, 4),
                                tag=LINE)

        for n in node.children:
            self.visit(n)

    def updateLocations(self):
        """Move all circles, reconstruct QuadTree and repaint."""
        if not self.paused:
            self.master.after(frameDelay, self.updateLocations)

        if self.tree.root is None: return

        # Destroy tree each time and reinsert all circles, taking care to reset
        # collision status (HIT) and whether stored by interior node (MULTIPLE).
        nodes = self.tree.root.preorder()
        self.tree = QuadTree(Region(0, 0, 512, 512))
        for n in nodes:
            for c in n.circles:
                c[HIT] = False

                dx = dy = 0
                if c[X] - c[RADIUS] + c[DX] <= self.tree.region.x_min:
                    c[DX] = -c[DX]
                elif c[X] + c[RADIUS] + c[DX] >= self.tree.region.x_max:
                    c[DX] = -c[DX]
                else:
                    c[X] = c[X] + c[DX]
                    dx = c[DX]

                if c[Y] - c[RADIUS] + c[DY] <= self.tree.region.y_min:
                    c[DY] = -c[DY]
                elif c[Y] + c[RADIUS] + c[DY] >= self.tree.region.y_max:
                    c[DY] = -c[DY]
                else:
                    c[Y] = c[Y] + c[DY]
                    dy = c[DY]

                # Update hit status for all colliding circles, based on
                # newly added circle.
                for circ in self.tree.collide(c):
                    circ[HIT] = True
                    self.canvas.itemconfig(circ[ID], fill='red')
                    c[HIT] = True

                self.tree.add(c)

                # Update visual for circle, either creating anew or moving. Fill color
                # is based on whether colliding (RED) or stored in interior node (BLUE)
                markColor = 'black'
                if c[MULTIPLE]: markColor = 'blue'
                if c[HIT]: markColor = 'red'

                if c[ID] is None:
                    c[ID] = self.canvas.create_oval(
                        c[X] - c[RADIUS],
                        self.toTk(c[Y]) - c[RADIUS],
                        c[X] + c[RADIUS],
                        self.toTk(c[Y]) + c[RADIUS],
                        fill=markColor)
                else:
                    # dy is Cartesian, but tk is opposite in y-direction
                    self.canvas.move(c[ID], dx, -dy)
                    self.canvas.itemconfig(c[ID], fill=markColor)

        # recreate entire visualization by deleting lines and moving circles
        self.canvas.delete(LINE)
        self.visit(self.tree.root)
        self.viz.plot(self.tree.root)
    def updateLocations(self):
        """Move all circles, reconstruct QuadTree and repaint."""
        if not self.paused:
            self.master.after(frameDelay, self.updateLocations)

        if self.tree.root is None: return

        # Destroy tree each time and reinsert all circles, taking care to reset
        # collision status (HIT) and whether stored by interior node (MULTIPLE).
        nodes = self.tree.root.preorder()
        self.tree = QuadTree(Region(0, 0, 512, 512))
        for n in nodes:
            for c in n.circles:
                c[HIT] = False

                dx = dy = 0
                if c[X] - c[RADIUS] + c[DX] <= self.tree.region.x_min:
                    c[DX] = -c[DX]
                elif c[X] + c[RADIUS] + c[DX] >= self.tree.region.x_max:
                    c[DX] = -c[DX]
                else:
                    c[X] = c[X] + c[DX]
                    dx = c[DX]

                if c[Y] - c[RADIUS] + c[DY] <= self.tree.region.y_min:
                    c[DY] = -c[DY]
                elif c[Y] + c[RADIUS] + c[DY] >= self.tree.region.y_max:
                    c[DY] = -c[DY]
                else:
                    c[Y] = c[Y] + c[DY]
                    dy = c[DY]

                # Update hit status for all colliding circles, based on
                # newly added circle.
                for circ in self.tree.collide(c):
                    circ[HIT] = True
                    self.canvas.itemconfig(circ[ID], fill='red')
                    c[HIT] = True

                self.tree.add(c)

                # Update visual for circle, either creating anew or moving. Fill color
                # is based on whether colliding (RED) or stored in interior node (BLUE)
                markColor = 'black'
                if c[MULTIPLE]: markColor = 'blue'
                if c[HIT]: markColor = 'red'

                if c[ID] is None:
                    c[ID] = self.canvas.create_oval(
                        c[X] - c[RADIUS],
                        self.toTk(c[Y]) - c[RADIUS],
                        c[X] + c[RADIUS],
                        self.toTk(c[Y]) + c[RADIUS],
                        fill=markColor)
                else:
                    # dy is Cartesian, but tk is opposite in y-direction
                    self.canvas.move(c[ID], dx, -dy)
                    self.canvas.itemconfig(c[ID], fill=markColor)

        # recreate entire visualization by deleting lines and moving circles
        self.canvas.delete(LINE)
        self.visit(self.tree.root)
        self.viz.plot(self.tree.root)
Exemplo n.º 16
0
class TestQuadMethods(unittest.TestCase):
    def setUp(self):
        self.qt = QuadTree(Region(0, 0, 1024, 1024))

        self.qt.add([22, 40, 10, False, False])
        self.qt.add([13, 59, 20, False, False])
        self.qt.add([57, 37, 30, False, False])
        self.qt.add([43, 21, 20, False, False])
        self.qt.add([33, 11, 10, False, False])

    def tearDown(self):
        self.qt = None

    def test_basic(self):
        self.assertTrue([43, 21, 20] in self.qt)
        self.assertFalse([21, 43, 11] in self.qt)

        # already present.
        self.assertFalse(self.qt.add([33, 11, 10, False, False]))

    def test_remove_less_than_four(self):
        self.qt = QuadTree(Region(0, 0, 1024, 1024))

        self.qt.add([22, 40, 10, False, False])
        self.qt.add([13, 59, 20, False, False])
        self.qt.add([57, 37, 30, False, False])

        self.assertTrue(self.qt.remove([57, 37, 30]))
        self.assertTrue(self.qt.remove([13, 59, 20]))
        self.assertTrue(self.qt.remove([22, 40, 10]))

        self.assertFalse(self.qt.remove([57, 37, 30]))
        self.assertFalse(self.qt.remove([13, 59, 20]))
        self.assertFalse(self.qt.remove([22, 40, 10]))

    def test_remove(self):
        self.assertTrue(self.qt.remove([57, 37, 30]))
        ct = 0
        for _ in self.qt:
            ct += 1
        self.assertEqual(4, ct)

        self.assertTrue(self.qt.remove([13, 59, 20]))

    def test_iteration(self):
        ct = 0
        for _ in self.qt:
            ct += 1
        self.assertEqual(5, ct)