From 8d6d42ed3cf15958f000bf0918cdbbdfa83847b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:47:57 +0200 Subject: [PATCH] feat(pose): wrist-anchored xy mods + wrist markers The X/Y voice mods were anchored to the 21-landmark centroid, so opening or pinching the fingers shifted the mod values (gesture/mod cross-talk). cx/cy now track the WRIST (kp 0). The renderer draws a cross marker at each wrist on the video whenever features exist, so the control point stays visible even for far hands the overlay filters out (user request live 2026-07-02). --- data_only_viz/hand_features.py | 8 ++++++-- data_only_viz/renderer.py | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/data_only_viz/hand_features.py b/data_only_viz/hand_features.py index bb7650d..0b76516 100644 --- a/data_only_viz/hand_features.py +++ b/data_only_viz/hand_features.py @@ -62,8 +62,12 @@ class HandFeatureExtractor: def _features(self, lm: list) -> dict: xs = [_finite(_coord(p, "x", 0), 0.5) for p in lm[:21]] ys = [_finite(_coord(p, "y", 1), 0.5) for p in lm[:21]] - cx = _clamp(sum(xs) / len(xs), 0.0, 1.0) - cy = _clamp(sum(ys) / len(ys), 0.0, 1.0) + # cx/cy anchor = the WRIST (kp 0), not the landmark centroid: the + # centroid shifts when fingers open/pinch, cross-talking gestures + # into the X/Y voice mods. The wrist decouples them (user request + # live 2026-07-02). + cx = _clamp(xs[WRIST], 0.0, 1.0) + cy = _clamp(ys[WRIST], 0.0, 1.0) size = math.hypot(xs[MIDDLE_MCP] - xs[WRIST], ys[MIDDLE_MCP] - ys[WRIST]) size = size if size > 1e-4 else 1e-4 span = math.hypot(xs[THUMB_TIP] - xs[PINKY_TIP], diff --git a/data_only_viz/renderer.py b/data_only_viz/renderer.py index 2b11c35..0f7373a 100644 --- a/data_only_viz/renderer.py +++ b/data_only_viz/renderer.py @@ -575,6 +575,16 @@ class MetalRenderer(NSObject): content_pid=pid_s, ): push_panel(ax, ay, bx, by, gconf, gpid) + # Wrist marker ON the video: the X/Y voice mods are anchored + # to the wrist, so its control point stays visible whenever + # features exist (even for far hands the overlay filters + # out). push_seg applies the video mirror like the overlay. + if _g_cx is not None and _g_cy is not None: + _mx = 0.015 / asp + push_seg(_g_cx - _mx, _g_cy, _g_cx + _mx, _g_cy, + 1.0, pid_s) + push_seg(_g_cx, _g_cy - 0.015, _g_cx, _g_cy + 0.015, + 1.0, pid_s) if h_kp is not None: for seg in panel_segments( h_kp, side_name, lhand_bones_p, asp,