Skip to content
Snippets Groups Projects
Commit 4f981d3c authored by iggy's avatar iggy Committed by q3k
Browse files

py: st4m gesture scrolling with inertia wip

parent 875a0291
No related branches found
No related tags found
1 merge request!35py: grand st3m unification
......@@ -69,8 +69,116 @@ menu_main = FlowerMenu(
)
from st4m.input import PetalController
import math
from st3m.system import hardware
class PetalResponder(st4m.Responder):
def __init__(self):
self.petal = PetalController(0)
self.ts = 0.0
self._last = ""
self.speed = 0.0
self.value = 0.0
self.speedbuffer = [0.0]
self._ignore = 0
def think(self, ins: InputState, delta_ms: int) -> None:
self.ts += delta_ms
self.petal.think(ins, delta_ms)
if self._ignore:
self._ignore -= 1
return
dx = self.petal._input._dx
dphi = self.petal._input._dphi
phase = self.petal._input.phase()
speed = sum(self.speedbuffer) / len(self.speedbuffer)
if abs(speed) < 0.00001:
speed = 0
max = 50
if speed > max:
speed = max
if speed < -max:
speed = -max
if len(self.speedbuffer) > 10:
self.speedbuffer.pop(0)
if phase == self.petal._input.ENDED:
self.speedbuffer = [0 if abs(speed) < 0.001 else speed]
elif phase == self.petal._input.UP:
self.speedbuffer.append(speed * 0.85)
elif phase == self.petal._input.BEGIN:
self._ignore = 5
self.speedbuffer = [0.0]
elif phase == self.petal._input.RESTING:
self.speedbuffer.append(0.0)
elif phase == self.petal._input.MOVED:
self.speedbuffer.append(dphi / delta_ms)
# self.speed *= 0.3
# print(speed, phase, self.speedbuffer)
self.value += speed * delta_ms
self.speed = speed
def draw(self, ctx: Ctx) -> None:
tau = math.pi * 2
dx = self.petal._input._dx
dy = self.petal._input._dy
sum = abs(dx) + abs(dy)
pos = self.petal._input._pos
# ang = -math.atan2(*pos) - tau / 6 * 2
ang = self.value
p = self.petal._input.phase()
self._last = p
# if p == "up" and self._last != "up":
# print(p)
# if p != "up" and p != "resting":
# print(p, dx, dy, sum, ang)
ctx.gray(0)
ctx.rectangle(-120, -120, 240, 240).fill()
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.font_size = 20
ctx.gray(1)
# ctx.rectangle(-20, -20, 40, 40).fill()
ctx.move_to(0, 0).text(p)
ctx.move_to(0, 60).text(f"value: {self.value:.2f}")
dir = "stop"
if self.speed > 0:
dir = "ccw"
if self.speed < 0:
dir = "cw"
ctx.move_to(0, 90).text(f"{dir}: {self.speed*1000:.2f}")
# if p != "up":
ctx.rectangle(math.sin(ang) * 100, math.cos(ang) * 100, 20, 20).fill()
(r, phi) = self.petal._input._polar
p = (math.sin(phi) * r, math.cos(phi) * r)
ctx.rgb(1, 0, 0).rectangle(p[0], p[1], 10, 10).fill()
pr = PetalResponder()
vm.push(menu_main)
reactor = st4m.Reactor()
reactor.set_top(vm)
# reactor.set_top(pr)
reactor.run()
......@@ -4,6 +4,8 @@ from st4m.ui.ctx import Ctx
import hardware
import captouch
import math
class InputState:
"""
......@@ -16,11 +18,14 @@ class InputState:
def __init__(
self,
petal_pressed: List[bool],
petal_pads: List[List[int]],
# petal_pads: List[List[int]],
petal_pos: List[List[int]],
left_button: int,
right_button: int,
) -> None:
self.petal_pressed = petal_pressed
# self.petal_pads = petal_pads
self.petal_pos = petal_pos
self.left_button = left_button
self.right_button = right_button
......@@ -32,14 +37,19 @@ class InputState:
"""
cts = captouch.read()
petal_pressed = [cts.petals[i].pressed for i in range(10)]
petal_pads = [
[0, 0, 0]
# petal_pads = [
# [hardware.captouch_get_petal_pad(petal_ix, pad_ix) for pad_ix in range(3)]
# for petal_ix in range(10)
# ]
petal_pos = [
ctx.petals[petal_ix].position
for petal_ix in range(10)
]
left_button = hardware.left_button_get()
right_button = hardware.right_button_get()
return InputState(petal_pressed, petal_pads, left_button, right_button)
return InputState(petal_pressed, petal_pos, left_button, right_button)
class RepeatSettings:
......@@ -48,10 +58,6 @@ class RepeatSettings:
self.subsequent = subsequent
class Slideable:
pass
class Pressable:
"""
A pressable button or button-acting object (like captouch petal in button
......@@ -186,6 +192,90 @@ class PetalState(Pressable):
super().__init__(False)
class Touchable(Pressable):
"""
An object that can receive touch gestures (captouch petal)
"""
BEGIN = "begin"
RESTING = "resting"
MOVED = "moved"
ENDED = "ended"
def __init__(self, pos=(0, 0)):
super().__init__(False)
self._pos = pos
self._prev_pos = pos
self._polar = self._prev_polar = (0, 0)
self._dx = 0.0
self._dy = 0.0
self._dphi = 0.0
self._dr = 0.0
def _update(self, ts, state, pos):
self._prev_pos = self._pos
self._pos = pos
self._prev_polar = self._polar
self._dx = self._pos[0] - self._prev_pos[0]
self._dy = self._pos[1] - self._prev_pos[1]
x0 = -pos[0] / 500
x1 = pos[1] / 500
phi = math.atan2(x0, x1) - math.pi / 2
r = math.sqrt(x0 * x0 + x1 * x1)
self._polar = (r, phi)
self._dr = self._polar[0] - self._prev_polar[0]
v = self._polar[1] - self._prev_polar[1]
for sign in [1, -1]:
t = v + sign * 2 * math.pi
if abs(t) < abs(v):
v = t
self._dphi = v
# self._dphi = min(
# self._polar[1] - self._prev_polar[1],
# self._polar[1] + 2 * math.pi - (self._prev_polar[1]),
# self._polar[1] - 2 * math.pi - (self._prev_polar[1]),
# )
super()._update(ts, state)
if self.state != self.DOWN:
self._dx = self._dy = self._dphi = self._dr = 0
else:
pass
# print(r, phi, self._dr, self._dphi)
def phase(self) -> str:
if self.state == self.UP:
return self.UP
if self.state == self.RELEASED:
return self.ENDED
if self.state == self.PRESSED:
return self.BEGIN
if self.state == self.DOWN or self.state == self.REPEATED:
if abs(self._dr) > 1 or abs(self._dphi) > 0.01:
return self.MOVED
else:
return self.RESTING
return "HUHUHU"
class PetalGestureState(Touchable):
def __init__(self, ix: int) -> None:
self.ix = ix
super().__init__()
def _update(self, ts: int, hr: InputState) -> None:
super()._update(ts, hr.petal_pressed[self.ix], hr.petal_pos[self.ix])
class CaptouchState:
"""
State of capacitive touch petals.
......@@ -289,10 +379,10 @@ class InputController:
self.right_shoulder._ignore_pressed()
class PetalSlideController:
class PetalController:
def __init__(self, ix):
self._ts = 0
self._input = PetalState(ix)
self._input = PetalGestureState(ix)
def think(self, hr: InputState, delta_ms: int) -> None:
self._ts += delta_ms
......
......@@ -123,8 +123,10 @@ class FlowerMenu(MenuController):
item.highlighted = False
item.rotation_time = 10000
current = self._scroll_controller.current_position()
self.ui.items_ring[int(current)].highlighted = True
self.ui.items_ring[int(current)].rotation_time = 3000
current_int = round(current) % len(self._items)
print("current", current, current_int)
self.ui.items_ring[current_int].highlighted = True
self.ui.items_ring[current_int].rotation_time = 3000
self.ui.angle_offset = math.pi - (tau * current / len(self.ui.items_ring))
self.ui.draw(ctx)
import st4m
from st4m.input import InputState
from st4m.input import InputState, PetalController
from st4m.ui.ctx import Ctx
from st4m import Responder
......@@ -56,7 +56,7 @@ class ScrollController(st4m.Responder):
press).
"""
self._target_position -= 1
self._velocity = -10
self._velocity = -30
def scroll_right(self) -> None:
"""
......@@ -64,7 +64,7 @@ class ScrollController(st4m.Responder):
press).
"""
self._target_position += 1
self._velocity = 10
self._velocity = 30
def think(self, ins: InputState, delta_ms: int) -> None:
if self._nitems == 0:
......@@ -97,7 +97,7 @@ class ScrollController(st4m.Responder):
Use this value to animate the scroll list.
"""
return self._current_position
return round(self._current_position, 4) % self._nitems
def target_position(self) -> int:
"""
......@@ -134,12 +134,15 @@ class ScrollController(st4m.Responder):
max_velocity = 500
velocity = self._velocity
if abs(velocity) < 1:
self._target_position = int(self._current_position)
if abs(diff) > 0.2:
# Apply force to reach target position.
if diff > 0:
velocity += 80 * delta
velocity += 5 * delta
else:
velocity -= 80 * delta
velocity -= 5 * delta
# Clamp velocity.
if velocity > max_velocity:
......@@ -147,12 +150,16 @@ class ScrollController(st4m.Responder):
if velocity < -max_velocity:
velocity = -max_velocity
self._velocity = velocity
elif diff == 0:
pass
print("at target")
else:
# Try to snap to target position.
pos = self._velocity > 0 and diff > 0
neg = self._velocity < 0 and diff < 0
if self.wrap or pos or neg:
self._current_position = self._target_position
print("snapped")
self._current_position = self._target_position % self._nitems
self._velocity = 0
self._physics_integrate(delta)
......@@ -163,3 +170,81 @@ class ScrollController(st4m.Responder):
self._current_position + self._velocity * delta
) % self._nitems
class GestureScrollController(ScrollController):
def __init__(self, petal_index, wrap=False):
super().__init__(wrap)
self._petal = PetalController(petal_index)
self._speedbuffer = [0.0]
self._ignore = 0
def think(self, ins: InputState, delta_ms: int) -> None:
# super().think(ins, delta_ms)
self._petal.think(ins, delta_ms)
if self._ignore:
self._ignore -= 1
return
dphi = self._petal._input._dphi
phase = self._petal._input.phase()
self._speedbuffer.append(self._velocity)
if len(self._speedbuffer) > 10:
self._speedbuffer.pop(0)
speed = sum(self._speedbuffer) / len(self._speedbuffer)
if phase == self._petal._input.ENDED:
self._speedbuffer = [0 if abs(speed) < 0.001 else speed]
elif phase == self._petal._input.UP:
# pass
self._speedbuffer.append(speed * 0.85)
elif phase == self._petal._input.BEGIN:
self._ignore = 5
self._speedbuffer = [0.0]
elif phase == self._petal._input.RESTING:
self._speedbuffer.append(0.0)
elif phase == self._petal._input.MOVED:
impulse = -dphi / delta_ms
# if impulse > 10:
# self._target_position -= 1
# elif impulse < -10:
# self._target_position += 1
self._speedbuffer.append(impulse)
if abs(speed) < 0.000001:
speed = 0
self._velocity = speed
self._current_position = (
self._current_position + self._velocity * delta_ms
) % self._nitems
microstep = round(self._current_position) - self._current_position
print("micro:", microstep)
print("v", self._velocity)
if self._velocity >= 0 and microstep > 0:
self._velocity -= microstep / 100
print("1")
elif self._velocity <= 0 and microstep > 0:
self._velocity += microstep / 100
print("2")
elif self._velocity >= 0 and microstep < 0:
self._velocity -= microstep / 50
print("3")
elif self._velocity <= 0 and microstep < 0:
self._velocity += microstep / 50
print("4")
# if self._velocity > 0:
# self._velocity -= microstep / 100
# else:
# self._velocity += microstep / 100
# if self._velocity > 0 and microstep > 0:
# self._velocity -= (0.5 - microstep) / 1000
# if self._velocity < 0 and microstep < 0:
# self._velocity -= (-0.5 + microstep) / 1000
# print(phase, self._speedbuffer)
......@@ -10,7 +10,7 @@ from st4m.ui.view import (
ViewTransitionSwipeLeft,
ViewTransitionSwipeRight,
)
from st4m.ui.interactions import ScrollController
from st4m.ui.interactions import ScrollController, GestureScrollController
from st4m.ui.ctx import Ctx
......@@ -101,7 +101,7 @@ class MenuController(ViewWithInputState):
def __init__(self, items: List[MenuItem], vm: Optional[ViewManager]) -> None:
self._items = items
self._scroll_controller = ScrollController()
self._scroll_controller = GestureScrollController(0)
self._scroll_controller.set_item_count(len(items))
self._view_manager = vm
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment