Final refractor

This commit is contained in:
Jens Bouman 2020-05-09 12:53:26 +02:00
parent afa67b21ac
commit 0fb7601130
25 changed files with 223 additions and 8418 deletions

BIN
ColorError.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

View File

@ -1,4 +1,4 @@
import interpreter.imageWrapper as imageWrapper import interpreter.imageFunctions as imageWrapper
class canvasManager(): class canvasManager():
@ -6,29 +6,54 @@ class canvasManager():
self.canvas = canvas self.canvas = canvas
self.image = image self.image = image
self.programState = programState self.programState = programState
self.previousProgramState = None
self.scaleSize = scaleSize self.scaleSize = scaleSize
def updateImage(self, newImage): def updateImage(self, newImage):
self.image = newImage self.image = newImage
def updateScaleSize(self, scaleSize): def updateScaleSize(self, scaleSize):
self.scaleSize = scaleSize self.scaleSize = scaleSize
def updateProgramState(self, newProgramState): def updateProgramState(self, newProgramState):
self.previousProgramState = self.programState
self.programState = newProgramState self.programState = newProgramState
def pixelToHexString(self, pixel) -> str: def pixelToHexString(self, pixel) -> str:
"""
Transforms the color of a pixel to hex-string
:param pixel: list with three values (RGB)
:return:
"""
return '#%02x%02x%02x' %(pixel[0], pixel[1], pixel[2]) return '#%02x%02x%02x' %(pixel[0], pixel[1], pixel[2])
def updateCanvas(self): def updateCanvas(self):
"""
Draws the canvas, then highlights the current codel. If a previous game state exists, it only reverses the highlight, instead of redrawing the entire canvas
:return:
"""
if self.image is None or self.canvas is None or self.programState is None or self.scaleSize is None: if self.image is None or self.canvas is None or self.programState is None or self.scaleSize is None:
print("one is not none")
return False return False
self.drawImage()
if self.previousProgramState is None:
self.drawImage()
else:
self.unHighlightCodel()
self.highlightCodel() self.highlightCodel()
# Draw breakpoint
return True return True
def drawImage(self): def drawImage(self):
"""
Draw the image pixel for pixel, upscaled to the scaleSize.
:return:
"""
self.clearCanvas() self.clearCanvas()
for raw_y, row in enumerate(self.image): for raw_y, row in enumerate(self.image):
for raw_x, pixel in enumerate(row): for raw_x, pixel in enumerate(row):
@ -39,19 +64,56 @@ class canvasManager():
def clearCanvas(self): def clearCanvas(self):
width = self.canvas.winfo_width() """
height = self.canvas.winfo_height() Draws a white rectangle over the canvas
:return:
"""
width = len(self.image[0]) * self.scaleSize
height = len(self.image) * self.scaleSize
self.canvas.create_rectangle(0,0, width, height, fill="#FFFFFF") self.canvas.create_rectangle(0,0, width, height, fill="#FFFFFF")
def highlightCodel(self): def highlightCodel(self):
"""
Outlines the current codel with complemented colors
:return:
"""
codel = imageWrapper.getCodel(self.image, self.programState.position) codel = imageWrapper.getCodel(self.image, self.programState.position)
pixel = imageWrapper.getPixel(self.image, self.programState.position) pixel = imageWrapper.getPixel(self.image, self.programState.position)
color = self.pixelToHexString(pixel) color = self.pixelToHexString(pixel)
self.colorCodel(codel, color, "#000000") outline = self.pixelToHexString(self.complement(int(pixel[0]), int(pixel[1]), int(pixel[2])))
self.colorCodel(codel, color, outline)
def unHighlightCodel(self):
codel = imageWrapper.getCodel(self.image, self.previousProgramState.position)
pixel = imageWrapper.getPixel(self.image, self.previousProgramState.position)
color = self.pixelToHexString(pixel)
self.colorCodel(codel, color, color)
def colorCodel(self, codel, fill, outline): def colorCodel(self, codel, fill, outline):
for position in codel.codel: for position in codel.codel:
x = position.coords[0] * self.scaleSize x = position.coords[0] * self.scaleSize
y = position.coords[1] * self.scaleSize y = position.coords[1] * self.scaleSize
self.canvas.create_rectangle(x,y, x+self.scaleSize - 1, y+self.scaleSize - 1, fill=fill, outline=outline) self.canvas.create_rectangle(x,y, x+self.scaleSize - 1, y+self.scaleSize - 1, fill=fill, outline=outline)
def hilo(self, a, b, c):
"""
Credit to StackOverflow user 'PM 2Ring' for making this code.
"""
if c < b: b, c = c, b
if b < a: a, b = b, a
if c < b: b, c = c, b
return a + c
def complement(self, r, g, b):
"""
Credit to StackOverflow user 'PM 2Ring' for making this code.
"""
if r == 255 and g == 255 and b == 255:
return (0,0,0)
k = self.hilo(r, g, b)
return tuple(k - u for u in (r, g, b))

View File

@ -1,7 +1,7 @@
import interpreter.imageWrapper as imageWrapper import interpreter.imageFunctions as imageWrapper
import interpreter.colors as colors import interpreter.colors as colors
import interpreter.tokens as lexerTokens import interpreter.tokens as lexerTokens
import interpreter.movement as movement import interpreter.movementFunctions as movement
from interpreter.dataStructures import direction from interpreter.dataStructures import direction
class infoManager(): class infoManager():
@ -15,26 +15,12 @@ class infoManager():
self.updateProgramStateInfo(programState) self.updateProgramStateInfo(programState)
def updateGeneralinfo(self, image, graph, programState): def updateGeneralinfo(self, image, graph, programState):
self.updateCodelInfo(image, programState.position)
self.updateEdgesInfo(image, graph, programState) self.updateEdgesInfo(image, graph, programState)
def updateProgramStateInfo(self, programState): def updateProgramStateInfo(self, programState):
self.updateStackInfo(programState.dataStack) self.updateStackInfo(programState.dataStack)
self.updatePointersInfo(programState.position, programState.direction) self.updatePointersInfo(programState.position, programState.direction)
def updateCodelInfo(self, image, newPosition):
infoMessage = self.builder.get_object('positionInfoMessage', self.generalInfo)
if colors.isBlack(imageWrapper.getPixel(image, newPosition)):
infoMessage.configure(text="Black pixels are no codel, and have no edges")
return None
baseString = "Selected codel contains:\n"
codel = imageWrapper.getCodel(image, newPosition)
for position in codel.codel:
baseString += "{}\n".format(position)
infoMessage.configure(text=baseString.strip('\n'))
def updateEdgesInfo(self, image, inputGraph, programState): def updateEdgesInfo(self, image, inputGraph, programState):
edgesInfo = self.builder.get_object('codelEdgesMessage', self.generalInfo) edgesInfo = self.builder.get_object('codelEdgesMessage', self.generalInfo)

View File

@ -1,17 +1,10 @@
# helloworld.py
import pygubu import pygubu
import sys
import os import os
sys.path.insert(0, "GUI/TKinter/.") from interpreter import imageFunctions as imageWrapper
from interpreter import imageWrapper as imageWrapper
from interpreter import lexer as lexer from interpreter import lexer as lexer
from interpreter import tokens as lexerTokens from interpreter import executeFunctions as main
from interpreter import colors as colors
from interpreter import movement as movement
from interpreter import executionFunctions as main
from interpreter.dataStructures import programState, direction, position from interpreter.dataStructures import programState, direction, position
import threading
from GUI import infoManager from GUI import infoManager
from GUI import canvasManager from GUI import canvasManager
@ -20,7 +13,7 @@ from GUI import canvasManager
class GUI: class GUI:
def __init__(self): def __init__(self):
# In pixelWidth/height per pixel. scaleSize = 25 means that every pixel will show as a 25x25 square # In pixelWidth/height per pixel. scaleSize = 25 means that every pixel will show as a 25x25 square
self.scaleSize = 25 self.scaleSize = 15
# In percentage # In percentage
self.executionSpeed = 15 self.executionSpeed = 15
@ -38,13 +31,13 @@ class GUI:
self.canvas = None self.canvas = None
#1: Create a builder #1: Create a builder
self.builder = builder = pygubu.Builder() self.builder = pygubu.Builder()
#2: Load an ui file #2: Load an ui file
builder.add_from_file("{}/tkinterLayout.ui".format(os.path.abspath(os.path.dirname(__file__)))) self.builder.add_from_file("{}/tkinterLayout.ui".format(os.path.abspath(os.path.dirname(__file__))))
#3: Create the mainwindow #3: Create the mainwindow
self.mainwindow = builder.get_object('rootWindow') self.mainwindow = self.builder.get_object('rootWindow')
self.initializeFrames() self.initializeFrames()
self.initializeCallbacks() self.initializeCallbacks()
@ -60,14 +53,16 @@ class GUI:
self.builder.connect_callbacks({ self.builder.connect_callbacks({
'loadFile': self.loadFile, 'loadFile': self.loadFile,
'setScale': self.setScale, 'setScale': self.setScale,
'takeStep': self.takeStep, 'takeStep': self.takeStep
'setExecutionSpeed': self.setExecutionSpeed,
'setBreakpoint': self.setBreakpoint,
'runProgram': self.runProgram,
'updateHighlight': self.toggleHighlight
}) })
self.canvas.bind("<Button-1>", self.canvasPressed) horizontalBar = self.builder.get_object("canvasHorizontalScroll", self.canvasFrame)
verticalBar = self.builder.get_object("canvasVerticalScroll", self.canvasFrame)
horizontalBar.config(command = self.canvas.xview)
verticalBar.config(command = self.canvas.yview)
self.canvas.config(xscrollcommand=horizontalBar.set, yscrollcommand=verticalBar.set)
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def initializeFrames(self): def initializeFrames(self):
self.optionBar = self.builder.get_object('optionBar', self.mainwindow) self.optionBar = self.builder.get_object('optionBar', self.mainwindow)
@ -75,8 +70,8 @@ class GUI:
self.actionBar = self.builder.get_object('actionBar', self.mainwindow) self.actionBar = self.builder.get_object('actionBar', self.mainwindow)
self.generalInfoFrame = self.builder.get_object("generalInfoFrame", self.content) self.generalInfoFrame = self.builder.get_object("generalInfoFrame", self.content)
self.programStateInfoFrame = self.builder.get_object("programStateInfoFrame", self.content) self.programStateInfoFrame = self.builder.get_object("programStateInfoFrame", self.content)
canvasFrame = self.builder.get_object('canvasFrame', self.content) self.canvasFrame = self.builder.get_object('canvasFrame', self.content)
self.canvas = self.builder.get_object('canvas', canvasFrame) self.canvas = self.builder.get_object('canvas', self.canvasFrame)
def update(self): def update(self):
@ -85,6 +80,7 @@ class GUI:
self.canvasManager.updateImage(self.image) self.canvasManager.updateImage(self.image)
self.canvasManager.updateProgramState(self.programState) self.canvasManager.updateProgramState(self.programState)
self.canvasManager.updateCanvas() self.canvasManager.updateCanvas()
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def takeStep(self): def takeStep(self):
@ -92,77 +88,59 @@ class GUI:
return None return None
newProgramState = main.takeStep(self.image, self.programState) newProgramState = main.takeStep(self.image, self.programState)
if isinstance(newProgramState, bool): # Error encountered, close window
return False if isinstance(newProgramState, BaseException):
self.mainwindow.destroy()
self.mainwindow.quit()
raise newProgramState
self.programState = newProgramState self.programState = newProgramState
self.selectedPosition = self.programState.position self.selectedPosition = self.programState.position
self.update() self.update()
print("Take step!")
return True return True
def setBreakpoint(self):
print("BREAKPOINT")
def setFileText(self, filePath): def setFileText(self, filePath):
print("Filepath: {}".format(filePath))
self.builder.get_object("fileNameEntry", self.optionBar).delete(0, len(self.builder.get_object("fileNameEntry", self.optionBar).get())) self.builder.get_object("fileNameEntry", self.optionBar).delete(0, len(self.builder.get_object("fileNameEntry", self.optionBar).get()))
self.builder.get_object("fileNameEntry", self.optionBar).insert(0, filePath) self.builder.get_object("fileNameEntry", self.optionBar).insert(0, filePath)
print("Get filepath: {}".format(self.builder.get_object("fileNameEntry", self.optionBar).get()))
def setExecutionSpeed(self, pos): def setExecutionSpeed(self, pos):
if 0 < float(pos) < 100: if 0 < float(pos) < 100:
self.executionSpeed = float(pos) self.executionSpeed = float(pos)
def toggleHighlight(self):
print(self.builder.get_object("highlightEdgeCheck", self.actionBar).instate(['selected']))
def getWaitTime(self):
return self.executionSpeed/100*self.maxWait
def runProgram(self):
if self.graph is None or self.image is None:
return None
step = self.takeStep()
if step:
timer = threading.Timer(self.getWaitTime(), self.runProgram)
timer.start()
return True
else:
return False
def setScale(self): def setScale(self):
scaleValue = int(self.builder.get_object('scaleEntry', self.optionBar).get()) scaleValue = int(self.builder.get_object('scaleEntry', self.optionBar).get())
if 0 < scaleValue < 100: if 0 < scaleValue < 100:
self.canvasManager.clearCanvas()
self.scaleSize = int(scaleValue) self.scaleSize = int(scaleValue)
self.update() self.update()
print("SCALE") self.canvasManager.drawImage()
self.canvasManager.updateCanvas()
def loadFile(self): def loadFile(self):
fileName = self.builder.get_object('fileNameEntry', self.optionBar).get() fileName = self.builder.get_object('fileNameEntry', self.optionBar).get()
if len(fileName) < 1: if len(fileName) < 1:
return None return None
try:
tmpImage = imageWrapper.getImage(fileName)
except FileNotFoundError:
edgeInfo = self.infoManager.builder.get_object('codelEdgesMessage', self.infoManager.generalInfo)
edgeInfo.configure(text="The file '{}' could not be found".format(fileName))
return False
self.image = imageWrapper.getImage(fileName) tmpResult = lexer.graphImage(tmpImage)
self.graph = lexer.graphImage(self.image)[0] if len(tmpResult[1]) != 0:
edgeInfo = self.infoManager.builder.get_object('codelEdgesMessage', self.infoManager.generalInfo)
edgeInfo.configure(text="The following exceptions occured while making the graph:\n{}".format("".join(list(map(lambda x: "\t{}\n".format(x), tmpResult[1])))))
return False
self.image = tmpImage
self.graph = tmpResult[0]
self.programState = programState(self.graph, position((0,0)), direction((0,0))) self.programState = programState(self.graph, position((0,0)), direction((0,0)))
# Reset previous state
self.canvasManager.previousProgramState = None
self.canvasManager.programState = None
self.update() self.update()
print("LOAD FILE!")
def canvasPressed(self, event):
unscaled_x = int(event.x / self.scaleSize)
unscaled_y = int(event.y / self.scaleSize)
self.selectedPosition = (unscaled_x, unscaled_y)
self.update()
if __name__ == '__main__':
app = GUI()
app.run()

View File

@ -71,7 +71,7 @@
</child> </child>
<child> <child>
<object id="scaleEntry" class="ttk.Entry"> <object id="scaleEntry" class="ttk.Entry">
<property name="text" translatable="yes">75</property> <property name="text" translatable="yes">15</property>
<layout> <layout>
<property name="column">3</property> <property name="column">3</property>
<property name="ipady">2</property> <property name="ipady">2</property>
@ -108,32 +108,6 @@
</column> </column>
</columns> </columns>
</layout> </layout>
<child>
<object id="runProgram" class="ttk.Button">
<property name="command">runProgram</property>
<property name="text" translatable="yes">Run program</property>
<layout>
<property name="column">0</property>
<property name="propagate">True</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object id="executionSpeed" class="ttk.Scale">
<property name="command">setExecutionSpeed</property>
<property name="from_">1</property>
<property name="orient">horizontal</property>
<property name="to">100</property>
<property name="value">15</property>
<property name="variable">int:pos</property>
<layout>
<property name="column">1</property>
<property name="propagate">True</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child> <child>
<object id="takeStep" class="ttk.Button"> <object id="takeStep" class="ttk.Button">
<property name="command">takeStep</property> <property name="command">takeStep</property>
@ -147,18 +121,6 @@
</layout> </layout>
</object> </object>
</child> </child>
<child>
<object id="highlightEdgeCheck" class="ttk.Checkbutton">
<property name="command">updateHighlight</property>
<property name="text" translatable="yes">Highlight edges</property>
<property name="variable">int:0</property>
<layout>
<property name="column">4</property>
<property name="propagate">True</property>
<property name="row">0</property>
</layout>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>
@ -213,31 +175,12 @@
</layout> </layout>
</object> </object>
</child> </child>
<child>
<object id="positionInfoMessage" class="tk.Message">
<property name="background">#ffffff</property>
<property name="highlightcolor">#a7a7a7</property>
<property name="highlightthickness">1</property>
<property name="text" translatable="yes">Selected codel contains the following positions:
- (0,0)
- (0,1)</property>
<property name="width">200</property>
<layout>
<property name="column">0</property>
<property name="padx">5</property>
<property name="propagate">True</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child> <child>
<object id="codelEdgesMessage" class="tk.Message"> <object id="codelEdgesMessage" class="tk.Message">
<property name="background">#FFFFFF</property> <property name="background">#FFFFFF</property>
<property name="highlightcolor">#a7a7a7</property> <property name="highlightbackground">#39af36</property>
<property name="highlightthickness">1</property> <property name="highlightthickness">1</property>
<property name="text" translatable="yes">Codel edges are as follows: <property name="text" translatable="yes">Codel edges are as follows: </property>
(0,1) -&gt; (1,0) push 0</property>
<property name="width">200</property> <property name="width">200</property>
<layout> <layout>
<property name="column">0</property> <property name="column">0</property>
@ -246,17 +189,6 @@
</layout> </layout>
</object> </object>
</child> </child>
<child>
<object id="setBreakPoint" class="ttk.Button">
<property name="command">setBreakpoint</property>
<property name="text" translatable="yes">Set breakpoint</property>
<layout>
<property name="column">0</property>
<property name="propagate">True</property>
<property name="row">3</property>
</layout>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>
@ -286,6 +218,7 @@
<object id="stackContents" class="tk.Message"> <object id="stackContents" class="tk.Message">
<property name="anchor">center</property> <property name="anchor">center</property>
<property name="background">#ffffff</property> <property name="background">#ffffff</property>
<property name="highlightbackground">#39af36</property>
<property name="highlightcolor">#a7a7a7</property> <property name="highlightcolor">#a7a7a7</property>
<property name="highlightthickness">1</property> <property name="highlightthickness">1</property>
<layout> <layout>
@ -320,6 +253,8 @@
<child> <child>
<object id="pointerMessage" class="tk.Message"> <object id="pointerMessage" class="tk.Message">
<property name="background">#ffffff</property> <property name="background">#ffffff</property>
<property name="highlightbackground">#39af36</property>
<property name="highlightthickness">1</property>
<layout> <layout>
<property name="column">1</property> <property name="column">1</property>
<property name="propagate">True</property> <property name="propagate">True</property>
@ -337,6 +272,7 @@
<property name="column">2</property> <property name="column">2</property>
<property name="propagate">True</property> <property name="propagate">True</property>
<property name="row">0</property> <property name="row">0</property>
<property name="sticky">nw</property>
</layout> </layout>
<child> <child>
<object id="canvas" class="tk.Canvas"> <object id="canvas" class="tk.Canvas">
@ -353,26 +289,29 @@
<property name="pady">5</property> <property name="pady">5</property>
<property name="propagate">True</property> <property name="propagate">True</property>
<property name="row">0</property> <property name="row">0</property>
<property name="sticky">n</property>
</layout> </layout>
</object> </object>
</child> </child>
<child> <child>
<object id="Scrollbar_1" class="ttk.Scrollbar"> <object id="canvasVerticalScroll" class="tk.Scrollbar">
<property name="orient">vertical</property> <property name="orient">vertical</property>
<layout> <layout>
<property name="column">1</property> <property name="column">2</property>
<property name="propagate">True</property> <property name="propagate">True</property>
<property name="row">0</property> <property name="row">0</property>
<property name="sticky">ns</property>
</layout> </layout>
</object> </object>
</child> </child>
<child> <child>
<object id="Scrollbar_2" class="ttk.Scrollbar"> <object id="canvasHorizontalScroll" class="tk.Scrollbar">
<property name="orient">horizontal</property> <property name="orient">horizontal</property>
<layout> <layout>
<property name="column">0</property> <property name="column">0</property>
<property name="propagate">True</property> <property name="propagate">True</property>
<property name="row">1</property> <property name="row">1</property>
<property name="sticky">ew</property>
</layout> </layout>
</object> </object>
</child> </child>

BIN
Info/GUI layout.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 KiB

After

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

0
__init__.py Normal file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

BIN
countdown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

BIN
divideByZero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

BIN
endless.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

View File

Before

Width:  |  Height:  |  Size: 354 B

After

Width:  |  Height:  |  Size: 354 B

View File

@ -2,7 +2,7 @@ from typing import Dict, Union
import numpy as np import numpy as np
import interpreter.errors as errors from interpreter import errors as errors
class possiblePixels: class possiblePixels:
def __init__(self): def __init__(self):
@ -52,11 +52,10 @@ def getPixelChange(colorStart: np.ndarray, colorEnd: np.ndarray) -> Union[Dict[s
return {"hueChange": 0, "lightChange": 0} return {"hueChange": 0, "lightChange": 0}
pixelsColors = possiblePixels() pixelsColors = possiblePixels()
# Converting np arrays to common lists # Converting np arrays to normal lists
colorStart = list(colorStart)[:3] colorStart = list(colorStart)[:3]
colorEnd = list(colorEnd)[:3] colorEnd = list(colorEnd)[:3]
if colorStart not in pixelsColors.colors: if colorStart not in pixelsColors.colors:
return errors.UnknownColorError("Color {} is not recognized as a correct color".format(colorStart)) return errors.UnknownColorError("Color {} is not recognized as a correct color".format(colorStart))
if colorEnd not in pixelsColors.colors: if colorEnd not in pixelsColors.colors:

View File

@ -1,7 +1,7 @@
from typing import Set, Tuple, Dict, List from typing import Set, Tuple, Dict, List
import copy import copy
import interpreter.tokens as tokens from interpreter import tokens as tokens
class position(): class position():
""" """
@ -125,6 +125,9 @@ class graph():
return str(self) return str(self)
class programState(): class programState():
"""
The program state contains the graph of the program, the position, direction and stack.
"""
def __init__(self, newGraph: graph, newPosition: position, newDirection: direction, dataStack: List[int] = None): def __init__(self, newGraph: graph, newPosition: position, newDirection: direction, dataStack: List[int] = None):
if dataStack is None: if dataStack is None:
dataStack = [] dataStack = []
@ -135,7 +138,7 @@ class programState():
self.dataStack = dataStack self.dataStack = dataStack
def __str__(self): def __str__(self):
return "{pos} / {pointers}. Stack: {stack}".format(pos=self.position, pointers=self.direction, stack=self.dataStack) return "Pos:{pos} / {pointers}. Stack: {stack}".format(pos=self.position, pointers=self.direction, stack=self.dataStack)
def __repr__(self): def __repr__(self):
return str(self) return str(self)

View File

@ -1,14 +1,12 @@
import copy import copy
from typing import Union, List, Callable from typing import Union, List, Callable
import sys
import numpy as np import numpy as np
# sys.path.insert(0, "../") from interpreter import imageFunctions as imageWrapper
from interpreter import imageWrapper as imageWrapper
from interpreter import lexer as lexer from interpreter import lexer as lexer
from interpreter import tokens as tokens from interpreter import tokens as tokens
from interpreter import movement as movement from interpreter import movementFunctions as movement
from interpreter import colors as colors from interpreter import colors as colors
from interpreter import tokenFunctions as runner from interpreter import tokenFunctions as runner
from interpreter import errors as errors from interpreter import errors as errors
@ -26,13 +24,15 @@ def interpret(image: np.ndarray) -> Union[programState, List[BaseException]]:
print("The following exceptions occured while making the graph:\n{}".format("".join(list(map(lambda x: "\t{}\n".format(x), graph[1]))))) print("The following exceptions occured while making the graph:\n{}".format("".join(list(map(lambda x: "\t{}\n".format(x), graph[1])))))
return graph[1] return graph[1]
# This is the default programState.
startPosition = position((0, 0)) startPosition = position((0, 0))
pointers = direction((0, 0)) pointers = direction((0, 0))
PS = programState(graph[0], startPosition, pointers) PS = programState(graph[0], startPosition, pointers)
result = runProgram(image, PS) result = runProgram(image, PS)
# Check if executed step had an error
if isinstance(result, BaseException): if isinstance(result, BaseException):
print("The following exceptions occured while executing the next step:\n{}".format(result)) print("The following exception occured while executing the next step:\n{}".format(result))
return [result] return [result]
return result return result
@ -64,8 +64,13 @@ def runProgram(image: np.ndarray, PS: programState) -> Union[programState, BaseE
return runProgram(image, newState) return runProgram(image, newState)
def countSteps(f: Callable): def countSteps(f: Callable[[np.ndarray, programState], programState]) -> Callable[[np.ndarray, programState], programState]:
def inner(image: np.ndarray, PS: programState): """
A decorator function to count the steps taken in the program
:param f: original function to call
:return: A decorated function
"""
def inner(image: np.ndarray, PS: programState) -> programState:
inner.counter += 1 inner.counter += 1
return f(image, PS) return f(image, PS)
inner.counter = 0 inner.counter = 0
@ -90,21 +95,18 @@ def takeStep(image: np.ndarray, PS: programState) -> Union[programState, BaseExc
result = runner.executeToken(newToken, newState.direction, newState.dataStack) result = runner.executeToken(newToken, newState.direction, newState.dataStack)
# Add additional information to the error message (Position and direction)
if isinstance(result, BaseException): if isinstance(result, BaseException):
return result return type(result)("{}, at position {}, direction {}".format(result.args[0], edgePosition,newState.direction))
# return result
# If the next token is either white or color, just move along. If the token was black (or terminate), the direction # If the next token is either white or color, just move along. If the token was black (or terminate), the direction
# is already changed # is already changed, but the position shouldn't move
if isinstance(newToken, (tokens.toWhiteToken, tokens.toColorToken)): if isinstance(newToken, (tokens.toWhiteToken, tokens.toColorToken)):
newState.position = movement.getNextPosition(edgePosition, newState.direction.pointers[0]) newState.position = movement.getNextPosition(edgePosition, newState.direction.pointers[0])
# Use the new direction and stack for the next step
newState.direction = result[0] newState.direction = result[0]
newState.dataStack = result[1] newState.dataStack = result[1]
return newState return newState
if __name__ == "__main__":
sys.setrecursionlimit(1000000)
im = imageWrapper.getImage("../Piet_hello.png")
interpret(im)

View File

@ -1,11 +1,11 @@
from typing import Union, List, Any from typing import Union
import numpy as np import numpy as np
import interpreter.imageWrapper as imageWrapper from interpreter import imageFunctions as imageWrapper
import interpreter.colors as colors from interpreter import colors as colors
import interpreter.movement as movement from interpreter import movementFunctions as movement
import interpreter.tokens as tokens from interpreter import tokens as tokens
import interpreter.errors as errors from interpreter import errors as errors
from interpreter.dataStructures import edge from interpreter.dataStructures import edge

View File

@ -54,6 +54,7 @@ def getCodel(image: np.ndarray, inputPosition: position, foundPixels: codel = No
if inputPosition in foundPixels.codel: if inputPosition in foundPixels.codel:
return foundPixels return foundPixels
# Adjacent white colors don't form a codel
if colors.isWhite(getPixel(image, inputPosition)): if colors.isWhite(getPixel(image, inputPosition)):
foundPixels.codel.add(inputPosition) foundPixels.codel.add(inputPosition)
return foundPixels return foundPixels

View File

@ -3,10 +3,10 @@ import copy
import numpy as np import numpy as np
import interpreter.colors as colors import interpreter.colors as colors
import interpreter.imageWrapper as imageWrapper import interpreter.imageFunctions as imageWrapper
import interpreter.tokens as tokens import interpreter.tokens as tokens
import interpreter.helperFunctions as helperFunctions import interpreter.helperFunctions as helperFunctions
import interpreter.movement as movement import interpreter.movementFunctions as movement
from interpreter.dataStructures import position, codel, edge, graphNode, graph, direction from interpreter.dataStructures import position, codel, edge, graphNode, graph, direction

View File

@ -1,4 +1,5 @@
from typing import Union from typing import Union
import operator
from interpreter.dataStructures import direction, position, codel from interpreter.dataStructures import direction, position, codel
@ -110,7 +111,6 @@ def flip(inputDirection: direction) -> direction:
return direction((flipDP(inputDirection.pointers[0]), inputDirection.pointers[1])) return direction((flipDP(inputDirection.pointers[0]), inputDirection.pointers[1]))
# TODO FIX KEYERROR
def getNextPosition(startPosition: position, directionPointer: int) -> Union[position, KeyError]: def getNextPosition(startPosition: position, directionPointer: int) -> Union[position, KeyError]:
""" """
Finds next position along the direction pointer Finds next position along the direction pointer
@ -155,49 +155,44 @@ def findEdge(inputCodel: codel, inputDirection: direction) -> Union[position, bo
dp = inputDirection.pointers[0] dp = inputDirection.pointers[0]
cc = inputDirection.pointers[1] cc = inputDirection.pointers[1]
# Right side
if dp == 0: if dp == 0:
edgePosition = max(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[0]) edgePosition = max(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[0])
for pos in inputCodel.codel: maxValues = list(filter(lambda lambdaPos: lambdaPos.coords[0] == edgePosition.coords[0], inputCodel.codel))
if pos.coords[0] == edgePosition.coords[0]: if cc == 0:
# -> ^ Right and up # -> ^ Right and up
if cc == 0 and pos.coords[1] < edgePosition.coords[1]: return min(maxValues, key=lambda lambdaPos: lambdaPos.coords[1])
edgePosition = pos else:
# -> V Right and down # -> V Right and down
if cc == 1 and pos.coords[1] > edgePosition.coords[1]: return max(maxValues, key=lambda lambdaPos: lambdaPos.coords[1])
edgePosition = pos # Bottom side
return edgePosition
elif dp == 1: elif dp == 1:
edgePosition = max(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[1]) edgePosition = max(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[1])
for pos in inputCodel.codel: maxValues = list(filter(lambda lambdaPos: lambdaPos.coords[1] == edgePosition.coords[1], inputCodel.codel))
if pos.coords[1] == edgePosition.coords[1]: if cc == 0:
# V -> Down and right # V -> Down and right
if cc == 0 and pos.coords[0] > edgePosition.coords[0]: return max(maxValues, key=lambda lambaPos: lambaPos.coords[0])
edgePosition = pos else:
# V <- Down and left # V <- Down and left
elif cc == 1 and pos.coords[0] < edgePosition.coords[0]: return min(maxValues, key=lambda lambdaPos: lambdaPos.coords[0])
edgePosition = pos # Left side
return edgePosition
elif dp == 2: elif dp == 2:
edgePosition = min(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[0]) edgePosition = min(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[0])
for pos in inputCodel.codel: minValues = list(filter(lambda lambdaPos: lambdaPos.coords[0] == edgePosition.coords[0], inputCodel.codel))
if pos.coords[0] == edgePosition.coords[0]: if cc == 0:
# <- V Left and down # <- V Left and down
if cc == 0 and pos.coords[1] > edgePosition.coords[1]: return max(minValues, key=lambda lambaPos: lambaPos.coords[1])
edgePosition = pos else:
# <- ^ left and up # <- ^ left and up
elif cc == 1 and pos.coords[1] < edgePosition.coords[1]: return min(minValues, key=lambda lambdaPos: lambdaPos.coords[1])
edgePosition = pos
return edgePosition # Top side
elif dp == 3: else: # dp == 3
edgePosition = min(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[1]) edgePosition = min(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[1])
for pos in inputCodel.codel: maxValues = list(filter(lambda lambdaPos: lambdaPos.coords[1] == edgePosition.coords[1], inputCodel.codel))
if pos.coords[1] == edgePosition.coords[1]: if cc == 0:
# ^ <- Up and left # ^ <- Up and left
if cc == 0 and pos.coords[0] < edgePosition.coords[0]: return min(maxValues, key=lambda lambaPos: lambaPos.coords[0])
edgePosition = pos else:
# ^ -> Up and right # ^ -> Up and right
elif cc == 1 and pos.coords[0] > edgePosition.coords[0]: return max(maxValues, key=lambda lambdaPos: lambdaPos.coords[0])
edgePosition = pos
return edgePosition
else:
raise SyntaxError("DirectionPointer '{}' is unknown".format(dp))

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,12 @@
from typing import List, Tuple, Union from typing import List, Tuple, Union
import copy import copy
import interpreter.tokens as lexerTokens from interpreter import tokens as lexerTokens
import interpreter.movement as movement from interpreter import movementFunctions as movement
import interpreter.errors as errors from interpreter import errors as errors
from interpreter.dataStructures import direction from interpreter.dataStructures import direction
# TODO Nettere afhandeling errors (Union[Tuple[List[int], Tuple[int, int]], bool])
# TODO Test cases maken per token
def executeToken(token: lexerTokens.baseLexerToken, inputDirection: direction, dataStack: List[int]) -> Union[Tuple[direction, List[int]], BaseException]: def executeToken(token: lexerTokens.baseLexerToken, inputDirection: direction, dataStack: List[int]) -> Union[Tuple[direction, List[int]], BaseException]:
""" """
Executes the function associated with tokens Executes the function associated with tokens
@ -144,16 +142,16 @@ def divideOperator(inputDirection: direction, dataStack: List[int]) -> Union[Tup
:return: Tuple with the new data stack and new pointers :return: Tuple with the new data stack and new pointers
""" """
newStack = dataStack.copy() newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection) newDirection = copy.deepcopy(inputDirection)
if len(newStack) < 2: if len(newStack) < 2:
return (inputDirection, newStack) return (newDirection, newStack)
first = newStack.pop() first = newStack.pop()
second = newStack.pop() second = newStack.pop()
if second == 0: if second == 0:
return ZeroDivisionError("Division by zero {}/{}".format(first, second)) return ZeroDivisionError("Division by zero {}/{}".format(first, second))
newStack.append(int(second / first)) newStack.append(int(second / first))
return (inputDirection, newStack) return (newDirection, newStack)
def modOperator(inputDirection: direction, dataStack: List[int]) -> Union[Tuple[direction, List[int]], BaseException]: def modOperator(inputDirection: direction, dataStack: List[int]) -> Union[Tuple[direction, List[int]], BaseException]:
@ -170,7 +168,6 @@ def modOperator(inputDirection: direction, dataStack: List[int]) -> Union[Tuple[
valA = newStack.pop() valA = newStack.pop()
valB = newStack.pop() valB = newStack.pop()
if valB == 0: if valB == 0:
# TODO ERROR
return ZeroDivisionError("Second value is 0: {}%{}".format(valA, valB)) return ZeroDivisionError("Second value is 0: {}%{}".format(valA, valB))
newStack.append(valB % valA) newStack.append(valB % valA)
return (inputDirection, newStack) return (inputDirection, newStack)

View File

@ -10,21 +10,33 @@ class baseLexerToken():
class toBlackToken(baseLexerToken): class toBlackToken(baseLexerToken):
"""
Used when a transition to black (or edge) occurs
"""
def __init__(self, tokenType: str = "toBlack"): def __init__(self, tokenType: str = "toBlack"):
super().__init__(tokenType) super().__init__(tokenType)
class toWhiteToken(baseLexerToken): class toWhiteToken(baseLexerToken):
"""
Used when a transition to white occurs
"""
def __init__(self): def __init__(self):
super().__init__("toWhite") super().__init__("toWhite")
class terminateToken(baseLexerToken): class terminateToken(baseLexerToken):
"""
Used when a codel has no possible way to escape (8 * toBlack)
"""
def __init__(self): def __init__(self):
super().__init__("exit") super().__init__("exit")
class toColorToken(baseLexerToken): class toColorToken(baseLexerToken):
"""
Used when a transition to a color occurs
"""
def __init__(self, tokenType: str, codelSize: int): def __init__(self, tokenType: str, codelSize: int):
super().__init__(tokenType) super().__init__(tokenType)
self.codelSize = codelSize self.codelSize = codelSize
@ -34,6 +46,12 @@ class toColorToken(baseLexerToken):
def getTokenType(hueChange: int, lightChange: int) -> str: def getTokenType(hueChange: int, lightChange: int) -> str:
"""
Find the toColorToken type based on hue change and lightness change
:param hueChange: number of hue changes between two pixels
:param lightChange: number of lightness changes between two pixels
:return: A string with the toColorToken type
"""
tokens = [ tokens = [
["noop", "push", "pop"], ["noop", "push", "pop"],
["add", "subtract", "multiply"], ["add", "subtract", "multiply"],

View File

@ -1,7 +1,10 @@
import argparse import argparse
import sys
from interpreter import executionFunctions as executionFunctions sys.setrecursionlimit(100000)
from interpreter import imageWrapper as imageWrapper
from interpreter import executeFunctions as executionFunctions
from interpreter import imageFunctions as imageWrapper
from GUI import main as GUIMain from GUI import main as GUIMain
parser = argparse.ArgumentParser(description='Interprets a piet image') parser = argparse.ArgumentParser(description='Interprets a piet image')
@ -17,7 +20,6 @@ if not args.graphical:
if args.verbose: if args.verbose:
print("\nTotal steps: {}".format(executionFunctions.takeStep.counter)) print("\nTotal steps: {}".format(executionFunctions.takeStep.counter))
else: else:
print("GUI TIME!")
app = GUIMain.GUI() app = GUIMain.GUI()
app.setFileText(args.file) app.setFileText(args.file)
app.loadFile() app.loadFile()