10 Commits

Author SHA1 Message Date
lenn
e75d55e0fb refactor: adjust resultant force zero threshold from 0.4 to 0.1 2026-05-18 10:48:54 +08:00
lenn
83832139a8 Merge branch 'display'
# Conflicts:
#	.gitignore
#	package-lock.json
#	package.json
#	src-tauri/Cargo.lock
#	src-tauri/Cargo.toml
#	src-tauri/tauri.conf.json
#	src/routes/+page.svelte
2026-04-29 16:08:46 +08:00
lenn
50700e73df update .gitignore 2026-04-20 09:32:43 +08:00
lenn
7e047daf00 remove log file 2026-04-15 09:50:29 +08:00
lenn
842d4980d9 remove target dirs 2026-04-15 09:49:33 +08:00
lenn
04a0c3e46b remove unnecessary dotfiles 2026-04-15 09:46:19 +08:00
lenn
d4e1da6219 更新软件版本号 2026-04-15 09:44:54 +08:00
lenn
d1c9be56ec Merge branch 'main' of https://gitea.e-skin.top/E-Skin/JE-Skin 2026-04-15 09:28:09 +08:00
lenn
2e30fa388a 导出数据添加summary列 2026-04-15 09:20:56 +08:00
Lenn
4b2203e008 合并请求 #2
feat:增加点和数字切换,减小点最大尺寸,增加range配色方案
2026-04-09 01:18:56 +00:00
7 changed files with 77 additions and 27 deletions

2
package-lock.json generated
View File

@@ -6,7 +6,7 @@
"packages": { "packages": {
"": { "": {
"name": "JE-Skin", "name": "JE-Skin",
"version": "0.3.0", "version": "0.4.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",

View File

@@ -1,7 +1,9 @@
# Generated by Cargo # Generated by Cargo
# will have compiled files and executables # will have compiled files and executables
/target/ /target/
/target-*/
# Generated by Tauri # Generated by Tauri
# will have schema files for capabilities auto-completion # will have schema files for capabilities auto-completion
/gen/schemas /gen/schemas
*log*

View File

@@ -263,6 +263,7 @@ impl CsvExporter<TactileARepFrame> for TactileACsvExporter {
} }
header.push("dts".to_string()); header.push("dts".to_string());
header.push("summary".to_string());
header header
} }
@@ -271,8 +272,10 @@ impl CsvExporter<TactileARepFrame> for TactileACsvExporter {
item: &RecordedFrame<TactileARepFrame>, item: &RecordedFrame<TactileARepFrame>,
) -> anyhow::Result<Vec<String>> { ) -> anyhow::Result<Vec<String>> {
let packet = TactileADataPacket::try_from(&item.frame)?; let packet = TactileADataPacket::try_from(&item.frame)?;
let summary: i32 = packet.data.iter().sum();
let mut row: Vec<String> = packet.data.iter().map(|x| x.to_string()).collect(); let mut row: Vec<String> = packet.data.iter().map(|x| x.to_string()).collect();
row.push(packet.dts_ms.to_string()); row.push(packet.dts_ms.to_string());
row.push(summary.to_string());
Ok(row) Ok(row)
} }
} }

View File

@@ -322,7 +322,6 @@ where
let force = raw_to_g1(summary as u32); let force = raw_to_g1(summary as u32);
push_devkit_frame(&app, vals.as_slice(), frame.dts_ms(), force); push_devkit_frame(&app, vals.as_slice(), frame.dts_ms(), force);
} }
pending_sub_frame = Some(PendingSubFrame { pending_sub_frame = Some(PendingSubFrame {
frame: frame.clone(), frame: frame.clone(),
values: vals, values: vals,
@@ -398,12 +397,12 @@ fn infer_matrix_shape(len: usize) -> (u32, u32) {
} }
fn raw_to_g1(raw: u32) -> f64 { fn raw_to_g1(raw: u32) -> f64 {
const X: [u32; 12] = [ const X: [u32; 11] = [
0, 84402, 117218, 140176, 159126, 175812, 191484, 208758, 224703, 252448, 302361, 352703, 0, 75507, 93732, 122031, 145263, 168630, 189980, 226021, 253636, 307140, 361368,
]; ];
const Y: [f64; 12] = [ const Y: [f64; 11] = [
0.0, 160.0, 260.0, 360.0, 460.0, 560.0, 660.0, 760.0, 860.0, 1060.0, 1560.0, 2060.0, 0.0, 197.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0,
]; ];
let n = X.len(); let n = X.len();

View File

@@ -68,6 +68,7 @@
let replaySide: "left" | "right" = "right"; let replaySide: "left" | "right" = "right";
const minRailScale = 0.2; const minRailScale = 0.2;
const resultantForceZeroThreshold = 0.1;
const dispatch = createEventDispatcher<{ const dispatch = createEventDispatcher<{
configclose: void; configclose: void;
replaytoggle: void; replaytoggle: void;
@@ -81,7 +82,8 @@
$: replaySide = summarySide === "left" ? "right" : "left"; $: replaySide = summarySide === "left" ? "right" : "left";
$: replayToggleButtonText = replayIsPlaying ? replayPauseLabel : replayPlayLabel; $: replayToggleButtonText = replayIsPlaying ? replayPauseLabel : replayPlayLabel;
$: replayProgressPercent = Math.round(Math.min(1, Math.max(0, replayProgress)) * 100); $: replayProgressPercent = Math.round(Math.min(1, Math.max(0, replayProgress)) * 100);
$: summaryCurveVisible = summary.points.length > 0 && summary.points.some((value) => Number.isFinite(value) && Math.abs(value) >= 0.0001); $: summaryCurveVisible =
summary.latest != null && Number.isFinite(summary.latest) && summary.latest > resultantForceZeroThreshold;
$: splitMatrixTitle = locale === "zh-CN" ? "数字矩阵" : "Matrix"; $: splitMatrixTitle = locale === "zh-CN" ? "数字矩阵" : "Matrix";
$: splitMatrixHint = locale === "zh-CN" ? "实时压力数据 / 数字矩阵" : "Live pressure matrix"; $: splitMatrixHint = locale === "zh-CN" ? "实时压力数据 / 数字矩阵" : "Live pressure matrix";

View File

@@ -145,6 +145,10 @@
return "--"; return "--";
} }
if (value === 0) {
return "0";
}
return value.toFixed(1); return value.toFixed(1);
} }

View File

@@ -169,6 +169,7 @@
const summaryPointsPerSeries = 42; const summaryPointsPerSeries = 42;
const signalRenderTickMs = 1200; const signalRenderTickMs = 1200;
const replayDefaultFrameMs = 40; const replayDefaultFrameMs = 40;
const resultantForceZeroThreshold = 0.1;
const showSignalPanels = false; const showSignalPanels = false;
const mockToneCycle: SignalTone[] = ["cyan", "lime", "orange", "violet", "gold", "rose"]; const mockToneCycle: SignalTone[] = ["cyan", "lime", "orange", "violet", "gold", "rose"];
@@ -707,6 +708,21 @@
return new Array<number>(totalCells).fill(0); return new Array<number>(totalCells).fill(0);
} }
function shouldZeroPressureMatrix(summaryValue: HudSummary): boolean {
return summaryValue.latest != null && Number.isFinite(summaryValue.latest) && summaryValue.latest === 0;
}
function resolvePressureMatrixForSummary(
sourceMatrix: number[] | null,
summaryValue: HudSummary
): number[] | null {
if (shouldZeroPressureMatrix(summaryValue)) {
return buildZeroMatrix();
}
return sourceMatrix;
}
function resetReplayVisualState(): void { function resetReplayVisualState(): void {
pressureMatrix = buildZeroMatrix(); pressureMatrix = buildZeroMatrix();
signalPanels = buildInactivePanels(); signalPanels = buildInactivePanels();
@@ -743,9 +759,10 @@
replayCurrentIndex = safeIndex; replayCurrentIndex = safeIndex;
replayHasDisplayedFrame = true; replayHasDisplayedFrame = true;
replayProgress = replayFrames.length > 1 ? safeIndex / (replayFrames.length - 1) : 1; replayProgress = replayFrames.length > 1 ? safeIndex / (replayFrames.length - 1) : 1;
pressureMatrix = frameValuesToMatrix(replayFrames[safeIndex].values); const nextSummary = buildReplaySummaryAt(safeIndex);
pressureMatrix = resolvePressureMatrixForSummary(frameValuesToMatrix(replayFrames[safeIndex].values), nextSummary);
signalPanels = buildInactivePanels(); signalPanels = buildInactivePanels();
summary = buildReplaySummaryAt(safeIndex); summary = nextSummary;
hasSignalData = true; hasSignalData = true;
} }
@@ -911,16 +928,37 @@
}; };
} }
function isZeroLikeValue(value: number): boolean { function normalizeResultantForce(value: number): number {
return !Number.isFinite(value) || Math.abs(value) < 0.0001; if (!Number.isFinite(value)) {
return 0;
}
return value <= resultantForceZeroThreshold ? 0 : value;
} }
function shouldHideSummary(points: number[]): boolean { function normalizeNullableResultantForce(value: number | null): number | null {
return points.length === 0 || points.every((value) => isZeroLikeValue(value)); return value == null ? null : normalizeResultantForce(value);
} }
function normalizeSummary(summaryValue: HudSummary): HudSummary { function normalizeSummary(summaryValue: HudSummary): HudSummary {
return shouldHideSummary(summaryValue.points) ? buildEmptySummary() : summaryValue; if (summaryValue.points.length === 0) {
return {
...summaryValue,
latest: normalizeNullableResultantForce(summaryValue.latest),
min: normalizeNullableResultantForce(summaryValue.min),
max: normalizeNullableResultantForce(summaryValue.max)
};
}
const points = summaryValue.points.map(normalizeResultantForce);
return {
...summaryValue,
points,
latest: points[points.length - 1],
min: Math.min(...points),
max: Math.max(...points)
};
} }
function buildSummary(points: number[], xValues: number[] = []): HudSummary { function buildSummary(points: number[], xValues: number[] = []): HudSummary {
@@ -928,7 +966,8 @@
return buildEmptySummary(); return buildEmptySummary();
} }
const resolvedXValues = points.map((_, index) => { const normalizedPoints = points.map(normalizeResultantForce);
const resolvedXValues = normalizedPoints.map((_, index) => {
const x = xValues[index]; const x = xValues[index];
return Number.isFinite(x) ? Number(x) : index + 1; return Number.isFinite(x) ? Number(x) : index + 1;
}); });
@@ -936,10 +975,10 @@
return { return {
label: "Resultant Force", label: "Resultant Force",
xValues: resolvedXValues, xValues: resolvedXValues,
points, points: normalizedPoints,
latest: points[points.length - 1], latest: normalizedPoints[normalizedPoints.length - 1],
min: Math.min(...points), min: Math.min(...normalizedPoints),
max: Math.max(...points) max: Math.max(...normalizedPoints)
}; };
} }
@@ -985,20 +1024,21 @@
if (replayHasData) { if (replayHasData) {
return; return;
} }
const normalizedSummary = normalizeSummary(packet.summary);
signalPanels = showSignalPanels ? packet.panels : buildInactivePanels(); signalPanels = showSignalPanels ? packet.panels : buildInactivePanels();
if (packet.summary.points.length > 0) { if (normalizedSummary.points.length > 0) {
const nowSeconds = Math.round((Date.now() - sessionStartedAt) / 100) / 10; const nowSeconds = Math.round((Date.now() - sessionStartedAt) / 100) / 10;
const pointCount = packet.summary.points.length; const pointCount = normalizedSummary.points.length;
const spacing = const spacing =
pointCount > 1 ? Math.min(1.2, nowSeconds / Math.max(pointCount - 1, 1)) : 0; pointCount > 1 ? Math.min(1.2, nowSeconds / Math.max(pointCount - 1, 1)) : 0;
const startX = Math.max(0, nowSeconds - spacing * Math.max(pointCount - 1, 0)); const startX = Math.max(0, nowSeconds - spacing * Math.max(pointCount - 1, 0));
const xValues = packet.summary.points.map((_, index) => Math.round((startX + index * spacing) * 10) / 10); const xValues = normalizedSummary.points.map((_, index) => Math.round((startX + index * spacing) * 10) / 10);
summary = { ...packet.summary, xValues }; summary = { ...normalizedSummary, xValues };
} else { } else {
summary = packet.summary; summary = normalizedSummary;
} }
pressureMatrix = packet.pressureMatrix; pressureMatrix = resolvePressureMatrixForSummary(packet.pressureMatrix, normalizedSummary);
hasSignalData = signalPanels.length > 0 || packet.summary.points.length > 0; hasSignalData = signalPanels.length > 0 || normalizedSummary.points.length > 0;
} }
function clearHudPanels(): void { function clearHudPanels(): void {