diff --git a/package-lock.json b/package-lock.json index 917ae2e..9db4a2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "JE-Skin", - "version": "0.3.0", + "version": "0.4.0", "license": "MIT", "dependencies": { "@tauri-apps/api": "^2", diff --git a/src-tauri/src/serial_core/serial.rs b/src-tauri/src/serial_core/serial.rs index fca733d..8eb1ddf 100644 --- a/src-tauri/src/serial_core/serial.rs +++ b/src-tauri/src/serial_core/serial.rs @@ -322,7 +322,6 @@ where let force = raw_to_g1(summary as u32); push_devkit_frame(&app, vals.as_slice(), frame.dts_ms(), force); } - pending_sub_frame = Some(PendingSubFrame { frame: frame.clone(), values: vals, @@ -398,12 +397,12 @@ fn infer_matrix_shape(len: usize) -> (u32, u32) { } fn raw_to_g1(raw: u32) -> f64 { - const X: [u32; 12] = [ - 0, 84402, 117218, 140176, 159126, 175812, 191484, 208758, 224703, 252448, 302361, 352703, + const X: [u32; 11] = [ + 0, 75507, 93732, 122031, 145263, 168630, 189980, 226021, 253636, 307140, 361368, ]; - const Y: [f64; 12] = [ - 0.0, 160.0, 260.0, 360.0, 460.0, 560.0, 660.0, 760.0, 860.0, 1060.0, 1560.0, 2060.0, + const Y: [f64; 11] = [ + 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(); diff --git a/src/lib/components/CenterStage.svelte b/src/lib/components/CenterStage.svelte index de63228..ab7863a 100644 --- a/src/lib/components/CenterStage.svelte +++ b/src/lib/components/CenterStage.svelte @@ -68,6 +68,7 @@ let replaySide: "left" | "right" = "right"; const minRailScale = 0.2; + const resultantForceZeroThreshold = 0.1; const dispatch = createEventDispatcher<{ configclose: void; replaytoggle: void; @@ -81,7 +82,8 @@ $: replaySide = summarySide === "left" ? "right" : "left"; $: replayToggleButtonText = replayIsPlaying ? replayPauseLabel : replayPlayLabel; $: 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"; $: splitMatrixHint = locale === "zh-CN" ? "实时压力数据 / 数字矩阵" : "Live pressure matrix"; diff --git a/src/lib/components/PressureMatrixViewer.svelte b/src/lib/components/PressureMatrixViewer.svelte index ad52a11..f37d8b6 100644 --- a/src/lib/components/PressureMatrixViewer.svelte +++ b/src/lib/components/PressureMatrixViewer.svelte @@ -145,6 +145,10 @@ return "--"; } + if (value === 0) { + return "0"; + } + return value.toFixed(1); } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index a9b8603..526a53d 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -169,6 +169,7 @@ const summaryPointsPerSeries = 42; const signalRenderTickMs = 1200; const replayDefaultFrameMs = 40; + const resultantForceZeroThreshold = 0.1; const showSignalPanels = false; const mockToneCycle: SignalTone[] = ["cyan", "lime", "orange", "violet", "gold", "rose"]; @@ -707,6 +708,21 @@ return new Array(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 { pressureMatrix = buildZeroMatrix(); signalPanels = buildInactivePanels(); @@ -743,9 +759,10 @@ replayCurrentIndex = safeIndex; replayHasDisplayedFrame = true; 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(); - summary = buildReplaySummaryAt(safeIndex); + summary = nextSummary; hasSignalData = true; } @@ -911,16 +928,37 @@ }; } - function isZeroLikeValue(value: number): boolean { - return !Number.isFinite(value) || Math.abs(value) < 0.0001; + function normalizeResultantForce(value: number): number { + if (!Number.isFinite(value)) { + return 0; + } + + return value <= resultantForceZeroThreshold ? 0 : value; } - function shouldHideSummary(points: number[]): boolean { - return points.length === 0 || points.every((value) => isZeroLikeValue(value)); + function normalizeNullableResultantForce(value: number | null): number | null { + return value == null ? null : normalizeResultantForce(value); } 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 { @@ -928,7 +966,8 @@ return buildEmptySummary(); } - const resolvedXValues = points.map((_, index) => { + const normalizedPoints = points.map(normalizeResultantForce); + const resolvedXValues = normalizedPoints.map((_, index) => { const x = xValues[index]; return Number.isFinite(x) ? Number(x) : index + 1; }); @@ -936,10 +975,10 @@ return { label: "Resultant Force", xValues: resolvedXValues, - points, - latest: points[points.length - 1], - min: Math.min(...points), - max: Math.max(...points) + points: normalizedPoints, + latest: normalizedPoints[normalizedPoints.length - 1], + min: Math.min(...normalizedPoints), + max: Math.max(...normalizedPoints) }; } @@ -985,20 +1024,21 @@ if (replayHasData) { return; } + const normalizedSummary = normalizeSummary(packet.summary); 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 pointCount = packet.summary.points.length; + const pointCount = normalizedSummary.points.length; const spacing = 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 xValues = packet.summary.points.map((_, index) => Math.round((startX + index * spacing) * 10) / 10); - summary = { ...packet.summary, xValues }; + const xValues = normalizedSummary.points.map((_, index) => Math.round((startX + index * spacing) * 10) / 10); + summary = { ...normalizedSummary, xValues }; } else { - summary = packet.summary; + summary = normalizedSummary; } - pressureMatrix = packet.pressureMatrix; - hasSignalData = signalPanels.length > 0 || packet.summary.points.length > 0; + pressureMatrix = resolvePressureMatrixForSummary(packet.pressureMatrix, normalizedSummary); + hasSignalData = signalPanels.length > 0 || normalizedSummary.points.length > 0; } function clearHudPanels(): void {