Compare commits
2 Commits
bf7907ce6f
...
ca6ca377df
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca6ca377df | ||
|
|
801a909c64 |
92
src/app.rs
92
src/app.rs
@@ -25,6 +25,7 @@ use std::{
|
|||||||
const SUMMARY_POINTS_PER_SERIES: usize = 42;
|
const SUMMARY_POINTS_PER_SERIES: usize = 42;
|
||||||
const HAND_FORCE_PANEL_COUNT: usize = 7;
|
const HAND_FORCE_PANEL_COUNT: usize = 7;
|
||||||
const HAND_FORCE_SEGMENT_COUNTS: [usize; HAND_FORCE_PANEL_COUNT] = [84, 84, 84, 84, 84, 70, 44];
|
const HAND_FORCE_SEGMENT_COUNTS: [usize; HAND_FORCE_PANEL_COUNT] = [84, 84, 84, 84, 84, 70, 44];
|
||||||
|
const HAND_FORCE_DISPLAY_THRESHOLD_N: f32 = 0.05;
|
||||||
const HAND_IMAGE_SIZE_PX: [f32; 2] = [971.0, 1117.0];
|
const HAND_IMAGE_SIZE_PX: [f32; 2] = [971.0, 1117.0];
|
||||||
const HAND_FINGERTIP_FORCE_ANCHORS_PX: [[f32; 2]; HAND_FINGERTIP_COUNT] = [
|
const HAND_FINGERTIP_FORCE_ANCHORS_PX: [[f32; 2]; HAND_FINGERTIP_COUNT] = [
|
||||||
[265.0, 495.0],
|
[265.0, 495.0],
|
||||||
@@ -246,7 +247,12 @@ impl EskinDesktopApp {
|
|||||||
|
|
||||||
fn analyze_spatial_force(&mut self, values: &[u32]) {
|
fn analyze_spatial_force(&mut self, values: &[u32]) {
|
||||||
if matches!(self.config_state.mode, SerialMode::Hand) {
|
if matches!(self.config_state.mode, SerialMode::Hand) {
|
||||||
self.latest_hand_spatial_forces = self.force_estimator.analyze_fingertips(values);
|
let enabled = std::array::from_fn(|tip_index| {
|
||||||
|
hand_segment_force(values, tip_index) > HAND_FORCE_DISPLAY_THRESHOLD_N
|
||||||
|
});
|
||||||
|
self.latest_hand_spatial_forces = self
|
||||||
|
.force_estimator
|
||||||
|
.analyze_fingertips_masked(values, &enabled);
|
||||||
self.latest_spatial_force = self.strongest_hand_spatial_force();
|
self.latest_spatial_force = self.strongest_hand_spatial_force();
|
||||||
} else {
|
} else {
|
||||||
self.latest_spatial_force = self.force_estimator.analyze(values);
|
self.latest_spatial_force = self.force_estimator.analyze(values);
|
||||||
@@ -707,33 +713,42 @@ fn update_hand_signal_histories(
|
|||||||
histories: &mut [Vec<f32>; HAND_FORCE_PANEL_COUNT],
|
histories: &mut [Vec<f32>; HAND_FORCE_PANEL_COUNT],
|
||||||
raw_values: &[u32],
|
raw_values: &[u32],
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let mut offset = 0usize;
|
|
||||||
let mut total_force = 0.0;
|
let mut total_force = 0.0;
|
||||||
for (index, (history, sample_count)) in histories
|
for (index, history) in histories.iter_mut().enumerate() {
|
||||||
.iter_mut()
|
let force = hand_segment_force(raw_values, index);
|
||||||
.zip(HAND_FORCE_SEGMENT_COUNTS)
|
let force = if force <= HAND_FORCE_DISPLAY_THRESHOLD_N {
|
||||||
.enumerate()
|
0.0
|
||||||
{
|
} else {
|
||||||
let raw_total = raw_values
|
force
|
||||||
.get(offset..offset + sample_count)
|
};
|
||||||
.unwrap_or(&[])
|
|
||||||
.iter()
|
|
||||||
.fold(0_u64, |sum, value| sum + *value as u64)
|
|
||||||
.min(u32::MAX as u64) as u32;
|
|
||||||
let force = raw_to_hand_segment_g(index, raw_total).min(20.6);
|
|
||||||
let force = if force <= 0.1 { 0.0 } else { force };
|
|
||||||
total_force += force;
|
total_force += force;
|
||||||
history.push(force);
|
history.push(force);
|
||||||
if history.len() > SUMMARY_POINTS_PER_SERIES {
|
if history.len() > SUMMARY_POINTS_PER_SERIES {
|
||||||
history.remove(0);
|
history.remove(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += sample_count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
total_force
|
total_force
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hand_segment_force(raw_values: &[u32], index: usize) -> f32 {
|
||||||
|
let offset: usize = HAND_FORCE_SEGMENT_COUNTS[..index.min(HAND_FORCE_PANEL_COUNT)]
|
||||||
|
.iter()
|
||||||
|
.sum();
|
||||||
|
let sample_count = HAND_FORCE_SEGMENT_COUNTS
|
||||||
|
.get(index)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let raw_total = raw_values
|
||||||
|
.get(offset..offset + sample_count)
|
||||||
|
.unwrap_or(&[])
|
||||||
|
.iter()
|
||||||
|
.fold(0_u64, |sum, value| sum + *value as u64)
|
||||||
|
.min(u32::MAX as u64) as u32;
|
||||||
|
|
||||||
|
raw_to_hand_segment_g(index, raw_total).min(20.6)
|
||||||
|
}
|
||||||
|
|
||||||
fn raw_to_hand_segment_g(index: usize, raw: u32) -> f32 {
|
fn raw_to_hand_segment_g(index: usize, raw: u32) -> f32 {
|
||||||
match index {
|
match index {
|
||||||
0 => raw_to_g1(raw),
|
0 => raw_to_g1(raw),
|
||||||
@@ -741,8 +756,8 @@ fn raw_to_hand_segment_g(index: usize, raw: u32) -> f32 {
|
|||||||
2 => raw_to_g3(raw),
|
2 => raw_to_g3(raw),
|
||||||
3 => raw_to_g4(raw),
|
3 => raw_to_g4(raw),
|
||||||
4 => raw_to_g5(raw),
|
4 => raw_to_g5(raw),
|
||||||
// 5 => raw_to_g6(raw),
|
5 => raw_to_g6(raw),
|
||||||
// 6 => raw_to_g7(raw),
|
6 => raw_to_g7(raw),
|
||||||
_ => raw_to_gd(raw),
|
_ => raw_to_gd(raw),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -784,7 +799,7 @@ fn raw_to_gd(raw: u32) -> f32 {
|
|||||||
|
|
||||||
fn raw_to_g1(raw: u32) -> f32 {
|
fn raw_to_g1(raw: u32) -> f32 {
|
||||||
const RAW: [u32; 11] = [
|
const RAW: [u32; 11] = [
|
||||||
0, 106443, 365678, 449286, 532425, 608187, 685522, 797058, 886997, 1082570, 1244793,
|
0, 35694, 226640, 319868, 398021, 450448, 510506, 592075, 655760, 771671, 813967,
|
||||||
];
|
];
|
||||||
const FORCE_CENTI_N: [f32; 11] = [
|
const FORCE_CENTI_N: [f32; 11] = [
|
||||||
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
||||||
@@ -819,7 +834,7 @@ fn raw_to_g1(raw: u32) -> f32 {
|
|||||||
|
|
||||||
fn raw_to_g2(raw: u32) -> f32 {
|
fn raw_to_g2(raw: u32) -> f32 {
|
||||||
const RAW: [u32; 11] = [
|
const RAW: [u32; 11] = [
|
||||||
0, 111143, 310153, 375593, 427920, 497433, 548676, 648392, 715118, 877487, 994887,
|
0, 45490, 267579, 352066, 431681, 457605, 550875, 624327, 732735, 883397, 899338,
|
||||||
];
|
];
|
||||||
const FORCE_CENTI_N: [f32; 11] = [
|
const FORCE_CENTI_N: [f32; 11] = [
|
||||||
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
||||||
@@ -854,7 +869,7 @@ fn raw_to_g2(raw: u32) -> f32 {
|
|||||||
|
|
||||||
fn raw_to_g3(raw: u32) -> f32 {
|
fn raw_to_g3(raw: u32) -> f32 {
|
||||||
const RAW: [u32; 11] = [
|
const RAW: [u32; 11] = [
|
||||||
0, 66898, 271692, 354094, 443663, 534994, 603246, 721942, 837520, 1030111, 1216809,
|
0, 51562, 252840, 326531, 386571, 415819, 514679, 631745, 678945, 810847, 900422,
|
||||||
];
|
];
|
||||||
const FORCE_CENTI_N: [f32; 11] = [
|
const FORCE_CENTI_N: [f32; 11] = [
|
||||||
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
||||||
@@ -889,7 +904,7 @@ fn raw_to_g3(raw: u32) -> f32 {
|
|||||||
|
|
||||||
fn raw_to_g4(raw: u32) -> f32 {
|
fn raw_to_g4(raw: u32) -> f32 {
|
||||||
const RAW: [u32; 11] = [
|
const RAW: [u32; 11] = [
|
||||||
0, 122337, 381708, 480527, 577445, 677301, 762306, 906751, 1019182, 1254601, 1415051,
|
0, 33213, 220611, 307383, 369637, 430462, 489659, 584789, 664960, 802749, 926907,
|
||||||
];
|
];
|
||||||
const FORCE_CENTI_N: [f32; 11] = [
|
const FORCE_CENTI_N: [f32; 11] = [
|
||||||
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
||||||
@@ -924,7 +939,7 @@ fn raw_to_g4(raw: u32) -> f32 {
|
|||||||
|
|
||||||
fn raw_to_g5(raw: u32) -> f32 {
|
fn raw_to_g5(raw: u32) -> f32 {
|
||||||
const RAW: [u32; 11] = [
|
const RAW: [u32; 11] = [
|
||||||
0, 82466, 287752, 356167, 433286, 506855, 604014, 671789, 770692, 887287, 1032058,
|
0, 44291, 216657, 301950, 366186, 384791, 496521, 566189, 701459, 845608, 940796,
|
||||||
];
|
];
|
||||||
const FORCE_CENTI_N: [f32; 11] = [
|
const FORCE_CENTI_N: [f32; 11] = [
|
||||||
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
||||||
@@ -959,7 +974,7 @@ fn raw_to_g5(raw: u32) -> f32 {
|
|||||||
|
|
||||||
fn raw_to_g6(raw: u32) -> f32 {
|
fn raw_to_g6(raw: u32) -> f32 {
|
||||||
const RAW: [u32; 11] = [
|
const RAW: [u32; 11] = [
|
||||||
0, 0, 4290, 8096, 10567, 14856, 19685, 43856, 84675, 114001, 129068,
|
0, 0, 2532, 5122, 7256, 11525, 13544, 16044, 18504, 29778, 41845,
|
||||||
];
|
];
|
||||||
const FORCE_CENTI_N: [f32; 11] = [
|
const FORCE_CENTI_N: [f32; 11] = [
|
||||||
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
||||||
@@ -994,7 +1009,7 @@ fn raw_to_g6(raw: u32) -> f32 {
|
|||||||
|
|
||||||
fn raw_to_g7(raw: u32) -> f32 {
|
fn raw_to_g7(raw: u32) -> f32 {
|
||||||
const RAW: [u32; 11] = [
|
const RAW: [u32; 11] = [
|
||||||
0, 10081, 40910, 49944, 64291, 68299, 79282, 97767, 113567, 140520, 167132,
|
0, 233, 9000, 12067, 14311, 16297, 18113, 27918, 32559, 50861, 65857,
|
||||||
];
|
];
|
||||||
const FORCE_CENTI_N: [f32; 11] = [
|
const FORCE_CENTI_N: [f32; 11] = [
|
||||||
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
|
||||||
@@ -1082,8 +1097,8 @@ struct PressureColorRange {
|
|||||||
gamma: f32,
|
gamma: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The shader begins its orange-to-red transition at intensity 0.66. With this
|
// Controls the pressure-to-color response curve. Higher values reserve warmer
|
||||||
// gamma, a raw cell value at 80% of its configured range maps to that point.
|
// colors for larger fractions of each panel's configured raw range.
|
||||||
const PRESSURE_COLOR_GAMMA: f32 = 1.5;
|
const PRESSURE_COLOR_GAMMA: f32 = 1.5;
|
||||||
|
|
||||||
impl PressureColorRange {
|
impl PressureColorRange {
|
||||||
@@ -1128,13 +1143,13 @@ impl PressureColorRange {
|
|||||||
const DEFAULT_COLOR_RANGE: PressureColorRange = PressureColorRange::new(0, 7000);
|
const DEFAULT_COLOR_RANGE: PressureColorRange = PressureColorRange::new(0, 7000);
|
||||||
|
|
||||||
const HAND_SENSOR_PANEL_COLOR_RANGES: [PressureColorRange; HAND_FORCE_PANEL_COUNT] = [
|
const HAND_SENSOR_PANEL_COLOR_RANGES: [PressureColorRange; HAND_FORCE_PANEL_COUNT] = [
|
||||||
PressureColorRange::new(0, 1244793),
|
PressureColorRange::new(0, 813967),
|
||||||
PressureColorRange::new(0, 994887),
|
PressureColorRange::new(0, 899338),
|
||||||
PressureColorRange::new(0, 1216809),
|
PressureColorRange::new(0, 900422),
|
||||||
PressureColorRange::new(0, 1415051),
|
PressureColorRange::new(0, 926907),
|
||||||
PressureColorRange::new(0, 1032058),
|
PressureColorRange::new(0, 940796),
|
||||||
PressureColorRange::new(0, 32648),
|
PressureColorRange::new(0, 41845),
|
||||||
PressureColorRange::new(0, 32648),
|
PressureColorRange::new(0, 65857),
|
||||||
];
|
];
|
||||||
|
|
||||||
const HAND_FINGER_SENSOR_CELLS: usize = 12 * 7;
|
const HAND_FINGER_SENSOR_CELLS: usize = 12 * 7;
|
||||||
@@ -1223,13 +1238,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pressure_normalization_starts_red_transition_at_eighty_percent() {
|
fn pressure_normalization_applies_configured_gamma() {
|
||||||
let range = PressureColorRange::new(0, 10_000);
|
let range = PressureColorRange::new(0, 10_000);
|
||||||
let [intensity, _] = range.normalize(8_000);
|
let [intensity, _] = range.normalize(8_000);
|
||||||
|
let expected = 0.8_f32.powf(PRESSURE_COLOR_GAMMA);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
(intensity - 0.66).abs() < 0.002,
|
(intensity - expected).abs() < 0.002,
|
||||||
"80% of the configured raw range should map to the shader's red-transition point"
|
"the normalized response should follow the configured gamma"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14
src/force.rs
14
src/force.rs
@@ -2,7 +2,7 @@ use crate::serial_core::multi_dim_force::PztProcessor;
|
|||||||
|
|
||||||
pub const FINGER_SAMPLE_COUNT: usize = 84;
|
pub const FINGER_SAMPLE_COUNT: usize = 84;
|
||||||
pub const HAND_FINGERTIP_COUNT: usize = 5;
|
pub const HAND_FINGERTIP_COUNT: usize = 5;
|
||||||
const MIN_TANGENTIAL_MAGNITUDE: f32 = 0.02;
|
const MIN_TANGENTIAL_MAGNITUDE: f32 = 0.1;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct HudSpatialForce {
|
pub struct HudSpatialForce {
|
||||||
@@ -50,10 +50,22 @@ impl ForceEstimatorState {
|
|||||||
pub fn analyze_fingertips(
|
pub fn analyze_fingertips(
|
||||||
&mut self,
|
&mut self,
|
||||||
values: &[u32],
|
values: &[u32],
|
||||||
|
) -> [Option<HudSpatialForce>; HAND_FINGERTIP_COUNT] {
|
||||||
|
self.analyze_fingertips_masked(values, &[true; HAND_FINGERTIP_COUNT])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn analyze_fingertips_masked(
|
||||||
|
&mut self,
|
||||||
|
values: &[u32],
|
||||||
|
enabled: &[bool; HAND_FINGERTIP_COUNT],
|
||||||
) -> [Option<HudSpatialForce>; HAND_FINGERTIP_COUNT] {
|
) -> [Option<HudSpatialForce>; HAND_FINGERTIP_COUNT] {
|
||||||
let mut forces = [None; HAND_FINGERTIP_COUNT];
|
let mut forces = [None; HAND_FINGERTIP_COUNT];
|
||||||
|
|
||||||
for (tip_index, processor) in self.fingertip_processors.iter_mut().enumerate() {
|
for (tip_index, processor) in self.fingertip_processors.iter_mut().enumerate() {
|
||||||
|
if !enabled[tip_index] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let start = tip_index * FINGER_SAMPLE_COUNT;
|
let start = tip_index * FINGER_SAMPLE_COUNT;
|
||||||
let end = start + FINGER_SAMPLE_COUNT;
|
let end = start + FINGER_SAMPLE_COUNT;
|
||||||
let Some(segment) = values.get(start..end) else {
|
let Some(segment) = values.get(start..end) else {
|
||||||
|
|||||||
Reference in New Issue
Block a user