156 lines
5.2 KiB
Python
156 lines
5.2 KiB
Python
![]() |
from PyQt5 import QtGui, QtCore, QtWidgets
|
||
|
from PIL import ImageDraw, ImageEnhance, ImageChops, ImageFilter
|
||
|
import os
|
||
|
import math
|
||
|
|
||
|
from component import Component
|
||
|
from toolkit.frame import BlankFrame, FramePainter
|
||
|
|
||
|
|
||
|
class Component(Component):
|
||
|
name = 'Conway\'s Game of Life'
|
||
|
version = '1.0.0a'
|
||
|
|
||
|
def widget(self, *args):
|
||
|
super().widget(*args)
|
||
|
self.scale = 32
|
||
|
self.updateGridSize()
|
||
|
self.startingGrid = {}
|
||
|
self.trackWidgets({
|
||
|
'tickRate': self.page.spinBox_tickRate,
|
||
|
'scale': self.page.spinBox_scale,
|
||
|
'color': self.page.lineEdit_color,
|
||
|
'shapeType': self.page.comboBox_shapeType,
|
||
|
'shadow': self.page.checkBox_shadow,
|
||
|
}, colorWidgets={
|
||
|
'color': self.page.pushButton_color,
|
||
|
})
|
||
|
self.page.spinBox_scale.setValue(self.scale)
|
||
|
|
||
|
def update(self):
|
||
|
self.updateGridSize()
|
||
|
super().update()
|
||
|
|
||
|
def previewClickEvent(self, pos, size, button):
|
||
|
pos = (
|
||
|
math.ceil((pos[0] / size[0]) * self.gridWidth) - 1,
|
||
|
math.ceil((pos[1] / size[1]) * self.gridHeight) - 1
|
||
|
)
|
||
|
if button == 1:
|
||
|
self.startingGrid[pos] = True
|
||
|
elif button == 2 and pos in self.startingGrid:
|
||
|
self.startingGrid.pop(pos)
|
||
|
|
||
|
def updateGridSize(self):
|
||
|
w, h = self.core.resolutions[-1].split('x')
|
||
|
self.gridWidth = int(int(w) / self.scale)
|
||
|
self.gridHeight = int(int(h) / self.scale)
|
||
|
self.pxWidth = math.ceil(self.width / self.gridWidth)
|
||
|
self.pxHeight = math.ceil(self.height / self.gridHeight)
|
||
|
|
||
|
def previewRender(self):
|
||
|
return self.drawGrid(self.startingGrid)
|
||
|
|
||
|
def preFrameRender(self, *args, **kwargs):
|
||
|
super().preFrameRender(*args, **kwargs)
|
||
|
self.progressBarSetText.emit("Computing evolution...")
|
||
|
self.tickGrids = {0: self.startingGrid}
|
||
|
tick = 0
|
||
|
for frameNo in range(
|
||
|
self.tickRate, len(self.completeAudioArray), self.sampleSize
|
||
|
):
|
||
|
if frameNo % self.tickRate == 0:
|
||
|
tick += 1
|
||
|
self.tickGrids[tick] = self.gridForTick(tick)
|
||
|
|
||
|
# update progress bar
|
||
|
progress = int(100*(frameNo/len(self.completeAudioArray)))
|
||
|
if progress >= 100:
|
||
|
progress = 100
|
||
|
pStr = "Computing evolution: "+str(progress)+'%'
|
||
|
self.progressBarSetText.emit(pStr)
|
||
|
self.progressBarUpdate.emit(int(progress))
|
||
|
|
||
|
def frameRender(self, frameNo):
|
||
|
tick = math.floor(frameNo / self.tickRate)
|
||
|
grid = self.tickGrids[tick]
|
||
|
return self.drawGrid(grid)
|
||
|
|
||
|
def drawGrid(self, grid):
|
||
|
frame = BlankFrame(self.width, self.height)
|
||
|
drawer = ImageDraw.Draw(frame)
|
||
|
|
||
|
for x, y in grid:
|
||
|
drawPtX = x * self.pxWidth
|
||
|
drawPtY = y * self.pxHeight
|
||
|
rect = (
|
||
|
(drawPtX, drawPtY),
|
||
|
(drawPtX + self.pxWidth, drawPtY + self.pxHeight)
|
||
|
)
|
||
|
if self.shapeType == 0:
|
||
|
drawer.rectangle(rect, fill=self.color)
|
||
|
elif self.shapeType == 1:
|
||
|
drawer.ellipse(rect, fill=self.color)
|
||
|
elif self.shapeType == 2:
|
||
|
drawer.pieslice(rect, 290, 250, fill=self.color)
|
||
|
elif self.shapeType == 3:
|
||
|
drawer.pieslice(rect, 20, 340, fill=self.color)
|
||
|
|
||
|
if self.shadow:
|
||
|
shadImg = ImageEnhance.Contrast(frame).enhance(0.0)
|
||
|
shadImg = shadImg.filter(ImageFilter.GaussianBlur(5.00))
|
||
|
shadImg = ImageChops.offset(shadImg, -2, 2)
|
||
|
shadImg.paste(frame, box=(0, 0), mask=frame)
|
||
|
frame = shadImg
|
||
|
return frame
|
||
|
|
||
|
def gridForTick(self, tick):
|
||
|
'''Given a tick number over 0, returns a new grid dict of tuples'''
|
||
|
lastGrid = self.tickGrids[tick - 1]
|
||
|
|
||
|
def nearbyCoords(x, y):
|
||
|
yield x + 1, y + 1
|
||
|
yield x + 1, y - 1
|
||
|
yield x - 1, y + 1
|
||
|
yield x - 1, y - 1
|
||
|
yield x, y + 1
|
||
|
yield x, y - 1
|
||
|
yield x + 1, y
|
||
|
yield x - 1, y
|
||
|
|
||
|
def neighbours(x, y):
|
||
|
nearbyCells = [
|
||
|
lastGrid.get(cell) for cell in nearbyCoords(x, y)
|
||
|
]
|
||
|
return [
|
||
|
nearbyCell for nearbyCell in nearbyCells
|
||
|
if nearbyCell is not None
|
||
|
]
|
||
|
|
||
|
newGrid = {}
|
||
|
for x, y in lastGrid:
|
||
|
surrounding = len(neighbours(x, y))
|
||
|
if surrounding == 2 or surrounding == 3:
|
||
|
newGrid[(x, y)] = True
|
||
|
potentialNewCells = set([
|
||
|
coordTup for origin in lastGrid
|
||
|
for coordTup in list(nearbyCoords(*origin))
|
||
|
])
|
||
|
for x, y in potentialNewCells:
|
||
|
if (x, y) in newGrid:
|
||
|
continue
|
||
|
surrounding = len(neighbours(x, y))
|
||
|
if surrounding == 3:
|
||
|
newGrid[(x, y)] = True
|
||
|
|
||
|
return newGrid
|
||
|
|
||
|
def savePreset(self):
|
||
|
pr = super().savePreset()
|
||
|
pr['GRID'] = self.startingGrid
|
||
|
return pr
|
||
|
|
||
|
def loadPreset(self, pr, *args):
|
||
|
super().loadPreset(pr, *args)
|
||
|
self.startingGrid = pr['GRID']
|