Skip to content

Commit

Permalink
Finish
Browse files Browse the repository at this point in the history
Finished the UI. There will likely be minor tweaks (Like supporting
portrait orientations) in the upcoming days/weeks
  • Loading branch information
controversial committed Nov 10, 2015
1 parent 3780335 commit 43086b3
Show file tree
Hide file tree
Showing 18 changed files with 418 additions and 121 deletions.
55 changes: 55 additions & 0 deletions App/App.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from ui import *
import photos

import sys
sys.path.append('..')

from CV import *
from pathfinding import *
import MazeEditView
reload(MazeEditView)
from MazeEditView import MazeEditView
from StartEndView import StartEndView
from SolutionView import SolutionView

@in_background
def act(sender):
nv = sender.navigation_view
p=photos.pick_image()
#Generate maze map from image
image=mazeGen.finalScan(p.resize((320,240)))

def f_handle(image):
'''Callback function for MazeEditView'''
global edited,sev
edited=image
def f_handle_2(start,end):
sol=SolutionView(edited, start, end)
nv.push_view(sol)
sev = StartEndView(image=edited,finished_handler=f_handle_2)
sev.name='Step 3: Mark Start and End Points'
sev.right_button_items=[ButtonItem(title='Continue', action=sev.finish)]
nv.push_view(sev)

#View for editing scan, f_handle is callback for returning edited image
mev = MazeEditView(image=image,finished_handler=f_handle)
mev.name='Step 2: Fix Mistakes'
mev.right_button_items=[ButtonItem(title='Continue', action=mev.finish)]
nv.push_view(mev)
#Problem is here because mev is not closed automatically, code that closes is after wait modal. fix by pushing new view from within callback function.
mev.wait_modal()
sev.wait_modal()
edited.show()

v=View(background_color=(255,255,255),name='Step 1: Capture Image')

startButton=Button(frame=(0,0,350,150), flex= 'LRBT')
startButton.title='start'
startButton.tint_color=(.5,.5,1)
startButton.font = ('Menlo-Bold',100)
startButton.action=act

v.add_subview(startButton)

nav=NavigationView(v)
nav.present(hide_title_bar=1)
76 changes: 76 additions & 0 deletions App/MazeEditView.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'''View for editing the walls and structure of mazes, as well as marking the start and end'''

import ui, io, time
from PIL import Image
from math import pi

def pil_to_ui(img):
with io.BytesIO() as b:
img.save(b, "PNG")
return ui.Image.from_data(b.getvalue())

class MazeEditView(ui.View):
'''Used for correcting the maze scan'''
def __init__(self, image, finished_handler):
self.img = image.convert('L')
self.finished_handler = finished_handler
self.load=self.img.load()
self.buttonView=None
self.angle = 0

def invert(self,sender):
'''Invert both the color of a button and the corresponding pixel in self.img'''
x=int((sender.frame[0])/sender.frame[2])#sender.frame[2] is buttonsize
y=int(sender.frame[1]/sender.frame[2])
if sender.background_color==(1,1,1,1):
sender.background_color=(0,0,0,1)
self.load[x,y]=0

elif sender.background_color==(0,0,0,1):
sender.background_color=(1,1,1,1)
self.load[x,y]=255

def rotate(self,sender):
self.angle += 1
def anim():
self.buttonView.transform=ui.Transform().rotation((pi/2*self.angle))
ui.animate(anim,duration=0.5)

def finish(self, *args):
#uses *args to account for being activated by either button press or otherwise
self.img = self.img.rotate(-90*self.angle)
self.finished_handler(self.img)

def makeButtons(self):
buttonsize = int(self.height/16)
self.startx=int((self.width/2-self.height/2))

rot=ui.Button(frame=(self.startx-2*buttonsize-10,10,2*buttonsize,2*buttonsize))
rot.image=ui.Image.named('ionicons-ios7-refresh-empty-256')
rot.action=self.rotate
rot.tint_color=(0,0,0)

self.add_subview(rot)

self.buttonView = ui.View(frame=(self.startx, 0, buttonsize*16,buttonsize*16))
for x in range(16):
for y in range(16):
frame=(x*buttonsize,y*buttonsize,buttonsize,buttonsize)
b=ui.Button(frame=frame)
b.background_color=self.load[x,y]
b.action=self.invert
self.buttonView.add_subview(b)
self.add_subview(self.buttonView)

def draw(self):
if not self.buttonView:
self.makeButtons()

def will_close(self):
if __name__ == '__main__':
self.finish()
if __name__ == '__main__':
import photos
def show(image):
image.show()
MazeEditView(image=photos.pick_image(),finished_handler=show).present(hide_title_bar=1)
70 changes: 70 additions & 0 deletions App/SolutionView.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'''This view will compute the shortest path and then display it in a pretty format'''
import ui
import photos

import sys; sys.path.append('..')

from pathfinding import imageInput, dijkstra

class SolutionView(ui.View):
def __init__(self, image, start, goal):
self.image = image.convert('RGB')
self.load = self.image.load()
self.start=start
self.goal=goal

sx, sy=start
gx, gy=goal
self.load[sx,sy]=(0,255,0)
self.load[gx,gy]=(255,0,0)
def pathCalc(self):
#Create graph from image
graph = imageInput.GraphFromImage(self.image)
#Locate nodes in graph that correspond to start and finish
for node in graph:
if node.id == self.start:
startNode = node
if node.id == self.goal:
goalNode = node
#Calculate path
return [x.id for x in dijkstra.Dijkstra(graph, startNode, goalNode)]

def Djk2UI(self, path):
'''Convert a path of (x,y) coordinates to a pretty ui path'''

buttonsize=self.buttonsize
#Center offset, used so that path is centered on tiles
cenoff=0.5*buttonsize
#Convert path to coordinates of the bigger window
path = [(cenoff+self.startx+x*buttonsize,cenoff+y*buttonsize) for x,y in path]

p=ui.Path()
p.move_to(*path[0])
for point in path:
p.line_to(*point)
p.line_join_style=ui.LINE_JOIN_ROUND
p.line_cap_style=ui.LINE_CAP_ROUND
p.line_width=buttonsize/4
return p

def draw(self):
self.buttonsize = int(self.height/16)
self.startx=int((self.width/2-self.height/2))

buttonsize=self.buttonsize
for x in range(16):
for y in range(16):
frame=(self.startx+x*buttonsize,y*buttonsize,buttonsize,buttonsize)
p=ui.Path.rect(*frame)
ui.set_color(self.load[x,y])
p.fill()

path=self.Djk2UI(self.pathCalc())

ui.set_color((0,0,1))
ui.set_shadow((0,0,0),2,2,5)
path.stroke()


if __name__ == '__main__':
SolutionView(photos.pick_image(), (1,0), (12,14)).present(hide_title_bar=1)
83 changes: 83 additions & 0 deletions App/StartEndView.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'''View for marking the start and end of a maze'''
import ui
from PIL import Image

class target(ui.View):
pass

class dragDrop(ui.View):
def snap(self):
'''snap to nearest target in same dragContainer'''
targets = self.superview.targets
#find closest target
centers = [t.center for t in targets]
distances = [ abs(c[0]-self.center[0])+abs(c[1]-self.center[1]) for c in centers]

self.center = centers[distances.index(min(distances))]
def touch_moved(self, touch):
cx, cy = touch.location
ox, oy = touch.prev_location
tx, ty = ox-cx, oy-cy
self.x -= tx
self.y -= ty
def touch_ended(self,touch):
self.snap()

class dragContainer(ui.View):
def __init__(self, dragDrops, targets):
for t in targets:
self.add_subview(t)
for d in dragDrops:
self.add_subview(d)
self.dragDrops = dragDrops
self.targets = targets


class StartEndView(ui.View):
def __init__(self, image, finished_handler):
self.img = image.convert('RGB')
self.load = self.img.load()
self.finished_handler = finished_handler
self.container = None

def make(self):
buttonsize = int(self.height/16)
self.startx=int((self.width/2-self.height/2))

self.start=dragDrop(frame=(self.startx-buttonsize,0,buttonsize,buttonsize),background_color=(0,1,0))
self.end=dragDrop(frame=(self.startx-buttonsize,buttonsize,buttonsize,buttonsize), background_color=(1,0,0))

drags = self.start, self.end

targets = []
for x in range(16):
for y in range(16):
frame=(self.startx+x*buttonsize,y*buttonsize,buttonsize,buttonsize)
if self.load[x,y] == (255,255,255):
bg=(1,1,1)
else:
bg=(0,0,0)
targets.append(target(frame=frame, background_color=bg))

self.container = dragContainer(drags, targets)
self.container.background_color=(1,1,1)
self.container.frame = self.frame
self.add_subview(self.container)

def draw(self):
if self.container is None:
self.make()

def finish(self, *args):
sframe = self.start.frame
eframe = self.end.frame
start = (sframe[0]-self.startx)/sframe[2], sframe[1]/sframe[3]
end = (eframe[0]-self.startx)/eframe[2], eframe[1]/eframe[3]
start = tuple(int(c) for c in start)
end = tuple(int(c) for c in end)
self.finished_handler(start, end)

if __name__ == '__main__':
import photos
s = StartEndView(photos.pick_image())
s.present(hide_title_bar=1)
2 changes: 1 addition & 1 deletion CV/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import bradley, perspective, redFinder
import bradley, perspective, redFinder, mazeGen
42 changes: 0 additions & 42 deletions CV/bradley-slow.py

This file was deleted.

3 changes: 2 additions & 1 deletion CV/bradley.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'''Bradley adaptive thresholding. Credit goes to http://stackoverflow.com/a/33092928/4414003, where user rayryeng wrote an implementation much more efficient than mine'''
import numpy as np
from PIL import Image
import time
Expand Down Expand Up @@ -79,4 +80,4 @@ def bradley_threshold(image, s=None, t=None):

a=time.time()
bradley_threshold(p).show()
print time.time()-a
print time.time()-a
Loading

0 comments on commit 43086b3

Please sign in to comment.