From 328777361fefdac7ba912329160451fd498b8f7e Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Mon, 23 Dec 2024 13:26:51 +0100 Subject: [PATCH 1/5] indent settings files --- python_payload/st3m/application_settings.py | 2 +- python_payload/st3m/settings.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/python_payload/st3m/application_settings.py b/python_payload/st3m/application_settings.py index 027b7818b9..f35fb93b15 100644 --- a/python_payload/st3m/application_settings.py +++ b/python_payload/st3m/application_settings.py @@ -120,7 +120,7 @@ def _save_application_data(): log.info("Could not save application settings: " + str(e)) with open(_application_settings_path, "w") as f: try: - f.write(json.dumps(_application_data)) + f.write(json.dumps(_application_data, indent=4)) except Exception as e: log.info("Could not save application settings: " + str(e)) diff --git a/python_payload/st3m/settings.py b/python_payload/st3m/settings.py index b35d077701..7f125da2f2 100644 --- a/python_payload/st3m/settings.py +++ b/python_payload/st3m/settings.py @@ -377,7 +377,9 @@ def save_all() -> None: for setting in load_save_settings: res = _update(res, setting.save()) try: - saved_settings = save_file_if_changed(SETTINGS_JSON_FILE, json.dumps(res)) + saved_settings = save_file_if_changed( + SETTINGS_JSON_FILE, json.dumps(res, indent=4) + ) except Exception as e: log.warning("Could not save settings: " + str(e)) return -- GitLab From 05ef2fdcffceaf69fb287c7c03d68d1acf8a53fd Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Mon, 23 Dec 2024 14:22:06 +0100 Subject: [PATCH 2/5] widgets: various minor fixes --- python_payload/st3m/input.py | 29 ++++---- python_payload/st3m/ui/elements/menus.py | 10 ++- python_payload/st3m/ui/interactions.py | 32 ++++----- python_payload/st3m/ui/widgets/__init__.py | 78 ++++++++++++++++------ 4 files changed, 94 insertions(+), 55 deletions(-) diff --git a/python_payload/st3m/input.py b/python_payload/st3m/input.py index 8d199b6395..00aebf7966 100644 --- a/python_payload/st3m/input.py +++ b/python_payload/st3m/input.py @@ -279,7 +279,6 @@ class Pressable: class TouchableState(Enum): UP = "up" BEGIN = "begin" - RESTING = "resting" MOVED = "moved" ENDED = "ended" @@ -345,16 +344,17 @@ class Touchable: def __init__(self, ix) -> None: self._state = self.UP - self._dis = None - self._vel = 0j self._ts_prev = 0 conf = captouch.Config.empty() self._scroller = widgets.Scroller( conf, ix, constraint=widgets.constraints.Ellipse() ) + # somewhat illegal, just for the backwards hack... self._scroller.constraint = None + self._active_prev = False + self._cached_gesture = None def _update(self, ts: int, petal: captouch.CaptouchPetalState) -> None: """ @@ -363,23 +363,20 @@ class Touchable: self._scroller.think(None, ts - self._ts_prev, petal) self._ts_prev = ts + self._cached_gesture = None if self._scroller.active: if self._active_prev == self._scroller.active: self._state = self.MOVED - self._dis = self._scroller.pos else: self._state = self.BEGIN - self._vel = 0j - self._dis = 0j else: - if self._active_prev == self._scroller.active: - self._state = self.UP - self._dis = None - else: - self._state = self.ENDED - self._vel = self._scroller._vel + self._state = self.ENDED + if self._scroller.pos or self._scroller._vel: + self._cached_gesture = self.current_gesture() self._scroller.pos = 0j self._scroller._vel = 0j + elif self._active_prev == self._scroller.active: + self._state = self.UP self._active_prev = self._scroller.active def phase(self) -> TouchableState: @@ -389,10 +386,12 @@ class Touchable: return self._state def current_gesture(self) -> Optional[Gesture]: - if self._dis is None: + if self._cached_gesture: + return self._cached_gesture + if self._state == self.UP: return None - dis = self._dis * 35000 - vel = self._vel * 35000 * 1000 + dis = self._scroller.pos * 35000 + vel = self._scroller._vel * 35000 * 1000 return self.Gesture(*[(p.real, p.imag) for p in [dis, vel]]) diff --git a/python_payload/st3m/ui/elements/menus.py b/python_payload/st3m/ui/elements/menus.py index 8e2e4506e0..f5bbae1c39 100644 --- a/python_payload/st3m/ui/elements/menus.py +++ b/python_payload/st3m/ui/elements/menus.py @@ -97,8 +97,14 @@ class OSMenu(SimpleMenu): def __init__(self, items: List[MenuItem]) -> None: super().__init__(items) - self.captouch_config = captouch.Config.default() - self._scroller = widgets.MultiItemScroller(self.captouch_config, petals=[2, 8]) + self.captouch_config = captouch.Config.empty() + for x in range(10): + self.captouch_config.petals[x].set_min_mode(1) + self.captouch_config.petals[5].mode = 0 + + self._scroller = widgets.MultiItemScroller( + self.captouch_config, petals=[2, 8], friction=1 + ) self._scroller.max_item = len(items) - 1 self._scroll_controller = self._FakeScrollController(self._scroller) self.sensitivity = 1.5 diff --git a/python_payload/st3m/ui/interactions.py b/python_payload/st3m/ui/interactions.py index 6dc1ec230b..c0fb5fd66e 100644 --- a/python_payload/st3m/ui/interactions.py +++ b/python_payload/st3m/ui/interactions.py @@ -207,14 +207,21 @@ class CapScrollController: """ Call this in your think() method. """ - if t.phase() == t.BEGIN: - self._grab_start = self.position - self.momentum = (0.0, 0.0) - - if t.phase() == t.MOVED and self._grab_start is not None: + if t.phase() == t.UP: + rad_p, phi_p = self.position + rad_m, phi_m = self.momentum + rad_p += rad_m / (1000 / delta_ms) + phi_p += phi_m / (1000 / delta_ms) + rad_m *= self._damp + phi_m *= self._damp + self.momentum = (rad_m, phi_m) + self.position = (rad_p, phi_p) + self._grab_start = None + else: + if self._grab_start is None: + self._grab_start = self.position move = t.current_gesture() assert move is not None - assert self._grab_start is not None drad, dphi = move.distance drad /= 1000 dphi /= 1000 @@ -222,20 +229,7 @@ class CapScrollController: sphi = self._grab_start[1] self.position = (srad + drad, sphi + dphi) - if t.phase() == t.ENDED: - move = t.current_gesture() - assert move is not None vrad, vphi = move.velocity vrad /= 1000 vphi /= 1000 self.momentum = (vrad, vphi) - - if t.phase() == t.UP: - rad_p, phi_p = self.position - rad_m, phi_m = self.momentum - rad_p += rad_m / (1000 / delta_ms) - phi_p += phi_m / (1000 / delta_ms) - rad_m *= self._damp - phi_m *= self._damp - self.momentum = (rad_m, phi_m) - self.position = (rad_p, phi_p) diff --git a/python_payload/st3m/ui/widgets/__init__.py b/python_payload/st3m/ui/widgets/__init__.py index 500cea8153..e72e3bfdd4 100644 --- a/python_payload/st3m/ui/widgets/__init__.py +++ b/python_payload/st3m/ui/widgets/__init__.py @@ -241,6 +241,7 @@ class CaptouchWidget(Widget): ) def _apply_velocity(self, delta_ms): + vel_prev = self._vel if (not self._vel) or abs(self._vel) < 0.0001: self._vel = 0j return False @@ -437,7 +438,7 @@ class Scroller(PetalWidget): def _autoclear(self): self._update_pos() self._ref = None - if self._log.length_ms() > 40: + if self._log.length_ms() > 27: start = 0 stop = self._log.length() - 1 if stop > 2: @@ -446,7 +447,12 @@ class Scroller(PetalWidget): start = self._log.index_offset_ms(-1, -70) if self._log.index_offset_ms(start, 20) is not None: vel = self._log.slope_per_ms(start, stop) - self._vel = vel * self.gain + # gate val experimentally determined on petal 2 + # optimum might be different for each petal but good enough for now + if abs(vel) < 0.01: + self._vel = 0 + else: + self._vel = vel * self.gain self._pos_range = None # trigger callbacks self.active = True @@ -662,30 +668,36 @@ class MultiSlider(CaptouchWidget): class MultiItemScroller(CaptouchWidget): - def __init__(self, config, *, petals): - self._widgets = [] - super().__init__(config) + def __init__(self, config, *, petals, gain=1, friction=0.7): + super().__init__(config, gain=gain, friction=friction) self._item = 0 self._max_item = None + self._vel = 0 petals = set(petals) for petal in petals: if petal not in range(0, 10, 2): raise ValueError("all petals must be top petals") - constraint = constraints.Rectangle(100) + constraint = constraints.Rectangle() self._widgets = [ Scroller( config, x, gain=self.gain * captouch.PETAL_ANGLES[x], - friction=1, + friction=self.friction, constraint=constraint, ) for x in petals ] for widget in self._widgets: widget.ref_precision = 3 + widget.constraint = None + + def add_to_config(self, config): + if hasattr(self, "_widgets"): + for widget in self._widgets: + widget.add_to_config(config) @property def gain(self): @@ -694,8 +706,22 @@ class MultiItemScroller(CaptouchWidget): @gain.setter def gain(self, value): self._gain = value - for widget in self._widgets: - widget.gain = value * captouch.PETAL_ANGLES[widget.petal] + if hasattr(self, "_widgets"): + for widget in self._widgets: + widget.gain = value * captouch.PETAL_ANGLES[widget.petal] + + @property + def friction(self): + return self._friction + + @friction.setter + def friction(self, value): + if value != 1: + raise ValueError("unfinished widget, friction must be 1 for now, sorry!") + self._friction = value + if hasattr(self, "_widgets"): + for widget in self._widgets: + widget.friction = value @property def item(self): @@ -725,10 +751,6 @@ class MultiItemScroller(CaptouchWidget): self._max_item = val self.item = self._item - def add_to_config(self, config): - for widget in self._widgets: - widget.add_to_config(config) - def on_enter(self): for widget in self._widgets: widget.on_enter() @@ -749,12 +771,30 @@ class MultiItemScroller(CaptouchWidget): else: widget.pos = 0j - offset = self._item + delta - self.pos - if offset > 4: - offset = 4 - elif offset < -4: - offset = -4 - self.pos += offset * (1 - (0.002 ** (delta_ms / 1000))) + target = self._item + delta + + delta_ms_phys = delta_ms + while delta_ms_phys: + if delta_ms_phys < 50: + delta_s = delta_ms_phys / 1000 + delta_ms_phys = 0 + else: + delta_s = 50 / 1000 + delta_ms_phys -= 50 + offset = target - self.pos + if offset > 4: + offset = 4 + elif offset < -4: + offset = -4 + acc = offset * 175 + acc -= self._vel * 25 + # unit: items per second + self._vel += acc * delta_s + # if self._vel > 8: + # self._vel = 8 + # elif self._vel < -8: + # self._vel = -8 + self.pos += self._vel * delta_s delta_hyst = int(delta * 1.5) if delta_hyst: -- GitLab From 897fd7a8740ff760026cf87ada20b8d9acbb0491 Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Mon, 23 Dec 2024 16:53:30 +0100 Subject: [PATCH 3/5] menu: fix delete target This is due to a subtle change in behavior of our duck _scroll_controller, but it's a good one so we'll keep it. --- python_payload/st3m/main_menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_payload/st3m/main_menu.py b/python_payload/st3m/main_menu.py index 94f1e58e30..d24f918248 100644 --- a/python_payload/st3m/main_menu.py +++ b/python_payload/st3m/main_menu.py @@ -251,10 +251,10 @@ class ApplicationMenu(RestoreMenu): self.tags = [] def _pop_target_item(self): - self._scroll_controller.set_item_count(len(self._items) - 1) pos = self._scroll_controller.target_position() self._items.pop(pos) self._scroll_controller.set_position(pos - 1) + self._scroll_controller.set_item_count(len(self._items)) def _sort(self): def sort_key(item): -- GitLab From cde043461e00c90ac8514fb415615b1a8caaa917 Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Mon, 23 Dec 2024 17:26:25 +0100 Subject: [PATCH 4/5] menu: fix post sort scroll --- python_payload/st3m/ui/elements/menus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python_payload/st3m/ui/elements/menus.py b/python_payload/st3m/ui/elements/menus.py index f5bbae1c39..902b57fa59 100644 --- a/python_payload/st3m/ui/elements/menus.py +++ b/python_payload/st3m/ui/elements/menus.py @@ -93,6 +93,7 @@ class OSMenu(SimpleMenu): self._scroller.item = int(position) def set_position(self, position): + self._scroller.item = int(position) self._scroller.pos = int(position) def __init__(self, items: List[MenuItem]) -> None: -- GitLab From e7f774c9872720241eed9f6f441effdc5fa73c14 Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Mon, 23 Dec 2024 17:57:57 +0100 Subject: [PATCH 5/5] captouch: linearized bottom petal pos a bit --- components/flow3r_bsp/flow3r_bsp_ad7147.c | 27 ++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/components/flow3r_bsp/flow3r_bsp_ad7147.c b/components/flow3r_bsp/flow3r_bsp_ad7147.c index 811450d072..0f41fc3a67 100644 --- a/components/flow3r_bsp/flow3r_bsp_ad7147.c +++ b/components/flow3r_bsp/flow3r_bsp_ad7147.c @@ -1401,6 +1401,16 @@ static inline void pos_calc_bot(int index, int mode, int *rad, int *raw_sum, int rad_calc = tip - base; rad_calc *= petal_pos_gain[index]; rad_calc /= ((tip + base) >> 2) + 1; + + rad_calc = 16384 - rad_calc; + + int rad_calc_gain = rad_calc > 0 ? rad_calc : 0; + rad_calc_gain = (rad_calc_gain + 32768) >> 5; + rad_calc_gain = (rad_calc_gain * rad_calc_gain) >> 12; + rad_calc = (rad_calc * rad_calc_gain) >> 10; + + rad_calc = 16384 - rad_calc; + *rad = rad_calc; } else { *rad = 0; @@ -1435,9 +1445,20 @@ static inline void pos_calc_top2(int index, int mode, int *rad, int *phi, } } -static void pos_calc_bot2(int index, int mode, int *rad, int *raw_sum, - uint16_t *raw_petal) { - pos_calc_bot(index, mode, rad, raw_sum, raw_petal); +static inline void pos_calc_bot2(int index, int mode, int *rad, int *raw_sum, + uint16_t *raw_petal) { + int32_t tip = raw_petal[petal_pad_tip]; + int32_t base = raw_petal[petal_pad_base]; + *raw_sum = base + tip; + if (mode > 1) { + base += ((base * 3) >> 2); // tiny gain correction + int rad_calc = tip - base; + rad_calc *= petal_pos_gain[index]; + rad_calc /= ((tip + base) >> 2) + 1; + *rad = rad_calc; + } else { + *rad = 0; + } } #endif -- GitLab