Ejemplo n.º 1
0
class Interface():
    # Constructor
    def __init__(self, resolution=Dimensions(1001, 601), calcThread=None):
        # Declare constants
        # General
        self.fpsLimit = 60
        self.simRunning = False
        self.calcThread = calcThread
        # Section sizes
        self.control_width = 200
        # Key presses
        self.L_SHIFT = 1
        self.R_SHIFT = 2
        self.L_CTRL = 64
        self.R_CTRL = 128
        self.L_ALT = 256
        self.R_ALT = 512

        # Declare variables
        self.populationLimit = 3
        self.populationMin = 2
        self.generation = 0

        # Declare mouse event flags
        self.processMouse = False
        self.multiCellDrag = False
        self.multiCellDragState = True
        self.mouseHeld = False
        self.mouseRepeatDelayed = False
        self.mouseClickTime = 0

        # Initialize pygame window
        pygame.init()
        self.window = pygame.display.set_mode(resolution, pygame.RESIZABLE)
        pygame.display.set_caption("Conway's Game of Life")

        # Create clock to limit FPS
        self.fpsClock = pygame.time.Clock()

        # Enable key and mouse hold repeating and set limits
        pygame.key.set_repeat(500, 75)
        self.mouseRepeat = (500, 75)

        # Create the initial grid
        self.grid = Grid(
            Dimensions(self.window.get_rect().width - self.control_width,
                       self.window.get_rect().height), Position(0, 0))

        # Create control section
        self.controls = Controls(
            Dimensions(self.control_width,
                       self.window.get_rect().height),
            Position(self.grid.width, 0), self)

    ###########################################
    # MAIN INTERFACE LOOP METHODS             #
    ###########################################

    def update(self):
        # Process any mouse/keyboard events
        if not self.processEvents():
            # Tell calcThread to exit
            self.calcThread.killswitch = True
            return False

        # Get list of objects to update
        updates = self.draw()

        # Update window
        pygame.display.update(updates)

        # Limit FPS
        self.fpsClock.tick(self.fpsLimit)

        return True

    def processEvents(self):
        click_pos = (-1, -1)

        # Get pygame events to see if/what key is pressed
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                return False
            elif event.type == MOUSEBUTTONDOWN:
                if event.button <= 3:  # Left, middle, or right click get special processing
                    # Turn mouse processing on
                    self.processMouse = True
                    # Pretend like the delay has gone through so
                    # the first click will process
                    self.mouseRepeatDelayed = True
                    # Set a very long time in the future so the interval will process
                    self.mouseClickTime = time() + time()
                elif event.button == 4:  # Scroll wheel up
                    self.zoomIn(pygame.mouse.get_pos())  # Zoom in
                elif event.button == 5:  # Scroll wheel down
                    self.zoomOut(pygame.mouse.get_pos())  # Zoom out
                else:
                    pass
            elif event.type == MOUSEBUTTONUP:
                # Turn mouse processing off
                self.processMouse = False
                # Reset mouse flags
                self.multiCellDrag = False
                self.mouseHeld = False
            elif event.type == MOUSEMOTION:
                if self.processMouse:
                    self.multiCellDrag = True
            elif event.type == KEYDOWN:
                mods = pygame.key.get_mods()  # Get modifier keys
                if event.key == K_LEFT:
                    if mods == self.L_SHIFT:
                        self.popLimitDown()
                    elif mods == self.L_CTRL:
                        self.popMinDown()
                    else:
                        self.speedDown()
                elif event.key == K_RIGHT:
                    if mods == self.L_SHIFT:
                        self.popLimitUp()
                    elif mods == self.L_CTRL:
                        self.popMinUp()
                    else:
                        self.speedUp()
                elif event.key == K_SPACE:
                    self.pause()
                elif event.key == K_RETURN:
                    self.stepForward()
                else:
                    pass
            elif event.type == VIDEORESIZE:
                self.resize(event.dict['size'])
            else:
                pass

        if self.processMouse:
            self.processMouseEvents()
        return True

    def processMouseEvents(self):
        # Get current time
        currentTime = time()

        ###########################################################
        # Process mouse events that only happen once              #
        ###########################################################

        if not self.mouseHeld:
            if pygame.mouse.get_pressed()[0]:  # Left click
                if self.controls.collidepoint(pygame.mouse.get_pos()):
                    pass
            if pygame.mouse.get_pressed()[1]:  # Middle click
                pass
            if pygame.mouse.get_pressed()[2]:  # Right click
                pass

        ###########################################################
        # Process instantly repeated mouse events                 #
        ###########################################################

        if pygame.mouse.get_pressed()[0]:  # Left click
            if self.grid.collidepoint(pygame.mouse.get_pos()):
                if not self.mouseHeld:
                    # Activate the cell to change its alive status
                    # We want to be able to drag its new status onto other cells
                    self.multiCellDragState = self.grid.clickCell(
                        self.grid.getCell(pygame.mouse.get_pos()))
                elif self.multiCellDrag:
                    # If we are trying to bring multiple cells to life,
                    # then check the clicked cell's alive state before
                    # clicking on it.
                    if self.grid.getCell(pygame.mouse.get_pos()
                                         ).alive != self.multiCellDragState:
                        self.grid.clickCell(
                            self.grid.getCell(pygame.mouse.get_pos()))
        if pygame.mouse.get_pressed()[1]:  # Middle click
            pass
        if pygame.mouse.get_pressed()[2]:  # Right click
            pass

        ###########################################################
        # Process mouse events that repeat, but need delay        #
        ###########################################################

        # See if delay is needed
        if self.mouseHeld:
            # Has enough time passed since the first delay?
            if (not self.mouseRepeatDelayed) and (
                    currentTime - self.mouseClickTime <
                (self.mouseRepeat[0] / 1000)):
                return

            # Enough time has passed since the first delay, skip delay check next time
            self.mouseRepeatDelayed = True

            # Has enough time passed for another repeat to happen?
            if currentTime - self.mouseClickTime < (self.mouseRepeat[1] /
                                                    1000):
                return

        # Process events for the first time and
        # after the appropriate delay interval
        if pygame.mouse.get_pressed()[0]:  # Left click
            pass
        if pygame.mouse.get_pressed()[1]:  # Middle click
            pass
        if pygame.mouse.get_pressed()[2]:  # Right click
            pass

        ###########################################################
        # Turn on mouseHeld flag so the next time this processes  #
        # it will know that we've been holding it down            #
        # and how long we've been holding it down                 #
        ###########################################################

        self.mouseHeld = True
        self.mouseClickTime = currentTime

    ###########################################
    # DRAWING AND SIZE MANIPULATION METHODS   #
    ###########################################

    def draw(self):
        # Set list of rects that will be updated and returned
        updateList = []

        # Draw grid
        updateList.extend(self.grid.draw(self.window))

        # See if any cells that need draw updates collide with
        # any controls, and set those controls to redraw as well
        self.controls.checkGridOverlap(updateList)

        # Draw control interface
        updateList.extend(self.controls.draw(self.window))

        # Return list of rects to be updated
        return updateList

    def resize(self, size):
        self.window = pygame.display.set_mode(size, pygame.RESIZABLE)
        self.grid.resize(
            Dimensions(self.window.get_rect().width - self.control_width,
                       self.window.get_rect().height), Position(0, 0))
        self.controls.resize(
            Dimensions(self.control_width,
                       self.window.get_rect().height),
            Position(self.grid.width, 0))

    def zoomIn(self, pos, sizeChange=2):
        ##############################################
        # Get necessary constants for error checking #
        ##############################################

        # Find the new size of the cell
        newSize = Dimensions(self.grid.cellSize.width + sizeChange,
                             self.grid.cellSize.height + sizeChange)

        # Get the cell that was zoomed in on
        zoomedCell = self.grid.getCell(pos)

        ############################################
        # Make sure that this zoom action is valid #
        ############################################

        # Make sure we're not going outside of size limits
        if (newSize <= self.grid.minSize) or (newSize >= self.grid.maxSize):
            return
        # If no cell was zoomed in on, quit now
        if zoomedCell == None:
            return

        ########################################
        # Calculate new cell position based on #
        # current mouse location               #
        ########################################

        # Find position of cursor relative to the cell (should be from 0 to cell width)
        relativePos = Position(pos[0] - zoomedCell.left,
                               pos[1] - zoomedCell.top)
        # Find the ratio between the relative pos and the cell size
        posRatio = Position(relativePos.left / zoomedCell.width,
                            relativePos.top / zoomedCell.height)
        # Calculate the new position of the cell.
        newPos = Position(zoomedCell.left - (posRatio.left * sizeChange),
                          zoomedCell.top - (posRatio.top * sizeChange))

        #######################################
        # Move and resize all cells in grid   #
        # based on zoomed cell's new location #
        #######################################

        # Find the index of the cell that was zoomed on
        index = self.grid.getCellIndex(
            zoomedCell
        )  # Returns a tuple with the x/y position in the cell array

        # Using the index, we can calculate how far away the cells should be from the zoomed cell.
        # While we're iterating through, we also resize each cell (so we only iterate once).
        for x in range(len(self.grid.cells)):
            for y in range(len(self.grid.cells[x])):
                self.grid.cells[x][y].resize(newSize)
                self.grid.cells[x][y].move(
                    Position(
                        newPos.left - ((newSize.width - 1) * (index.x - x)),
                        newPos.top - ((newSize.height - 1) * (index.y - y))))

                # This is supposed to fix a minor cell-wall-width variation I only see in the top row or left column
                # 2017-07-17 Kevin T. Berstene
                if self.grid.cells[x][y].x <= 0:
                    self.grid.cells[x][y].move(
                        Position(self.grid.cells[x][y].x - 1,
                                 self.grid.cells[x][y].y))
                if self.grid.cells[x][y].y <= 0:
                    self.grid.cells[x][y].move(
                        Position(self.grid.cells[x][y].x,
                                 self.grid.cells[x][y].y - 1))

        # Set new grid cellSize
        # This needs to be done before adding cells
        # so any added cells will be of the right size
        self.grid.cellSize = newSize

        ########################################################
        # Add or remove cells that have moved on or off screen #
        ########################################################

        self.grid.autoAddRemoveCells()

        ############################################
        # Schedule controls and grid for redrawing #
        ############################################

        self.controls.redrawAll()
        self.grid.redrawAll()

    def zoomOut(self, pos, sizeChange=2):
        # Do everything zoom out does,
        # but with negative sizeChange
        self.zoomIn(pos, 0 - sizeChange)

    ##################################################
    # SIMULATION CONTROLS AND PARAMETER MANIPULATION #
    ##################################################

    def pause(self):
        self.simRunning = not (self.simRunning)
        self.controls.updateStatusDisplay(self.simRunning)

    def popLimitDown(self):
        if self.populationLimit > 1:
            self.populationLimit -= 1
            self.controls.updatePopLimitDisplay(self.populationLimit)

    def popLimitUp(self):
        if self.populationLimit < 8:
            self.populationLimit += 1
            self.controls.updatePopLimitDisplay(self.populationLimit)

    def popMinDown(self):
        if self.populationMin > 1:
            self.populationMin -= 1
            self.controls.updatePopMinDisplay(self.populationMin)

    def popMinUp(self):
        if self.populationMin < 8:
            self.populationMin += 1
            self.controls.updatePopMinDisplay(self.populationMin)

    def reset(self):
        self.__init__(
            Dimensions(self.window.get_rect().width,
                       self.window.get_rect().height), self.calcThread)
        self.calcThread.__init__(self)
        self.grid.indexGrid()

    def setCalcThread(self, calcThread):
        self.calcThread = calcThread
        self.controls.updateSpeedDisplay(self.calcThread.speed)

    def speedDown(self):
        if (self.calcThread.speed > 1):
            self.calcThread.speed -= 1
            self.controls.updateSpeedDisplay(self.calcThread.speed)

    def speedUp(self):
        self.calcThread.speed += 1
        self.controls.updateSpeedDisplay(self.calcThread.speed)

    def stepForward(self):
        if self.simRunning:
            self.pause()
        self.calcThread.calc()
        return self.simRunning
Ejemplo n.º 2
0
class Waves(object):
    def __init__(self):
        pygame.init()

        self.outputs = Outputs()
        self.stream = Stream(channels=1,
                             sample_rate=60 * 10**3,
                             sample_size=2**11)

        self.mouse_frequency = 0.0

        # visual params
        self.background_color = pygame.Color(50, 50, 50)
        self.colorA = pygame.Color("#ff0000")
        self.colorB = pygame.Color("#0000ff")
        self.num_bars = self.outputs.get_divisor()

        # surface params
        self.height = 1000
        self.dimensions = numpy.array([self.outputs.get_width(), self.height])
        self.surface_flags = pygame.HWSURFACE | pygame.DOUBLEBUF
        self.surface = pygame.display.set_mode(self.dimensions,
                                               self.surface_flags)
        self.time_surface = pygame.Surface(self.dimensions //
                                           numpy.array([1, 2]))
        self.freq_surface = pygame.Surface(self.dimensions //
                                           numpy.array([1, 2]))
        self.control_surface = pygame.Surface(self.dimensions // 2)
        self.control_surface.set_colorkey(self.background_color)

        self.controls = Controls(self.control_surface)

        self.sliders = {
            'pull':
            Slider(self.control_surface,
                   pygame.Rect(300, 46, 100, 10),
                   10,
                   15,
                   value=0.5),
            'smooth':
            Slider(self.control_surface,
                   pygame.Rect(300, 66, 100, 10),
                   10,
                   15,
                   value=0.5)
        }

        # smoothing history array
        self.t_history = numpy.full(self.num_bars, 0.5)
        self.f_history = numpy.full(self.num_bars, 0.0)

    def get_samples(self):
        format = '<{}h'.format(self.stream.sample_size)
        byte_string = self.stream.read(self.stream.sample_size)
        return list(map(util.normalize, struct.unpack(format, byte_string)))

    def draw_time_bars(self, samples, surface):
        width, height = surface.get_size()
        bar_width = width / self.num_bars

        s = self.sliders['smooth'].value
        for i in range(self.num_bars):
            power_i = samples[i]
            power_s = self.t_history[i] * s + power_i * (1 - s)
            power = self.t_history[i] = power_s

            bar_height = power * height
            top = height - bar_height
            left = i * bar_width
            rect = (left, top, bar_width, 5)  #bar_height)

            color = util.gradient(power, self.colorA, self.colorB)
            pygame.draw.rect(surface, color, rect)

    def draw_freq_bars(self, samples, surface):
        width, height = surface.get_size()
        y_max = self.stream.sample_size // 2
        bar_width = width / self.num_bars

        yf = numpy.log(numpy.abs(numpy.fft.fft(samples)) +
                       1) / numpy.log(y_max)

        s = self.sliders['smooth'].value

        pull = 1 - self.sliders['pull'].value
        g = (self.num_bars - 1) * (self.stream.sample_size // 2 - 1) * pull
        v, h = util.shift_inverse_consts(0, 1, self.num_bars - 1,
                                         self.stream.sample_size // 2 - 1, g)

        for x in range(self.num_bars):
            y = util.shift_inverse(x, g, v, h)

            power_i = yf[int(y)]
            power_s = self.f_history[x] * s + power_i * (1 - s)
            power = self.f_history[x] = power_s
            if power > 1.0:
                power = 1.0

            bar_height = power * height
            top = height - bar_height
            left = x * bar_width
            rect = (left, top, bar_width, bar_height)
            color = util.gradient(power, self.colorA, self.colorB)
            pygame.draw.rect(surface, color, rect)

    def resize_bars(self):
        self.num_bars = self.outputs.get_divisor()
        self.t_history.resize(self.num_bars)
        self.f_history.resize(self.num_bars)

    def resize(self):
        width = self.outputs.get_width()
        height = self.height
        self.time_surface = pygame.Surface((width, height // 2))
        self.freq_surface = pygame.Surface((width, height // 2))
        self.surface = pygame.display.set_mode((width, height),
                                               self.surface_flags)
        self.resize_bars()

    def process_key(self, key):
        HEIGHT_DELTA = 100
        HEIGHT_MIN = 300

        SIZE_MIN = 1

        RATE_DELTA = 1000
        RATE_MIN = 0

        mods = pygame.key.get_mods()
        shift = mods & pygame.KMOD_SHIFT

        if key == ord('b'):
            if shift:
                self.outputs.next_divisor()
            else:
                self.outputs.prev_divisor()
            self.resize_bars()

        if key == ord('h'):
            if shift:
                self.height += HEIGHT_DELTA
            elif self.height > HEIGHT_MIN:
                self.height -= HEIGHT_DELTA
            self.resize()

        if key == ord('n'):
            k = 2 if shift else 0.5
            if self.stream.sample_size > SIZE_MIN:
                self.stream.sample_size *= k

        if key == ord('r'):
            k = 1 if shift else -1
            if self.stream.sample_rate > RATE_MIN:
                self.stream.sample_rate += k * RATE_DELTA

        if key == ord('w'):
            if shift:
                self.outputs.next_width()
            else:
                self.outputs.prev_width()
            self.resize()

    def process_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.exit()

            if event.type == pygame.KEYDOWN:
                self.process_key(event.key)

            if event.type == pygame.MOUSEMOTION:
                x = event.pos[0]

                R = self.stream.sample_rate
                N = self.stream.sample_size
                pull = 1 - self.sliders['pull'].value
                g = (self.num_bars - 1) * (N // 2 - 1) * pull
                v, h = util.shift_inverse_consts(0, 1, self.num_bars - 1,
                                                 N // 2 - 1, g)

                bar_width = self.outputs.get_width() / self.num_bars
                bar_index = math.floor(x / bar_width)
                self.mouse_frequency = util.shift_inverse(
                    bar_index, g, v, h) * (R / 2) / (N / 2 - 1)

                for slider in self.sliders.values():
                    if slider.moving:
                        slider.set_value(x)

            if event.type == pygame.MOUSEBUTTONDOWN:
                for slider in self.sliders.values():
                    if slider.get_handle_rect().collidepoint(event.pos):
                        slider.moving = True

            if event.type == pygame.MOUSEBUTTONUP:
                for slider in self.sliders.values():
                    slider.moving = False

    def exit(self):
        self.stream.close()
        pygame.display.quit()
        pygame.quit()
        sys.exit(0)

    def loop(self):
        self.process_events()

        surfaces = [self.time_surface, self.freq_surface, self.control_surface]
        for surface in surfaces:
            surface.fill(self.background_color)

        samples = self.get_samples()

        self.controls.draw(self.stream.sample_rate, self.stream.sample_size,
                           self.sliders['pull'].value,
                           self.sliders['smooth'].value,
                           self.outputs.get_width(), self.num_bars,
                           self.mouse_frequency)

        for slider in self.sliders.values():
            slider.draw()

        self.draw_time_bars(samples, self.time_surface)
        self.draw_freq_bars(samples, self.freq_surface)

        self.surface.blit(self.time_surface, (0, 0))
        self.surface.blit(self.freq_surface, (0, self.height // 2))
        self.surface.blit(self.control_surface, (0, 0))

        pygame.display.flip()
Ejemplo n.º 3
0
class Interface():
	# Constructor
	def __init__(self, resolution = Dimensions(1001, 601), calcThread=None):
		# Declare constants
			# General
		self.fpsLimit = 60
		self.simRunning = False
		self.calcThread = calcThread
			# Section sizes
		self.control_width = 200
			# Key presses
		self.L_SHIFT = 1
		self.R_SHIFT = 2
		self.L_CTRL = 64
		self.R_CTRL = 128
		self.L_ALT = 256
		self.R_ALT = 512
	
		# Declare variables
		self.populationLimit = 3
		self.populationMin = 2
		self.generation = 0
		
		# Initialize pygame window
		pygame.init()
		self.window = pygame.display.set_mode(resolution, pygame.RESIZABLE)
		pygame.display.set_caption("Conway's Game of Life")
		
		# Create clock to limit FPS
		self.fpsClock = pygame.time.Clock()

		# Enable key hold repeating and set limits
		pygame.key.set_repeat(500,75)
		
		# Create the initial grid
		self.grid = Grid(Dimensions(self.window.get_rect().width - self.control_width, self.window.get_rect().height), Position(0, 0))
		
		# Create control section
		self.controls = Controls(Dimensions(self.control_width, self.window.get_rect().height),
								Position(self.grid.rect.width, 0), self)


	def setCalcThread(self,calcThread):
		self.calcThread = calcThread
		self.controls.updateSpeedDisplay(self.calcThread.speed)
		
	def update(self):
		# Process any mouse/keyboard events
		if not self.processEvents():
			return False

		# Draw objects
		self.draw()

		# Update window
		pygame.display.update()
		
		# Limit FPS
		self.fpsClock.tick(self.fpsLimit)

		return True
		
	def processEvents(self):
		click_pos=(-1,-1)

		# Get pygame events to see if/what key is pressed
		for event in pygame.event.get():
			if event.type == QUIT:
				pygame.quit()
				return False
			elif event.type == MOUSEBUTTONDOWN:
				if event.button == 1:
					self.controls.collidepoint(pygame.mouse.get_pos())
					self.grid.collidepoint(pygame.mouse.get_pos())
			elif event.type == KEYDOWN:
				mods = pygame.key.get_mods()# Get modifier keys
				if event.key == K_LEFT:
					if mods == self.L_SHIFT:
						self.popLimitDown()
					elif mods == self.L_CTRL:
						self.popMinDown()
					else:
						self.speedDown()
				elif event.key == K_RIGHT:
					if mods == self.L_SHIFT:
						self.popLimitUp()
					elif mods == self.L_CTRL:
						self.popMinUp()
					else:
						self.speedUp()
				elif event.key == K_SPACE:
					self.pause()
				elif event.key == K_RETURN:
					self.stepForward()
				else:
					pass
			elif event.type == VIDEORESIZE:
				self.resize(event.dict['size'])
			else:
				pass
		return True
		
	
	def draw(self):
		# Set background as white
		self.window.fill(pygame.Color("white"))

		# Draw grid
		self.grid.draw(self.window)

		# Draw control interface
		self.controls.draw(self.window)
		
	def resize(self, size):
		# Resize window
		self.window = pygame.display.set_mode(size, pygame.RESIZABLE)
		
		# Resize grid
		if (self.window.get_rect().width < self.control_width):
			# Grid size is less than zero, so set it to 2x2 pixels instead
			# (which should create a single cell).
			# It will get covered by the controls, anyway.
			# If grid is 0x0 and the calcThread starts, it will crash
			self.grid.resize(Dimensions(2, 2), Position(0, 0))
		else:
			self.grid.resize(Dimensions(self.window.get_rect().width - self.control_width, self.window.get_rect().height), Position(0, 0))
		
		# Resize controls
		self.controls.resize(Dimensions(self.control_width, self.window.get_rect().height), Position(self.grid.rect.width, 0))
	
	def reset(self):
		self.__init__(Dimensions(self.window.get_rect().width, self.window.get_rect().height), self.calcThread)
		self.calcThread.__init__(self)

	def pause(self):
		self.simRunning = not(self.simRunning)
		self.controls.updateStatusDisplay(self.simRunning)

	def speedUp(self):
		self.calcThread.speed += 1
		self.controls.updateSpeedDisplay(self.calcThread.speed)
	
	def speedDown(self):
		if (self.calcThread.speed > 1):
			self.calcThread.speed -= 1
			self.controls.updateSpeedDisplay(self.calcThread.speed)
	
	def popLimitUp(self):
		if self.populationLimit < 8:
			self.populationLimit += 1
			self.controls.updatePopLimitDisplay(self.populationLimit)
	
	def popLimitDown(self):
		if self.populationLimit > 1:
			self.populationLimit -= 1
			self.controls.updatePopLimitDisplay(self.populationLimit)
			
	def popMinUp(self):
		if self.populationMin < 8:
			self.populationMin += 1
			self.controls.updatePopMinDisplay(self.populationMin)
			
	def popMinDown(self):
		if self.populationMin > 1:
			self.populationMin -= 1
			self.controls.updatePopMinDisplay(self.populationMin)
	
	def stepForward(self):
		if self.simRunning:
			self.pause()
		self.calcThread.calc()