Final refractor
BIN
ColorError.png
Normal file
After Width: | Height: | Size: 788 B |
@ -1,4 +1,4 @@
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.imageFunctions as imageWrapper
|
||||
|
||||
|
||||
class canvasManager():
|
||||
@ -6,29 +6,54 @@ class canvasManager():
|
||||
self.canvas = canvas
|
||||
self.image = image
|
||||
self.programState = programState
|
||||
self.previousProgramState = None
|
||||
self.scaleSize = scaleSize
|
||||
|
||||
|
||||
def updateImage(self, newImage):
|
||||
self.image = newImage
|
||||
|
||||
|
||||
def updateScaleSize(self, scaleSize):
|
||||
self.scaleSize = scaleSize
|
||||
|
||||
|
||||
def updateProgramState(self, newProgramState):
|
||||
self.previousProgramState = self.programState
|
||||
self.programState = newProgramState
|
||||
|
||||
|
||||
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])
|
||||
|
||||
|
||||
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:
|
||||
print("one is not none")
|
||||
return False
|
||||
self.drawImage()
|
||||
|
||||
if self.previousProgramState is None:
|
||||
self.drawImage()
|
||||
else:
|
||||
self.unHighlightCodel()
|
||||
self.highlightCodel()
|
||||
# Draw breakpoint
|
||||
return True
|
||||
|
||||
|
||||
def drawImage(self):
|
||||
"""
|
||||
Draw the image pixel for pixel, upscaled to the scaleSize.
|
||||
:return:
|
||||
"""
|
||||
self.clearCanvas()
|
||||
for raw_y, row in enumerate(self.image):
|
||||
for raw_x, pixel in enumerate(row):
|
||||
@ -39,19 +64,56 @@ class canvasManager():
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
def highlightCodel(self):
|
||||
"""
|
||||
Outlines the current codel with complemented colors
|
||||
:return:
|
||||
"""
|
||||
codel = imageWrapper.getCodel(self.image, self.programState.position)
|
||||
pixel = imageWrapper.getPixel(self.image, self.programState.position)
|
||||
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):
|
||||
for position in codel.codel:
|
||||
x = position.coords[0] * 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)
|
||||
|
||||
|
||||
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))
|
||||
|
@ -1,7 +1,7 @@
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.imageFunctions as imageWrapper
|
||||
import interpreter.colors as colors
|
||||
import interpreter.tokens as lexerTokens
|
||||
import interpreter.movement as movement
|
||||
import interpreter.movementFunctions as movement
|
||||
from interpreter.dataStructures import direction
|
||||
|
||||
class infoManager():
|
||||
@ -15,26 +15,12 @@ class infoManager():
|
||||
self.updateProgramStateInfo(programState)
|
||||
|
||||
def updateGeneralinfo(self, image, graph, programState):
|
||||
self.updateCodelInfo(image, programState.position)
|
||||
self.updateEdgesInfo(image, graph, programState)
|
||||
|
||||
def updateProgramStateInfo(self, programState):
|
||||
self.updateStackInfo(programState.dataStack)
|
||||
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):
|
||||
edgesInfo = self.builder.get_object('codelEdgesMessage', self.generalInfo)
|
||||
|
108
GUI/main.py
@ -1,17 +1,10 @@
|
||||
# helloworld.py
|
||||
import pygubu
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, "GUI/TKinter/.")
|
||||
from interpreter import imageWrapper as imageWrapper
|
||||
from interpreter import imageFunctions as imageWrapper
|
||||
from interpreter import lexer as lexer
|
||||
from interpreter import tokens as lexerTokens
|
||||
from interpreter import colors as colors
|
||||
from interpreter import movement as movement
|
||||
from interpreter import executionFunctions as main
|
||||
from interpreter import executeFunctions as main
|
||||
from interpreter.dataStructures import programState, direction, position
|
||||
import threading
|
||||
|
||||
from GUI import infoManager
|
||||
from GUI import canvasManager
|
||||
@ -20,7 +13,7 @@ from GUI import canvasManager
|
||||
class GUI:
|
||||
def __init__(self):
|
||||
# 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
|
||||
self.executionSpeed = 15
|
||||
|
||||
@ -38,13 +31,13 @@ class GUI:
|
||||
self.canvas = None
|
||||
|
||||
#1: Create a builder
|
||||
self.builder = builder = pygubu.Builder()
|
||||
self.builder = pygubu.Builder()
|
||||
|
||||
#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
|
||||
self.mainwindow = builder.get_object('rootWindow')
|
||||
self.mainwindow = self.builder.get_object('rootWindow')
|
||||
|
||||
self.initializeFrames()
|
||||
self.initializeCallbacks()
|
||||
@ -60,14 +53,16 @@ class GUI:
|
||||
self.builder.connect_callbacks({
|
||||
'loadFile': self.loadFile,
|
||||
'setScale': self.setScale,
|
||||
'takeStep': self.takeStep,
|
||||
'setExecutionSpeed': self.setExecutionSpeed,
|
||||
'setBreakpoint': self.setBreakpoint,
|
||||
'runProgram': self.runProgram,
|
||||
'updateHighlight': self.toggleHighlight
|
||||
'takeStep': self.takeStep
|
||||
})
|
||||
|
||||
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):
|
||||
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.generalInfoFrame = self.builder.get_object("generalInfoFrame", self.content)
|
||||
self.programStateInfoFrame = self.builder.get_object("programStateInfoFrame", self.content)
|
||||
canvasFrame = self.builder.get_object('canvasFrame', self.content)
|
||||
self.canvas = self.builder.get_object('canvas', canvasFrame)
|
||||
self.canvasFrame = self.builder.get_object('canvasFrame', self.content)
|
||||
self.canvas = self.builder.get_object('canvas', self.canvasFrame)
|
||||
|
||||
|
||||
def update(self):
|
||||
@ -85,6 +80,7 @@ class GUI:
|
||||
self.canvasManager.updateImage(self.image)
|
||||
self.canvasManager.updateProgramState(self.programState)
|
||||
self.canvasManager.updateCanvas()
|
||||
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
|
||||
|
||||
|
||||
def takeStep(self):
|
||||
@ -92,77 +88,59 @@ class GUI:
|
||||
return None
|
||||
|
||||
newProgramState = main.takeStep(self.image, self.programState)
|
||||
if isinstance(newProgramState, bool):
|
||||
return False
|
||||
# Error encountered, close window
|
||||
if isinstance(newProgramState, BaseException):
|
||||
self.mainwindow.destroy()
|
||||
self.mainwindow.quit()
|
||||
raise newProgramState
|
||||
|
||||
self.programState = newProgramState
|
||||
self.selectedPosition = self.programState.position
|
||||
self.update()
|
||||
print("Take step!")
|
||||
return True
|
||||
|
||||
|
||||
def setBreakpoint(self):
|
||||
print("BREAKPOINT")
|
||||
|
||||
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).insert(0, filePath)
|
||||
print("Get filepath: {}".format(self.builder.get_object("fileNameEntry", self.optionBar).get()))
|
||||
|
||||
|
||||
def setExecutionSpeed(self, pos):
|
||||
if 0 < float(pos) < 100:
|
||||
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):
|
||||
scaleValue = int(self.builder.get_object('scaleEntry', self.optionBar).get())
|
||||
if 0 < scaleValue < 100:
|
||||
self.canvasManager.clearCanvas()
|
||||
self.scaleSize = int(scaleValue)
|
||||
self.update()
|
||||
print("SCALE")
|
||||
self.canvasManager.drawImage()
|
||||
self.canvasManager.updateCanvas()
|
||||
|
||||
|
||||
def loadFile(self):
|
||||
fileName = self.builder.get_object('fileNameEntry', self.optionBar).get()
|
||||
if len(fileName) < 1:
|
||||
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)
|
||||
self.graph = lexer.graphImage(self.image)[0]
|
||||
tmpResult = lexer.graphImage(tmpImage)
|
||||
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)))
|
||||
|
||||
# Reset previous state
|
||||
self.canvasManager.previousProgramState = None
|
||||
self.canvasManager.programState = None
|
||||
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()
|
||||
|
@ -71,7 +71,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<object id="scaleEntry" class="ttk.Entry">
|
||||
<property name="text" translatable="yes">75</property>
|
||||
<property name="text" translatable="yes">15</property>
|
||||
<layout>
|
||||
<property name="column">3</property>
|
||||
<property name="ipady">2</property>
|
||||
@ -108,32 +108,6 @@
|
||||
</column>
|
||||
</columns>
|
||||
</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>
|
||||
<object id="takeStep" class="ttk.Button">
|
||||
<property name="command">takeStep</property>
|
||||
@ -147,18 +121,6 @@
|
||||
</layout>
|
||||
</object>
|
||||
</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>
|
||||
</child>
|
||||
<child>
|
||||
@ -213,31 +175,12 @@
|
||||
</layout>
|
||||
</object>
|
||||
</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>
|
||||
<object id="codelEdgesMessage" class="tk.Message">
|
||||
<property name="background">#FFFFFF</property>
|
||||
<property name="highlightcolor">#a7a7a7</property>
|
||||
<property name="highlightbackground">#39af36</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<property name="text" translatable="yes">Codel edges are as follows:
|
||||
|
||||
(0,1) -> (1,0) push 0</property>
|
||||
<property name="text" translatable="yes">Codel edges are as follows: </property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
@ -246,17 +189,6 @@
|
||||
</layout>
|
||||
</object>
|
||||
</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>
|
||||
</child>
|
||||
<child>
|
||||
@ -286,6 +218,7 @@
|
||||
<object id="stackContents" class="tk.Message">
|
||||
<property name="anchor">center</property>
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="highlightbackground">#39af36</property>
|
||||
<property name="highlightcolor">#a7a7a7</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<layout>
|
||||
@ -320,6 +253,8 @@
|
||||
<child>
|
||||
<object id="pointerMessage" class="tk.Message">
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="highlightbackground">#39af36</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
@ -337,6 +272,7 @@
|
||||
<property name="column">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">nw</property>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="canvas" class="tk.Canvas">
|
||||
@ -353,26 +289,29 @@
|
||||
<property name="pady">5</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">n</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="Scrollbar_1" class="ttk.Scrollbar">
|
||||
<object id="canvasVerticalScroll" class="tk.Scrollbar">
|
||||
<property name="orient">vertical</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="column">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">ns</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="Scrollbar_2" class="ttk.Scrollbar">
|
||||
<object id="canvasHorizontalScroll" class="tk.Scrollbar">
|
||||
<property name="orient">horizontal</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
<property name="sticky">ew</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
|
BIN
Info/GUI layout.PNG
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
Info/poster.png
Before Width: | Height: | Size: 520 KiB After Width: | Height: | Size: 523 KiB |
BIN
Info/poster.xcf
0
__init__.py
Normal file
Before Width: | Height: | Size: 3.6 KiB |
BIN
countdown.png
Normal file
After Width: | Height: | Size: 613 B |
BIN
divideByZero.png
Normal file
After Width: | Height: | Size: 559 B |
BIN
endless.png
Normal file
After Width: | Height: | Size: 529 B |
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 354 B |
@ -2,7 +2,7 @@ from typing import Dict, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
import interpreter.errors as errors
|
||||
from interpreter import errors as errors
|
||||
|
||||
class possiblePixels:
|
||||
def __init__(self):
|
||||
@ -52,11 +52,10 @@ def getPixelChange(colorStart: np.ndarray, colorEnd: np.ndarray) -> Union[Dict[s
|
||||
return {"hueChange": 0, "lightChange": 0}
|
||||
|
||||
pixelsColors = possiblePixels()
|
||||
# Converting np arrays to common lists
|
||||
# Converting np arrays to normal lists
|
||||
colorStart = list(colorStart)[:3]
|
||||
colorEnd = list(colorEnd)[:3]
|
||||
|
||||
|
||||
if colorStart not in pixelsColors.colors:
|
||||
return errors.UnknownColorError("Color {} is not recognized as a correct color".format(colorStart))
|
||||
if colorEnd not in pixelsColors.colors:
|
||||
|
@ -1,7 +1,7 @@
|
||||
from typing import Set, Tuple, Dict, List
|
||||
import copy
|
||||
|
||||
import interpreter.tokens as tokens
|
||||
from interpreter import tokens as tokens
|
||||
|
||||
class position():
|
||||
"""
|
||||
@ -125,6 +125,9 @@ class graph():
|
||||
return str(self)
|
||||
|
||||
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):
|
||||
if dataStack is None:
|
||||
dataStack = []
|
||||
@ -135,7 +138,7 @@ class programState():
|
||||
self.dataStack = dataStack
|
||||
|
||||
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):
|
||||
return str(self)
|
||||
|
@ -1,14 +1,12 @@
|
||||
import copy
|
||||
from typing import Union, List, Callable
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
# sys.path.insert(0, "../")
|
||||
from interpreter import imageWrapper as imageWrapper
|
||||
from interpreter import imageFunctions as imageWrapper
|
||||
from interpreter import lexer as lexer
|
||||
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 tokenFunctions as runner
|
||||
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])))))
|
||||
return graph[1]
|
||||
|
||||
# This is the default programState.
|
||||
startPosition = position((0, 0))
|
||||
pointers = direction((0, 0))
|
||||
PS = programState(graph[0], startPosition, pointers)
|
||||
|
||||
result = runProgram(image, PS)
|
||||
# Check if executed step had an error
|
||||
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
|
||||
|
||||
@ -64,8 +64,13 @@ def runProgram(image: np.ndarray, PS: programState) -> Union[programState, BaseE
|
||||
return runProgram(image, newState)
|
||||
|
||||
|
||||
def countSteps(f: Callable):
|
||||
def inner(image: np.ndarray, PS: programState):
|
||||
def countSteps(f: Callable[[np.ndarray, programState], programState]) -> Callable[[np.ndarray, programState], 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
|
||||
return f(image, PS)
|
||||
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)
|
||||
|
||||
# Add additional information to the error message (Position and direction)
|
||||
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
|
||||
# is already changed
|
||||
# is already changed, but the position shouldn't move
|
||||
if isinstance(newToken, (tokens.toWhiteToken, tokens.toColorToken)):
|
||||
newState.position = movement.getNextPosition(edgePosition, newState.direction.pointers[0])
|
||||
|
||||
# Use the new direction and stack for the next step
|
||||
newState.direction = result[0]
|
||||
newState.dataStack = result[1]
|
||||
|
||||
return newState
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.setrecursionlimit(1000000)
|
||||
im = imageWrapper.getImage("../Piet_hello.png")
|
||||
interpret(im)
|
@ -1,11 +1,11 @@
|
||||
from typing import Union, List, Any
|
||||
from typing import Union
|
||||
import numpy as np
|
||||
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.colors as colors
|
||||
import interpreter.movement as movement
|
||||
import interpreter.tokens as tokens
|
||||
import interpreter.errors as errors
|
||||
from interpreter import imageFunctions as imageWrapper
|
||||
from interpreter import colors as colors
|
||||
from interpreter import movementFunctions as movement
|
||||
from interpreter import tokens as tokens
|
||||
from interpreter import errors as errors
|
||||
from interpreter.dataStructures import edge
|
||||
|
||||
|
||||
|
@ -54,6 +54,7 @@ def getCodel(image: np.ndarray, inputPosition: position, foundPixels: codel = No
|
||||
if inputPosition in foundPixels.codel:
|
||||
return foundPixels
|
||||
|
||||
# Adjacent white colors don't form a codel
|
||||
if colors.isWhite(getPixel(image, inputPosition)):
|
||||
foundPixels.codel.add(inputPosition)
|
||||
return foundPixels
|
@ -3,10 +3,10 @@ import copy
|
||||
import numpy as np
|
||||
|
||||
import interpreter.colors as colors
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.imageFunctions as imageWrapper
|
||||
import interpreter.tokens as tokens
|
||||
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
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import Union
|
||||
import operator
|
||||
|
||||
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]))
|
||||
|
||||
|
||||
# TODO FIX KEYERROR
|
||||
def getNextPosition(startPosition: position, directionPointer: int) -> Union[position, KeyError]:
|
||||
"""
|
||||
Finds next position along the direction pointer
|
||||
@ -155,49 +155,44 @@ def findEdge(inputCodel: codel, inputDirection: direction) -> Union[position, bo
|
||||
dp = inputDirection.pointers[0]
|
||||
cc = inputDirection.pointers[1]
|
||||
|
||||
# Right side
|
||||
if dp == 0:
|
||||
edgePosition = max(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[0])
|
||||
for pos in inputCodel.codel:
|
||||
if pos.coords[0] == edgePosition.coords[0]:
|
||||
# -> ^ Right and up
|
||||
if cc == 0 and pos.coords[1] < edgePosition.coords[1]:
|
||||
edgePosition = pos
|
||||
# -> V Right and down
|
||||
if cc == 1 and pos.coords[1] > edgePosition.coords[1]:
|
||||
edgePosition = pos
|
||||
return edgePosition
|
||||
maxValues = list(filter(lambda lambdaPos: lambdaPos.coords[0] == edgePosition.coords[0], inputCodel.codel))
|
||||
if cc == 0:
|
||||
# -> ^ Right and up
|
||||
return min(maxValues, key=lambda lambdaPos: lambdaPos.coords[1])
|
||||
else:
|
||||
# -> V Right and down
|
||||
return max(maxValues, key=lambda lambdaPos: lambdaPos.coords[1])
|
||||
# Bottom side
|
||||
elif dp == 1:
|
||||
edgePosition = max(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[1])
|
||||
for pos in inputCodel.codel:
|
||||
if pos.coords[1] == edgePosition.coords[1]:
|
||||
# V -> Down and right
|
||||
if cc == 0 and pos.coords[0] > edgePosition.coords[0]:
|
||||
edgePosition = pos
|
||||
# V <- Down and left
|
||||
elif cc == 1 and pos.coords[0] < edgePosition.coords[0]:
|
||||
edgePosition = pos
|
||||
return edgePosition
|
||||
maxValues = list(filter(lambda lambdaPos: lambdaPos.coords[1] == edgePosition.coords[1], inputCodel.codel))
|
||||
if cc == 0:
|
||||
# V -> Down and right
|
||||
return max(maxValues, key=lambda lambaPos: lambaPos.coords[0])
|
||||
else:
|
||||
# V <- Down and left
|
||||
return min(maxValues, key=lambda lambdaPos: lambdaPos.coords[0])
|
||||
# Left side
|
||||
elif dp == 2:
|
||||
edgePosition = min(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[0])
|
||||
for pos in inputCodel.codel:
|
||||
if pos.coords[0] == edgePosition.coords[0]:
|
||||
# <- V Left and down
|
||||
if cc == 0 and pos.coords[1] > edgePosition.coords[1]:
|
||||
edgePosition = pos
|
||||
# <- ^ left and up
|
||||
elif cc == 1 and pos.coords[1] < edgePosition.coords[1]:
|
||||
edgePosition = pos
|
||||
return edgePosition
|
||||
elif dp == 3:
|
||||
minValues = list(filter(lambda lambdaPos: lambdaPos.coords[0] == edgePosition.coords[0], inputCodel.codel))
|
||||
if cc == 0:
|
||||
# <- V Left and down
|
||||
return max(minValues, key=lambda lambaPos: lambaPos.coords[1])
|
||||
else:
|
||||
# <- ^ left and up
|
||||
return min(minValues, key=lambda lambdaPos: lambdaPos.coords[1])
|
||||
|
||||
# Top side
|
||||
else: # dp == 3
|
||||
edgePosition = min(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[1])
|
||||
for pos in inputCodel.codel:
|
||||
if pos.coords[1] == edgePosition.coords[1]:
|
||||
# ^ <- Up and left
|
||||
if cc == 0 and pos.coords[0] < edgePosition.coords[0]:
|
||||
edgePosition = pos
|
||||
# ^ -> Up and right
|
||||
elif cc == 1 and pos.coords[0] > edgePosition.coords[0]:
|
||||
edgePosition = pos
|
||||
return edgePosition
|
||||
else:
|
||||
raise SyntaxError("DirectionPointer '{}' is unknown".format(dp))
|
||||
maxValues = list(filter(lambda lambdaPos: lambdaPos.coords[1] == edgePosition.coords[1], inputCodel.codel))
|
||||
if cc == 0:
|
||||
# ^ <- Up and left
|
||||
return min(maxValues, key=lambda lambaPos: lambaPos.coords[0])
|
||||
else:
|
||||
# ^ -> Up and right
|
||||
return max(maxValues, key=lambda lambdaPos: lambdaPos.coords[0])
|
@ -1,14 +1,12 @@
|
||||
from typing import List, Tuple, Union
|
||||
import copy
|
||||
|
||||
import interpreter.tokens as lexerTokens
|
||||
import interpreter.movement as movement
|
||||
import interpreter.errors as errors
|
||||
from interpreter import tokens as lexerTokens
|
||||
from interpreter import movementFunctions as movement
|
||||
from interpreter import errors as errors
|
||||
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]:
|
||||
"""
|
||||
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
|
||||
"""
|
||||
newStack = dataStack.copy()
|
||||
inputDirection = copy.deepcopy(inputDirection)
|
||||
newDirection = copy.deepcopy(inputDirection)
|
||||
if len(newStack) < 2:
|
||||
return (inputDirection, newStack)
|
||||
return (newDirection, newStack)
|
||||
|
||||
first = newStack.pop()
|
||||
second = newStack.pop()
|
||||
if second == 0:
|
||||
return ZeroDivisionError("Division by zero {}/{}".format(first, second))
|
||||
newStack.append(int(second / first))
|
||||
return (inputDirection, newStack)
|
||||
return (newDirection, newStack)
|
||||
|
||||
|
||||
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()
|
||||
valB = newStack.pop()
|
||||
if valB == 0:
|
||||
# TODO ERROR
|
||||
return ZeroDivisionError("Second value is 0: {}%{}".format(valA, valB))
|
||||
newStack.append(valB % valA)
|
||||
return (inputDirection, newStack)
|
||||
|
@ -10,21 +10,33 @@ class baseLexerToken():
|
||||
|
||||
|
||||
class toBlackToken(baseLexerToken):
|
||||
"""
|
||||
Used when a transition to black (or edge) occurs
|
||||
"""
|
||||
def __init__(self, tokenType: str = "toBlack"):
|
||||
super().__init__(tokenType)
|
||||
|
||||
|
||||
class toWhiteToken(baseLexerToken):
|
||||
"""
|
||||
Used when a transition to white occurs
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__("toWhite")
|
||||
|
||||
|
||||
class terminateToken(baseLexerToken):
|
||||
"""
|
||||
Used when a codel has no possible way to escape (8 * toBlack)
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__("exit")
|
||||
|
||||
|
||||
class toColorToken(baseLexerToken):
|
||||
"""
|
||||
Used when a transition to a color occurs
|
||||
"""
|
||||
def __init__(self, tokenType: str, codelSize: int):
|
||||
super().__init__(tokenType)
|
||||
self.codelSize = codelSize
|
||||
@ -34,6 +46,12 @@ class toColorToken(baseLexerToken):
|
||||
|
||||
|
||||
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 = [
|
||||
["noop", "push", "pop"],
|
||||
["add", "subtract", "multiply"],
|
||||
|
8
main.py
@ -1,7 +1,10 @@
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from interpreter import executionFunctions as executionFunctions
|
||||
from interpreter import imageWrapper as imageWrapper
|
||||
sys.setrecursionlimit(100000)
|
||||
|
||||
from interpreter import executeFunctions as executionFunctions
|
||||
from interpreter import imageFunctions as imageWrapper
|
||||
from GUI import main as GUIMain
|
||||
|
||||
parser = argparse.ArgumentParser(description='Interprets a piet image')
|
||||
@ -17,7 +20,6 @@ if not args.graphical:
|
||||
if args.verbose:
|
||||
print("\nTotal steps: {}".format(executionFunctions.takeStep.counter))
|
||||
else:
|
||||
print("GUI TIME!")
|
||||
app = GUIMain.GUI()
|
||||
app.setFileText(args.file)
|
||||
app.loadFile()
|
||||
|