Skip to content
Snippets Groups Projects
Commit 35465adc authored by iggy's avatar iggy
Browse files

menu: fundamentals and integration of demos; ui work; bugfixes

parent 7c365256
No related branches found
No related tags found
No related merge requests found
...@@ -12,13 +12,11 @@ def xy_from_polar(r,deg): ...@@ -12,13 +12,11 @@ def xy_from_polar(r,deg):
) ) ) )
ctx = hardware.get_ctx() ctx = hardware.get_ctx()
ctx.text_align = ctx.CENTER popcorn = [9,7,9,5,0,5,-3,999]
ctx.text_baseline = ctx.MIDDLE
synth = tinysynth(440,1) synth = tinysynth(440,1)
synth.decay(25)
popcorn = [9,7,9,5,0,5,-3,999] sequencer = None
handler = None
def on_step(data): def on_step(data):
note = popcorn[data["step"]] note = popcorn[data["step"]]
...@@ -39,8 +37,24 @@ def on_step(data): ...@@ -39,8 +37,24 @@ def on_step(data):
ctx.move_to(x,y).rgb(0.5,0.5,0).text("{}".format(data["step"])) ctx.move_to(x,y).rgb(0.5,0.5,0).text("{}".format(data["step"]))
hardware.display_update() hardware.display_update()
def handle_input(data={}):
sequencer.remove()
ev.remove()
def init():
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
event.Sequence(bpm=160, steps=8, action=on_step, loop=True) synth = tinysynth(440,1)
synth.decay(25)
global sequencer
sequencer = event.Sequence(bpm=160, steps=8, action=on_step, loop=True)
global ev
ev=event.Event(name="sparabo",action=handle_input,
condition=lambda e: e["type"] =="button" and e["change"] and e["value"] == 2)
def run():
init();
print("run")
event.the_engine.eventloop() event.the_engine.eventloop()
...@@ -30,16 +30,21 @@ GREY = (0.5,0.5,0.5) ...@@ -30,16 +30,21 @@ GREY = (0.5,0.5,0.5)
# The global context (representing the whole screen) # The global context (representing the whole screen)
ctx = None ctx = None
worms = None worms = []
class Worm(): class Worm():
def __init__(self): def __init__(self,direction=None):
self.color = randrgb() self.color = randrgb()
if direction:
self.direction = direction
else:
self.direction = random.random()*math.pi*2 self.direction = random.random()*math.pi*2
self.size = 10
self.size = 50
self.speed = self.size/5 self.speed = self.size/5
(x,y) = xy_from_polar(40, self.direction+90) (x,y) = xy_from_polar(100, self.direction)
self.x = x self.x = x
self.y= y self.y= y
#(self.dx,self.dy) = xy_from_polar(1,self.direction) #(self.dx,self.dy) = xy_from_polar(1,self.direction)
...@@ -59,7 +64,12 @@ class Worm(): ...@@ -59,7 +64,12 @@ class Worm():
def move(self): def move(self):
dist = math.sqrt(self.x**2+self.y**2) dist = math.sqrt(self.x**2+self.y**2)
self.size = (120-dist)/3 target_size = (130-dist)/3
if self.size>target_size: self.size-=1
if self.size<target_size: self.size+=1
self.speed = self.size/5 self.speed = self.size/5
self.direction += (random.random()-0.5)*math.pi/4 self.direction += (random.random()-0.5)*math.pi/4
...@@ -69,7 +79,7 @@ class Worm(): ...@@ -69,7 +79,7 @@ class Worm():
self.y+=dy self.y+=dy
if dist>110-self.size/2 and dist>self._lastdist: if dist>120-self.size/2 and dist>self._lastdist:
polar_position=math.atan2(self.y,self.x) polar_position=math.atan2(self.y,self.x)
dx=dx*-abs(math.cos(polar_position)) dx=dx*-abs(math.cos(polar_position))
dy=dy*-abs(math.sin(polar_position)) dy=dy*-abs(math.sin(polar_position))
...@@ -78,24 +88,17 @@ class Worm(): ...@@ -78,24 +88,17 @@ class Worm():
self._lastdist = dist self._lastdist = dist
def init(): def handle_input(data):
global worms worms.append(Worm(data.get("index",0)*2*math.pi/10+math.pi ))
global ctx if len(worms)>10:
worms = [] worms.pop(0)
for i in range(23):
worms.append(Worm())
ctx = hardware.get_ctx()
def run():
global worms
for w in worms:
w.draw()
w.move()
hardware.display_update()
time.sleep(0.001)
def init(data={}):
# Get the global context (representing the whole screen)
ctx = hardware.get_ctx()
def foreground(): #center the text horizontally and vertically
ctx.text_align = ctx.CENTER ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE ctx.text_baseline = ctx.MIDDLE
...@@ -105,9 +108,28 @@ def foreground(): ...@@ -105,9 +108,28 @@ def foreground():
ctx.rgb(*BLUE).rectangle(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT).fill() ctx.rgb(*BLUE).rectangle(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT).fill()
#Write some text #Write some text
ctx.move_to(0,0).rgb(*WHITE).text("Hi :)") ctx.move_to(0,0).rgb(*WHITE).text("touch me :)")
hardware.display_update()
global worms
worms = []
for i in range(0):
worms.append(Worm())
event.Event(name="worms_control",action=handle_input,
condition=lambda data: data.get("type","")=="captouch" and data.get("value")==1 and data["change"])
def loop(data={}):
for w in worms:
w.draw()
w.move()
hardware.display_update() hardware.display_update()
def run(data={}):
init()
event.the_engine.userloop = loop
event.the_engine.eventloop()
#Known problems: #Known problems:
#ctx.rotate(math.pi) turns the display black until powercycled #ctx.rotate(math.pi) turns the display black until powercycled
#ctx.clip() causes crashes #ctx.clip() causes crashes
......
import hardware import hardware
import time import time
import math import math
import random
EVENTTYPE_TIMED = 1 EVENTTYPE_TIMED = 1
...@@ -15,6 +16,8 @@ class Engine(): ...@@ -15,6 +16,8 @@ class Engine():
self.events_input = [] self.events_input = []
self.next_timed = None self.next_timed = None
self.last_input_state = None self.last_input_state = None
self.userloop = None
self.is_running = False
def add(self,event): def add(self,event):
if isinstance(event,EventTimed): if isinstance(event,EventTimed):
...@@ -24,8 +27,14 @@ class Engine(): ...@@ -24,8 +27,14 @@ class Engine():
def add_timed(self,event): def add_timed(self,event):
self.events_timed.append(event) self.events_timed.append(event)
self.events_timed = sorted(self.events_timed, key = lambda event: event.deadline) self._sort_timed()
def remove_timed(self,group_id):
self.events_timed = [event for event in self.events_timed if event.group_id==group_id]
self._sort_timed()
def _sort_timed(self):
self.events_timed = sorted(self.events_timed, key = lambda event: event.deadline)
def add_input(self,event): def add_input(self,event):
self.events_input.append(event) self.events_input.append(event)
...@@ -62,11 +71,19 @@ class Engine(): ...@@ -62,11 +71,19 @@ class Engine():
diff.append({ diff.append({
"type" : input_state[key][1], "type" : input_state[key][1],
"index" : input_state[key][2], "index" : input_state[key][2],
"to" : input_state[key][0], "value" : input_state[key][0],
"from" : self.last_input_state[key][0], "from" : self.last_input_state[key][0],
"ticks_ms": time.ticks_ms() "ticks_ms": time.ticks_ms(),
"change": True
})
else:
diff.append({
"type" : input_state[key][1],
"index" : input_state[key][2],
"value" : input_state[key][0],
"ticks_ms": time.ticks_ms(),
"change": False
}) })
if diff: if diff:
#print(diff) #print(diff)
...@@ -80,19 +97,30 @@ class Engine(): ...@@ -80,19 +97,30 @@ class Engine():
self.last_input_state=input_state self.last_input_state=input_state
def _handle_userloop(self):
if self.userloop:
self.userloop()
def _eventloop_single(self): def _eventloop_single(self):
self._handle_timed() self._handle_timed()
self._handle_input() self._handle_input()
self._handle_userloop()
def eventloop(self): def eventloop(self):
while True: if self.is_running:
print ("eventloop already running")
return
else:
print("eventloop started")
self.is_running=True
while self.is_running:
self._eventloop_single() self._eventloop_single()
time.sleep(0.005) time.sleep(0.005)
class Event(): class Event():
def __init__(self,name="unknown",data={},action=None,condition=None): def __init__(self,name="unknown",data={},action=None,condition=None,group_id=None):
#print (action) #print (action)
self.name = name self.name = name
self.eventtype = None self.eventtype = None
...@@ -101,18 +129,21 @@ class Event(): ...@@ -101,18 +129,21 @@ class Event():
self.condition = condition self.condition = condition
if not condition: if not condition:
self.condition = lambda x: True self.condition = lambda x: True
self.group_id=group_id
the_engine.add(self) the_engine.add(self)
#print (data)
def trigger(self,triggerdata={}): def trigger(self,triggerdata={}):
print ("triggered {} (with {})".format(self.name,triggerdata)) print ("triggered {} (with {})".format(self.name,triggerdata))
if not self.action is None: if not self.action is None:
triggerdata.update(self.data) triggerdata.update(self.data)
#print("trigger")
self.action(triggerdata) self.action(triggerdata)
def remove(self):
if self in the_engine.events_input:
the_engine.events_input.remove(self)
if self in the_engine.events_timed:
the_engine.events_timed.remove(self)
class EventTimed(Event): class EventTimed(Event):
def __init__(self,ms,name="timer", *args, **kwargs): def __init__(self,ms,name="timer", *args, **kwargs):
...@@ -129,6 +160,7 @@ class EventTimed(Event): ...@@ -129,6 +160,7 @@ class EventTimed(Event):
class Sequence(): class Sequence():
def __init__(self,bpm=60,loop=True,steps=16,action=None): def __init__(self,bpm=60,loop=True,steps=16,action=None):
self.group_id = random.randint(0,100000000)
self.bpm = bpm self.bpm = bpm
if not action: if not action:
self.action = lambda data: print("step {}".format(data.get("step"))) self.action = lambda data: print("step {}".format(data.get("step")))
...@@ -136,26 +168,13 @@ class Sequence(): ...@@ -136,26 +168,13 @@ class Sequence():
self.action = action self.action = action
stepsize_ms = int(60*1000/bpm) stepsize_ms = int(60*1000/bpm)
for i in range(steps): for i in range(steps):
EventTimed(stepsize_ms*i,name="seq{}".format(i),action=self.action, data={'step':i}) EventTimed(stepsize_ms*i,name="seq{}".format(i),action=self.action, data={'step':i}, group_id=self.group_id)
if loop: if loop:
EventTimed(stepsize_ms*steps,name="loop",action=lambda data: Sequence(bpm=bpm,loop=loop,steps=steps,action=action)) EventTimed(stepsize_ms*steps,name="loop", group_id=self.group_id, action=lambda data: Sequence(bpm=bpm,loop=loop,steps=steps,action=action))
def remove(self):
the_engine.remove_timed(self.group_id)
global the_engine global the_engine
the_engine = Engine() the_engine = Engine()
\ No newline at end of file
EventTimed(200,name="bar",action=lambda data: print("GNANGNAGNA"))
EventTimed(100,name="foo")
Event(name="baz",
action=lambda data: print(data),
condition=lambda data: data.get('type')=="captouch"
)
#Sequence(action=axelf)
print (the_engine.events_timed)
#the_engine.eventloop()
import ui
import time
import hardware
import math
import event
menu_stack = []
active_menu = None
class Menu():
def __init__(self,name="menu",has_back=True):
self.name=name
self.items=[]
self.__index = 0
self.ui = ui.GroupRing(r=80)
self.ui.element_center = ui.Text(self.name)
self.icon = ui.Icon(label=name)
self.angle = 0
self.angle_step= 0.2
if has_back:
self.add(MenuItemBack())
def __repr__(self):
return "{} ({}): {}".format(self.name, self.__index, self.items)
def add(self, item):
self.items.append(item)
self.ui.add(item.ui)
def pop(self):
self.items.pop()
self.ui.children.pop()
def start(self):
print(self)
active_menu = self
render()
def scroll(self, n=0):
self.__index= (self.__index+n)%len(self.items)
return self.items[self.__index]
def rotate_by(self,angle):
self.rotate_to(self.angle+angle)
def rotate_to(self, angle):
self.angle = angle%(math.pi*2)
self.ui.angle_offset = self.angle
def rotate_steps(self, steps=1):
self.rotate_by(self.angle_step*steps)
def _get_hovered_index(self):
index = round(-self.angle/(math.pi*2)*len(self.items))
i = index%len(self.items)
return i
def get_hovered_item(self):
return self.items[self._get_hovered_index()]
def _get_angle_for_index(self,index):
return (math.pi*2/len(self.items)*(index)+self.angle)%(math.pi*2)
def _get_topness_for_index(self,index):
angle = self._get_angle_for_index(index)
dist = min(angle,math.pi*2-angle)
topness = 1-(dist/math.pi)
return topness
def draw(self):
hovered_index = self._get_hovered_index()
for i in range(len(self.items)):
item = self.items[i]
my_extra = abs(self._get_topness_for_index(i))*40
if i == hovered_index:
item.ui.has_highlight=True
my_extra+=20
else:
item.ui.has_highlight=False
item.ui.size=30+my_extra
self.ui.draw()
class MenuItem():
def __init__(self,name="item"):
self.name= name
self.action= None
self.ui = ui.Icon(label=name)
def __repr__(self):
return "item: {} (action: {})".format(self.name,"?")
def enter(self,data={}):
print("Enter MenuItem {}".format(self.name))
if self.action:
self.action(data)
class MenuItemSubmenu(MenuItem):
def __init__(self,submenu):
super().__init__(name=submenu.name)
self.ui = submenu.icon
self.target = submenu
def enter(self,data={}):
print("Enter Submenu {}".format(self.target.name))
menu_stack.append(active_menu)
set_active_menu(self.target)
class MenuItemBack(MenuItem):
def __init__(self):
super().__init__(name="<-")
def enter(self,data={}):
menu_back()
def on_scroll(d):
if active_menu is None:
return
if active_menu.angle_step<0.5:
active_menu.angle_step+=0.025
if d["value"] == -1:
active_menu.rotate_steps(-1)
elif d["value"] == 1:
active_menu.rotate_steps(1)
render()
def on_release(d):
if active_menu is None:
return
active_menu.angle_step = 0.2
render()
def on_enter(d):
if active_menu is None:
event.the_engine.userloop=None
menu_back()
return
else:
active_menu.get_hovered_item().enter()
event.Event(name="menu rotation",
condition=lambda e: e["type"] =="button" and not e["change"] and abs(e["value"])==1 ,
action=on_scroll
)
event.Event(name="menu rotation release",
condition=lambda e: e["type"] =="button" and e["change"] and e["value"] ==0,
action=on_release
)
event.Event(name="menu enter",
condition=lambda e: e["type"] =="button" and e["change"] and e["value"] == 2,
action=on_enter
)
def render():
print (active_menu)
if active_menu is None:
return
hardware.get_ctx().rectangle(-120,-120,240,240).rgb(0,0,0).fill()
active_menu.draw()
hardware.display_update()
def set_active_menu(menu):
global active_menu
active_menu = menu
def menu_back():
if not menu_stack:
return
previous = menu_stack.pop()
set_active_menu(previous)
\ No newline at end of file
import hardware
import random
import math
import time
class UIElement():
def __init__(self,origin=(0,0)):
self.children = []
self.origin = origin
self.ctx = hardware.get_ctx()
def draw(self, offset=(0,0)):
pos = (self.origin[0]+offset[0],self.origin[1]+offset[1])
self._draw(pos)
for child in self.children:
child.draw(pos)
def _draw(self,pos):
#print(pos)
pass
def add(self, child):
self.children.append(child)
class Text(UIElement):
def __init__(self,s="foo"):
self.s = s
super().__init__()
def _draw(self, pos):
self.ctx.text_align = self.ctx.CENTER
self.ctx.text_baseline = self.ctx.MIDDLE
self.ctx.rgb(1,1,1).move_to(pos[0],pos[1]).text(self.s)
class Icon(UIElement):
def __init__(self,label="?", size=60):
self.bg_r = random.random()
self.bg_g = random.random()
self.bg_b = random.random()
self.fg = 0
self.label=label
self.size = size
self.has_highlight = False
super().__init__()
def _draw(self,pos):
x = int(pos[0])
y = int(pos[1])
width = 55
height = 40
self.ctx.text_align = self.ctx.CENTER
self.ctx.text_baseline = self.ctx.MIDDLE
if self.has_highlight:
hs = self.size+5;
self.ctx.move_to(x,y-hs/2).rgb(1,1,1).round_rectangle(
x-hs/2,
y-hs/2,
hs,hs,hs//2
).fill()
self.ctx.move_to(x,y-self.size/2).rgb(self.bg_r,self.bg_g,self.bg_b).round_rectangle(
x-self.size/2,
y-self.size/2,
self.size,self.size,self.size//2
).fill()
self.ctx.rgb(1,1,1).move_to(x,y).text(self.label)
class GroupStackedVertical(UIElement):
pass
class GroupRing(UIElement):
def __init__(self,r=100,origin=(0,0),element_center=None):
self.r = r
self.angle_offset = 0
self.element_center=element_center
super().__init__(origin)
def draw(self, offset=(0,0)):
pos = (self.origin[0]+offset[0],self.origin[1]+offset[1])
self._draw(pos)
for index in range(len(self.children)):
child = self.children[index]
angle = 2*math.pi/len(self.children)*index+self.angle_offset
x = math.sin(angle)*self.r+pos[0]
y = -math.cos(angle)*self.r+pos[1]
child.draw(offset=(x,y))
def _draw(self,pos):
if self.element_center:
self.element_center._draw(pos)
def test():
group = UIElement((10,0))
group.add(UIElement((10,10)))
group.add(UIElement((20,20)))
#group.draw()
ring = GroupRing(r=80)
ctx = hardware.get_ctx()
for i in range(12):
ring.add(Icon(str(i)))
hardware.display_update()
while True:
ctx.rectangle(0,0,240,240).rgb(1,1,1).fill()
ring.draw()
hardware.display_update()
ring.angle_offset+=2*math.pi/60
time.sleep(0.01)
hardware.display_update()
#test()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment