redraw speedup though use of generator

This commit is contained in:
Richard Feistenauer 2019-01-05 17:38:21 +01:00
parent 68ac036203
commit f26a74d224
4 changed files with 72 additions and 65 deletions

View File

@ -2,11 +2,12 @@
import random import random
from cellular_automaton.cellular_automaton import CellularAutomaton from cellular_automaton.cellular_automaton import CellularAutomaton, CellularAutomatonEvolver
from cellular_automaton.ca_rule import Rule from cellular_automaton.ca_rule import Rule
from cellular_automaton.ca_neighborhood import MooreNeighborhood, EdgeRule from cellular_automaton.ca_neighborhood import MooreNeighborhood, EdgeRule
from cellular_automaton.ca_display import PyGameFor2D from cellular_automaton.ca_display import PyGameFor2D
from cellular_automaton.ca_cell_state import CellState from cellular_automaton.ca_cell_state import CellState
from cellular_automaton.ca_grid import Grid
class TestRule(Rule): class TestRule(Rule):
@ -43,6 +44,8 @@ class MyStatus(CellState):
if __name__ == "__main__": if __name__ == "__main__":
random.seed(1000) random.seed(1000)
rule = TestRule() rule = TestRule()
ca = CellularAutomaton([400, 400], MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS), rule) ca = CellularAutomaton(Grid([400, 400], MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS)),
ca_window = PyGameFor2D([1000, 800], ca, 5) rule)
ca_evolver = CellularAutomatonEvolver(2)
ca_window = PyGameFor2D([1000, 800], ca, ca_evolver, 5)
ca_window.main_loop() ca_window.main_loop()

View File

@ -2,49 +2,45 @@ import pygame
import time import time
import operator import operator
from cellular_automaton.cellular_automaton import CellularAutomaton from cellular_automaton.cellular_automaton import CellularAutomaton, CellularAutomatonEvolver
class _DisplayInfo:
def __init__(self, grid_size, grid_pos, cell_size, screen):
self.grid_size = grid_size
self.grid_pos = grid_pos
self.cell_size = cell_size
self.screen = screen
class DisplayFor2D: class DisplayFor2D:
def __init__(self, grid_rect: list, cellular_automaton: CellularAutomaton, screen): def __init__(self, grid_rect: list, cellular_automaton: CellularAutomaton, screen):
self.grid_size = grid_rect[-2:]
self.grid_pos = grid_rect[:2]
self._cellular_automaton = cellular_automaton self._cellular_automaton = cellular_automaton
self.screen = screen cell_size = self._calculate_cell_display_size(grid_rect[-2:])
self._display_info = _DisplayInfo(grid_rect[-2:], grid_rect[:2], cell_size, screen)
self.cell_size = self._calculate_cell_display_size()
self._surfaces_to_update = []
def set_cellular_automaton(self, cellular_automaton): def set_cellular_automaton(self, cellular_automaton):
self._cellular_automaton = cellular_automaton self._cellular_automaton = cellular_automaton
def _redraw_cellular_automaton(self): def _redraw_cellular_automaton(self):
for cell in self._cellular_automaton.grid.get_cells().values(): pygame.display.update(list(_cell_redraw_rectangles(self._cellular_automaton.grid.get_cells().values(),
self._redraw_cell(cell) self._cellular_automaton.evolution_iteration_index,
pygame.display.update(self._surfaces_to_update) self._display_info)))
self._surfaces_to_update = []
def _redraw_cell(self, cell): def _calculate_cell_display_size(self, grid_size):
if cell.is_set_for_redraw:
cell_color = cell.state.get_state_draw_color(self._cellular_automaton.get_iteration_index())
surface_pos = self._calculate_cell_position(cell)
surface_pos = list(map(operator.add, surface_pos, self.grid_pos))
self._surfaces_to_update.append(self.screen.fill(cell_color, (surface_pos, self.cell_size)))
cell.is_set_for_redraw = False
def _calculate_cell_position(self, cell):
return list(map(operator.mul, self.cell_size, cell.coordinate))
def _calculate_cell_display_size(self):
grid_dimension = self._cellular_automaton.grid.get_dimension() grid_dimension = self._cellular_automaton.grid.get_dimension()
return list(map(operator.truediv, self.grid_size, grid_dimension)) return list(map(operator.truediv, grid_size, grid_dimension))
class PyGameFor2D: class PyGameFor2D:
def __init__(self, windows_size: list, cellular_automaton: CellularAutomaton, ca_iterations_per_draw): def __init__(self,
windows_size: list,
cellular_automaton: CellularAutomaton,
cellular_automaton_evolver: CellularAutomatonEvolver,
ca_iterations_per_draw):
self._window_size = windows_size self._window_size = windows_size
self._cellular_automaton = cellular_automaton self._cellular_automaton = cellular_automaton
self._cellular_automaton_evolver = cellular_automaton_evolver
self._ca_steps_per_draw = ca_iterations_per_draw self._ca_steps_per_draw = ca_iterations_per_draw
pygame.init() pygame.init()
@ -69,7 +65,7 @@ class PyGameFor2D:
while running: while running:
time_ca_start = time.time() time_ca_start = time.time()
self._cellular_automaton.evolve_x_times(self._ca_steps_per_draw) self._cellular_automaton_evolver.evolve_x_times(self._cellular_automaton, self._ca_steps_per_draw)
time_ca_end = time.time() time_ca_end = time.time()
self.ca_display._redraw_cellular_automaton() self.ca_display._redraw_cellular_automaton()
time_ds_end = time.time() time_ds_end = time.time()
@ -79,3 +75,16 @@ class PyGameFor2D:
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
running = False running = False
def _cell_redraw_rectangles(cells, evolution_index, display_info):
for cell in cells:
if cell.is_set_for_redraw:
cell_color = cell.state.get_state_draw_color(evolution_index)
cell_pos = _calculate_cell_position(display_info.cell_size, cell)
surface_pos = list(map(operator.add, cell_pos, display_info.grid_pos))
yield display_info.screen.fill(cell_color, (surface_pos, display_info.cell_size))
cell.is_set_for_redraw = False
def _calculate_cell_position(cell_size, cell):
return list(map(operator.mul, cell_size, cell.coordinate))

View File

@ -6,11 +6,13 @@ class Grid:
def __init__(self, dimension: list, neighborhood: Neighborhood): def __init__(self, dimension: list, neighborhood: Neighborhood):
self._dimension = dimension self._dimension = dimension
self._cells = {} self._cells = {}
self._neighborhood = neighborhood self._active_cells = {}
self._init_cells(neighborhood)
def _init_cells(self, neighborhood):
self._create_cells() self._create_cells()
self._set_cell_neighbours() self._set_cell_neighbours(neighborhood)
self._active_cells = self._cells.copy() self._active_cells = self._cells.copy()
self._set_all_cells_active() self._set_all_cells_active()
@ -68,10 +70,10 @@ class Grid:
new_cod = coordinate + [cell_index] new_cod = coordinate + [cell_index]
recursion_method(dimension_index + 1, new_cod) recursion_method(dimension_index + 1, new_cod)
def _set_cell_neighbours(self): def _set_cell_neighbours(self, neighborhood):
for cell in self._cells.values(): for cell in self._cells.values():
neighbours_coordinates = self._neighborhood.calculate_cell_neighbor_coordinates(cell.coordinate, neighbours_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.coordinate,
self._dimension) self._dimension)
cell.neighbours = list(map(self._get_cell_by_coordinate, neighbours_coordinates)) cell.neighbours = list(map(self._get_cell_by_coordinate, neighbours_coordinates))
def _get_cell_by_coordinate(self, coordinate): def _get_cell_by_coordinate(self, coordinate):

View File

@ -1,61 +1,54 @@
from cellular_automaton.ca_grid import Grid from cellular_automaton.ca_grid import Grid
from cellular_automaton.ca_rule import Rule from cellular_automaton.ca_rule import Rule
from cellular_automaton.ca_neighborhood import Neighborhood
class CellularAutomaton: class CellularAutomaton:
def __init__(self, dimension: list, neighborhood: Neighborhood, evolution_rule: Rule=None): def __init__(self, grid: Grid, evolution_rule: Rule):
self.grid = Grid(dimension, neighborhood) self.grid = grid
self.evolution_rule = evolution_rule self.evolution_rule = evolution_rule
self.iteration = 0 self.evolution_iteration_index = 0
self.test_number = 0
def set_rule(self, rule: Rule):
""" Set new evolution rule.
:param rule:
:return:
"""
self.evolution_rule = rule
def get_iteration_index(self): class CellularAutomatonEvolver:
""" Get the count of evolution cycles done. def __init__(self, process_count: int = 1):
:return: Evolution steps done. self.__processes = process_count
""" self.__cellular_automaton = None
return self.iteration
def evolve_x_times(self, evolutions: int): def evolve_x_times(self, cellular_automaton: CellularAutomaton, evolution_steps: int):
""" Evolve all cells for x time steps. """ Evolve all cells for x time steps.
:param evolutions: the count of evolutions done. :param evolution_steps: the count of evolutions done.
:return: True if all cells are inactive :return: True if all cells are inactive
""" """
for evo in range(evolutions): for evo in range(evolution_steps):
finished = self.evolve() finished = self.evolve(cellular_automaton)
if finished: if finished:
return True return True
return False return False
def evolve(self): def evolve(self, cellular_automaton: CellularAutomaton):
""" Evolves all active cells for one time step. """ Evolves all active cells for one time step.
:return: True if all cells are inactive. :return: True if all cells are inactive.
""" """
self.__cellular_automaton = cellular_automaton
if self._is_evolution_finished(): if self._is_evolution_finished():
return True return True
else: else:
self.iteration += 1 cellular_automaton.evolution_iteration_index += 1
self._evolve_all_active_cells() self._evolve_all_active_cells()
return False return False
def _is_evolution_finished(self):
return len(self.__cellular_automaton.grid.get_active_cell_names()) == 0
def _evolve_all_active_cells(self): def _evolve_all_active_cells(self):
active_cells = self.grid.get_active_cells() active_cells = self.__cellular_automaton.grid.get_active_cells()
self.grid.clear_active_cells() self.__cellular_automaton.grid.clear_active_cells()
self._evolve_cells(active_cells.values()) self._evolve_cells(active_cells.values())
def _is_evolution_finished(self):
return len(self.grid.get_active_cell_names()) == 0
def _evolve_cells(self, cells: list): def _evolve_cells(self, cells: list):
cellular_automaton = self.__cellular_automaton
for cell in cells: for cell in cells:
active = self.evolution_rule.evolve_cell(cell, self.iteration) active = cellular_automaton.evolution_rule.evolve_cell(cell, cellular_automaton.evolution_iteration_index)
if active: if active:
self.grid.set_cells_active([cell] + cell.neighbours) cellular_automaton.grid.set_cells_active([cell] + cell.neighbours)