From 00d4e51ebe008c6c3946e94f951e8c634c3146b8 Mon Sep 17 00:00:00 2001 From: Jabberrock Date: Fri, 15 Nov 2024 21:03:05 -0800 Subject: [PATCH 1/2] [Stay Aligned] Add small yaw rotations to keep trackers aligned --- gui/public/i18n/en/translation.ftl | 20 + .../components/settings/SettingsSidebar.tsx | 3 + .../settings/pages/GeneralSettings.tsx | 63 ++- .../pages/components/StayAlignedSettings.tsx | 381 ++++++++++++++++ .../components/tracker/StayAlignedInfo.tsx | 50 +++ gui/src/components/tracker/TrackersTable.tsx | 13 + .../widgets/IMUVisualizerWidget.tsx | 10 + gui/src/hooks/datafeed-config.ts | 1 + gui/src/maths/angle.ts | 21 + .../dev/slimevr/config/StayAlignedConfig.kt | 49 ++ .../main/java/dev/slimevr/config/VRConfig.kt | 2 + .../serializers/AngleInDegConversions.kt | 27 ++ .../src/main/java/dev/slimevr/math/Angle.kt | 92 ++++ .../java/dev/slimevr/math/AngleAverage.kt | 22 + .../protocol/datafeed/DataFeedBuilder.java | 29 ++ .../rpc/settings/RPCSettingsBuilder.java | 26 +- .../rpc/settings/RPCSettingsHandler.kt | 16 + .../tracking/processor/HumanPoseManager.kt | 16 +- .../processor/skeleton/HumanSkeleton.kt | 11 + .../processor/stayaligned/StayAligned.kt | 51 +++ .../stayaligned/StayAlignedDefaults.kt | 59 +++ .../stayaligned/adjust/AdjustTrackerYaw.kt | 178 ++++++++ .../stayaligned/adjust/AngleErrors.kt | 31 ++ .../stayaligned/adjust/CenterErrorVisitor.kt | 151 +++++++ .../stayaligned/adjust/LockedErrorVisitor.kt | 126 ++++++ .../adjust/NeighborErrorVisitor.kt | 190 ++++++++ .../stayaligned/adjust/TrackerYaw.kt | 59 +++ .../stayaligned/skeleton/RelaxedBodyAngles.kt | 69 +++ .../processor/stayaligned/skeleton/Side.kt | 6 + .../stayaligned/skeleton/TrackerPose.kt | 54 +++ .../stayaligned/skeleton/TrackerSkeleton.kt | 419 ++++++++++++++++++ .../skeleton/TrackerSkeletonPose.kt | 93 ++++ .../stayaligned/state/RestDetector.kt | 55 +++ .../state/StayAlignedTrackerState.kt | 43 ++ .../stayaligned/state/YawCorrection.kt | 31 ++ .../processor/stayaligned/state/YawErrors.kt | 9 + .../dev/slimevr/tracking/trackers/Tracker.kt | 8 + .../tracking/trackers/TrackerResetsHandler.kt | 4 + .../io/github/axisangles/ktmath/Quaternion.kt | 68 ++- .../io/github/axisangles/ktmath/Vector3.kt | 3 + .../axisangles/ktmath/QuaternionTest.kt | 18 + solarxr-protocol | 2 +- 42 files changed, 2574 insertions(+), 5 deletions(-) create mode 100644 gui/src/components/settings/pages/components/StayAlignedSettings.tsx create mode 100644 gui/src/components/tracker/StayAlignedInfo.tsx create mode 100644 server/core/src/main/java/dev/slimevr/config/StayAlignedConfig.kt create mode 100644 server/core/src/main/java/dev/slimevr/config/serializers/AngleInDegConversions.kt create mode 100644 server/core/src/main/java/dev/slimevr/math/Angle.kt create mode 100644 server/core/src/main/java/dev/slimevr/math/AngleAverage.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/StayAligned.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/StayAlignedDefaults.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/adjust/AdjustTrackerYaw.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/adjust/AngleErrors.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/adjust/CenterErrorVisitor.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/adjust/LockedErrorVisitor.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/adjust/NeighborErrorVisitor.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/adjust/TrackerYaw.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/skeleton/RelaxedBodyAngles.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/skeleton/Side.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/skeleton/TrackerPose.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/skeleton/TrackerSkeleton.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/skeleton/TrackerSkeletonPose.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/state/RestDetector.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/state/StayAlignedTrackerState.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/state/YawCorrection.kt create mode 100644 server/core/src/main/java/dev/slimevr/tracking/processor/stayaligned/state/YawErrors.kt diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl index 6c33547edc..12485399fc 100644 --- a/gui/public/i18n/en/translation.ftl +++ b/gui/public/i18n/en/translation.ftl @@ -185,6 +185,7 @@ widget-imu_visualizer-rotation_raw = Raw rotation widget-imu_visualizer-rotation_preview = Preview rotation widget-imu_visualizer-acceleration = Acceleration widget-imu_visualizer-position = Position +widget-imu_visualizer-stay_aligned = Stay Aligned ## Widget: Skeleton Visualizer widget-skeleton_visualizer-preview = Skeleton preview @@ -209,6 +210,7 @@ tracker-table-column-temperature = Temp. °C tracker-table-column-linear-acceleration = Accel. X/Y/Z tracker-table-column-rotation = Rotation X/Y/Z tracker-table-column-position = Position X/Y/Z +tracker-table-column-stay_aligned = Stay Aligned tracker-table-column-url = URL ## Tracker rotation @@ -338,6 +340,7 @@ mounting_selection_menu-close = Close settings-sidebar-title = Settings settings-sidebar-general = General settings-sidebar-tracker_mechanics = Tracker mechanics +settings-sidebar-stay_aligned = Stay Aligned settings-sidebar-fk_settings = Tracking settings settings-sidebar-gesture_control = Gesture control settings-sidebar-interface = Interface @@ -428,6 +431,23 @@ settings-general-tracker_mechanics-use_mag_on_all_trackers-description = Can be disabled per tracker in the tracker's settings. Please don't shutdown any of the trackers while toggling this! settings-general-tracker_mechanics-use_mag_on_all_trackers-label = Use magnetometer on trackers +settings-general-stay_aligned = Stay Aligned +settings-general-stay_aligned-description = Keeps your trackers aligned by slowly adjusting the yaw of your trackers. +settings-general-stay_aligned-warnings-drift_compensation = ⚠ Please disable "Drift Compensation". Stay Aligned and Drift Compensation try to solve the same problem and only one should be enabled. +settings-general-stay_aligned-enabled-label = Enabled +settings-general-stay_aligned-amount-label = Maximum yaw adjustment rate +settings-general-stay_aligned-amount-description = Pick a rate depending on how good your IMU is: 0.1 deg/s for ICM45686, LSM6DSV or BNO085; 0.2 deg/s for LSM6DSR or BMI270; and 0.4 deg/s for BMI160. +settings-general-stay_aligned-relaxed_body_angles-label = Relaxed Body Angles +settings-general-stay_aligned-relaxed_body_angles-description = Stay Aligned needs to know your pose when you are relaxed. These angles describe how much you twist your legs and feet outwards. Do a yaw reset, then stand in a relaxed position, and press "Auto detect angles". Repeat while relaxing in a chair, and lying on your back. +settings-general-stay_aligned-relaxed_body_angles-standing-label = Standing +settings-general-stay_aligned-relaxed_body_angles-sitting-label = Sitting in chair +settings-general-stay_aligned-relaxed_body_angles-lying_on_back-label = Lying on back +settings-general-stay_aligned-relaxed_body_angles-upper_leg_angle = Upper leg +settings-general-stay_aligned-relaxed_body_angles-lower_leg_angle = Lower leg +settings-general-stay_aligned-relaxed_body_angles-foot_angle = Foot +settings-general-stay_aligned-relaxed_body_angles-auto_detect = Auto detect angles +settings-general-stay_aligned-relaxed_body_angles-reset = Reset angles + ## FK/Tracking settings settings-general-fk_settings = Tracking settings diff --git a/gui/src/components/settings/SettingsSidebar.tsx b/gui/src/components/settings/SettingsSidebar.tsx index ad6ddb29b6..d84e398eb3 100644 --- a/gui/src/components/settings/SettingsSidebar.tsx +++ b/gui/src/components/settings/SettingsSidebar.tsx @@ -58,6 +58,9 @@ export function SettingsSidebar() { {l10n.getString('settings-sidebar-tracker_mechanics')} + + {l10n.getString('settings-general-stay_aligned')} + {l10n.getString('settings-sidebar-fk_settings')} diff --git a/gui/src/components/settings/pages/GeneralSettings.tsx b/gui/src/components/settings/pages/GeneralSettings.tsx index 3946b551fc..e07f41f7a7 100644 --- a/gui/src/components/settings/pages/GeneralSettings.tsx +++ b/gui/src/components/settings/pages/GeneralSettings.tsx @@ -16,6 +16,7 @@ import { SettingsResponseT, SteamVRTrackersSettingT, TapDetectionSettingsT, + YawCorrectionSettingsT, } from 'solarxr-protocol'; import { useConfig } from '@/hooks/config'; import { useWebsocketAPI } from '@/hooks/websocket-api'; @@ -33,8 +34,9 @@ import { import { HandsWarningModal } from '@/components/settings/HandsWarningModal'; import { MagnetometerToggleSetting } from './MagnetometerToggleSetting'; import { DriftCompensationModal } from '@/components/settings/DriftCompensationModal'; +import { StayAlignedSettings } from './components/StayAlignedSettings'; -interface SettingsForm { +export interface SettingsForm { trackers: { waist: boolean; chest: boolean; @@ -104,6 +106,19 @@ interface SettingsForm { saveMountingReset: boolean; resetHmdPitch: boolean; }; + yawCorrectionSettings: { + enabled: boolean; + amountInDegPerSec: number; + standingUpperLegAngle: number; + standingLowerLegAngle: number; + standingFootAngle: number; + sittingUpperLegAngle: number; + sittingLowerLegAngle: number; + sittingFootAngle: number; + lyingOnBackUpperLegAngle: number; + lyingOnBackLowerLegAngle: number; + hideYawCorrection: boolean; + }; } const defaultValues: SettingsForm = { @@ -171,6 +186,19 @@ const defaultValues: SettingsForm = { saveMountingReset: false, resetHmdPitch: false, }, + yawCorrectionSettings: { + enabled: true, + amountInDegPerSec: 0.2, + standingUpperLegAngle: 0.0, + standingLowerLegAngle: 0.0, + standingFootAngle: 0.0, + sittingUpperLegAngle: 0.0, + sittingLowerLegAngle: 0.0, + sittingFootAngle: 0.0, + lyingOnBackUpperLegAngle: 0.0, + lyingOnBackLowerLegAngle: 0.0, + hideYawCorrection: false, + }, }; export function GeneralSettings() { @@ -300,6 +328,30 @@ export function GeneralSettings() { driftCompensation.maxResets = values.driftCompensation.maxResets; settings.driftCompensation = driftCompensation; + const yawCorrectionSettings = new YawCorrectionSettingsT(); + yawCorrectionSettings.enabled = values.yawCorrectionSettings.enabled; + yawCorrectionSettings.amountInDegPerSec = + values.yawCorrectionSettings.amountInDegPerSec; + yawCorrectionSettings.standingUpperLegAngle = + values.yawCorrectionSettings.standingUpperLegAngle; + yawCorrectionSettings.standingLowerLegAngle = + values.yawCorrectionSettings.standingLowerLegAngle; + yawCorrectionSettings.standingFootAngle = + values.yawCorrectionSettings.standingFootAngle; + yawCorrectionSettings.sittingUpperLegAngle = + values.yawCorrectionSettings.sittingUpperLegAngle; + yawCorrectionSettings.sittingLowerLegAngle = + values.yawCorrectionSettings.sittingLowerLegAngle; + yawCorrectionSettings.sittingFootAngle = + values.yawCorrectionSettings.sittingFootAngle; + yawCorrectionSettings.lyingOnBackUpperLegAngle = + values.yawCorrectionSettings.lyingOnBackUpperLegAngle; + yawCorrectionSettings.lyingOnBackLowerLegAngle = + values.yawCorrectionSettings.lyingOnBackLowerLegAngle; + yawCorrectionSettings.hideYawCorrection = + values.yawCorrectionSettings.hideYawCorrection; + settings.yawCorrectionSettings = yawCorrectionSettings; + if (values.resetsSettings) { const resetsSettings = new ResetsSettingsT(); resetsSettings.resetMountingFeet = @@ -423,6 +475,10 @@ export function GeneralSettings() { formData.resetsSettings = settings.resetsSettings; } + if (settings.yawCorrectionSettings) { + formData.yawCorrectionSettings = settings.yawCorrectionSettings; + } + reset({ ...getValues(), ...formData }); }); @@ -824,6 +880,11 @@ export function GeneralSettings() { /> + } id="fksettings" diff --git a/gui/src/components/settings/pages/components/StayAlignedSettings.tsx b/gui/src/components/settings/pages/components/StayAlignedSettings.tsx new file mode 100644 index 0000000000..e9c117bda6 --- /dev/null +++ b/gui/src/components/settings/pages/components/StayAlignedSettings.tsx @@ -0,0 +1,381 @@ +import { FlatDeviceTracker } from '@/hooks/app'; +import { normalizeAngleAroundZero, RAD_TO_DEG } from '@/maths/angle'; +import { QuaternionFromQuatT } from '@/maths/quaternion'; +import { Control, FieldPath, UseFormSetValue } from 'react-hook-form'; +import { BodyPart } from 'solarxr-protocol'; +import { SettingsForm } from '@/components/settings/pages/GeneralSettings'; +import { Button } from '@/components/commons/Button'; +import { CheckBox } from '@/components/commons/Checkbox'; +import { WrenchIcon } from '@/components/commons/icon/WrenchIcons'; +import { NumberSelector } from '@/components/commons/NumberSelector'; +import { Typography } from '@/components/commons/Typography'; +import { SettingsPagePaneLayout } from '@/components/settings/SettingsPageLayout'; +import { useLocalization } from '@fluent/react'; +import { useTrackers } from '@/hooks/tracker'; +import { useLocaleConfig } from '@/i18n/config'; +import { Euler } from 'three'; + +export function StayAlignedSettings({ + getValues, + setValue, + control, +}: { + getValues: () => SettingsForm; + setValue: UseFormSetValue; + control: Control; +}) { + const { l10n } = useLocalization(); + const { currentLocales } = useLocaleConfig(); + const degreePerSecFormat = new Intl.NumberFormat(currentLocales, { + style: 'unit', + unit: 'degree-per-second', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }); + const degreeFormat = new Intl.NumberFormat(currentLocales, { + style: 'unit', + unit: 'degree', + maximumFractionDigits: 0, + }); + + const { useConnectedIMUTrackers } = useTrackers(); + const trackers = useConnectedIMUTrackers(); + + const values = getValues(); + + const yawBetweenInDeg = ( + leftTracker: FlatDeviceTracker, + rightTracker: FlatDeviceTracker + ) => { + const leftTrackerYaw = new Euler().setFromQuaternion( + QuaternionFromQuatT(leftTracker.tracker.rotationReferenceAdjusted), + 'YZX' + ).y; + const rightTrackerYaw = new Euler().setFromQuaternion( + QuaternionFromQuatT(rightTracker.tracker.rotationReferenceAdjusted), + 'YZX' + ).y; + const yawDelta = normalizeAngleAroundZero(leftTrackerYaw - rightTrackerYaw); + return yawDelta * RAD_TO_DEG; + }; + + function findTracker(bodyPart: BodyPart): FlatDeviceTracker | undefined { + return trackers.find((t) => t.tracker.info?.bodyPart === bodyPart); + } + + const detectAngles = ( + upperLegKey: FieldPath, + lowerLegKey: FieldPath, + footKey?: FieldPath + ) => { + const leftUpperLegTracker = findTracker(BodyPart.LEFT_UPPER_LEG); + const rightUpperLegTracker = findTracker(BodyPart.RIGHT_UPPER_LEG); + if (leftUpperLegTracker && rightUpperLegTracker) { + const upperLegToBodyAngleInDeg = + yawBetweenInDeg(leftUpperLegTracker, rightUpperLegTracker) / 2.0; + setValue(upperLegKey, Math.round(upperLegToBodyAngleInDeg)); + } + + const leftLowerLegTracker = findTracker(BodyPart.LEFT_LOWER_LEG); + const rightLowerLegTracker = findTracker(BodyPart.RIGHT_LOWER_LEG); + if (leftLowerLegTracker && rightLowerLegTracker) { + const footToBodyAngleInDeg = + yawBetweenInDeg(leftLowerLegTracker, rightLowerLegTracker) / 2.0; + setValue(lowerLegKey, Math.round(footToBodyAngleInDeg)); + } + + if (footKey) { + const leftFootTracker = findTracker(BodyPart.LEFT_FOOT); + const rightFootTracker = findTracker(BodyPart.RIGHT_FOOT); + if (leftFootTracker && rightFootTracker) { + const footToBodyAngleInDeg = + yawBetweenInDeg(leftFootTracker, rightFootTracker) / 2.0; + setValue(footKey, Math.round(footToBodyAngleInDeg)); + } + } + }; + + const resetAngles = ( + upperLegKey: FieldPath, + lowerLegKey: FieldPath, + footKey?: FieldPath + ) => { + setValue(upperLegKey, 0.0); + setValue(lowerLegKey, 0.0); + if (footKey) { + setValue(footKey, 0.0); + } + }; + + return ( + } id="stayaligned"> + + {l10n.getString('settings-general-stay_aligned')} + +
+ {l10n + .getString('settings-general-stay_aligned-description') + .split('\n') + .map((line, i) => ( + + {line} + + ))} + {values.yawCorrectionSettings.enabled && ( + <> + {!!values.driftCompensation.enabled && ( +
+ {l10n.getString( + 'settings-general-stay_aligned-warnings-drift_compensation' + )} +
+ )} + + )} +
+
+ +
+ + {l10n.getString('settings-general-stay_aligned-amount-label')} + + + {l10n.getString('settings-general-stay_aligned-amount-description')} + + degreePerSecFormat.format(value)} + min={0.02} + max={2.0} + step={0.02} + /> +
+
+ + {l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-label' + )} + + + {l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-description' + )} + +
+
+ + {l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-standing-label' + )} + +
+ + `${l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-upper_leg_angle' + )}: ${degreeFormat.format(value)}` + } + min={-90.0} + max={90.0} + step={1.0} + /> + + `${l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-lower_leg_angle' + )}: ${degreeFormat.format(value)}` + } + min={-90.0} + max={90.0} + step={1.0} + /> + + `${l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-foot_angle' + )}: ${degreeFormat.format(value)}` + } + min={-90.0} + max={90.0} + step={1.0} + /> + + +
+
+
+ + {l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-sitting-label' + )} + +
+ + `${l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-upper_leg_angle' + )}: ${degreeFormat.format(value)}` + } + min={-90.0} + max={90.0} + step={1.0} + /> + + `${l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-lower_leg_angle' + )}: ${degreeFormat.format(value)}` + } + min={-90.0} + max={90.0} + step={1.0} + /> + + `${l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-foot_angle' + )}: ${degreeFormat.format(value)}` + } + min={-90.0} + max={90.0} + step={1.0} + /> + + +
+
+
+ + {l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-lying_on_back-label' + )} + +
+ + `${l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-upper_leg_angle' + )}: ${degreeFormat.format(value)}` + } + min={-90.0} + max={90.0} + step={1.0} + /> + + `${l10n.getString( + 'settings-general-stay_aligned-relaxed_body_angles-lower_leg_angle' + )}: ${degreeFormat.format(value)}` + } + min={-90.0} + max={90.0} + step={1.0} + /> +
+ + +
+
+
+
+ ); +} diff --git a/gui/src/components/tracker/StayAlignedInfo.tsx b/gui/src/components/tracker/StayAlignedInfo.tsx new file mode 100644 index 0000000000..9092e5f35d --- /dev/null +++ b/gui/src/components/tracker/StayAlignedInfo.tsx @@ -0,0 +1,50 @@ +import { Typography } from '@/components/commons/Typography'; +import { useLocaleConfig } from '@/i18n/config'; +import { angleIsNearZero } from '@/maths/angle'; +import { TrackerDataT } from 'solarxr-protocol'; + +export function StayAlignedInfo({ + color, + tracker, +}: { + color: 'primary' | 'secondary'; + tracker: TrackerDataT; +}) { + const { currentLocales } = useLocaleConfig(); + const degreeFormat = new Intl.NumberFormat(currentLocales, { + style: 'unit', + unit: 'degree', + minimumFractionDigits: 1, + maximumFractionDigits: 1, + }); + const errorFormat = new Intl.NumberFormat(currentLocales, { + minimumFractionDigits: 1, + maximumFractionDigits: 1, + }); + + const locked = tracker.stayAlignedLocked ? '🔒' : ''; + + const delta = `Δ=${degreeFormat.format(tracker.stayAlignedYawCorrectionInDeg)}`; + + const errors = []; + const maxErrorToShow = 0.1; + if (!angleIsNearZero(tracker.stayAlignedLockedErrorInDeg, maxErrorToShow)) { + errors.push(`L=${errorFormat.format(tracker.stayAlignedLockedErrorInDeg)}`); + } + if (!angleIsNearZero(tracker.stayAlignedCenterErrorInDeg, maxErrorToShow)) { + errors.push(`C=${errorFormat.format(tracker.stayAlignedCenterErrorInDeg)}`); + } + if (!angleIsNearZero(tracker.stayAlignedNeighborErrorInDeg, maxErrorToShow)) { + errors.push( + `N=${errorFormat.format(tracker.stayAlignedNeighborErrorInDeg)}` + ); + } + + const error = errors.length > 0 ? `(${errors.join(', ')})` : ''; + + return ( + + {locked} {delta} {error} + + ); +} diff --git a/gui/src/components/tracker/TrackersTable.tsx b/gui/src/components/tracker/TrackersTable.tsx index cdba255964..f36cbcb236 100644 --- a/gui/src/components/tracker/TrackersTable.tsx +++ b/gui/src/components/tracker/TrackersTable.tsx @@ -17,6 +17,7 @@ import { TrackerBattery } from './TrackerBattery'; import { TrackerStatus } from './TrackerStatus'; import { TrackerWifi } from './TrackerWifi'; import { trackerStatusRelated, useStatusContext } from '@/hooks/status-system'; +import { StayAlignedInfo } from './StayAlignedInfo'; enum DisplayColumn { NAME, @@ -28,6 +29,7 @@ enum DisplayColumn { TEMPERATURE, LINEAR_ACCELERATION, POSITION, + STAY_ALIGNED, URL, } @@ -41,6 +43,7 @@ const displayColumns: { [k: string]: boolean } = { [DisplayColumn.TEMPERATURE]: true, [DisplayColumn.LINEAR_ACCELERATION]: true, [DisplayColumn.POSITION]: true, + [DisplayColumn.STAY_ALIGNED]: true, [DisplayColumn.URL]: true, }; @@ -196,6 +199,7 @@ export function TrackersTable({ displayColumns[DisplayColumn.TEMPERATURE] = hasTemperature || false; displayColumns[DisplayColumn.POSITION] = moreInfo || false; displayColumns[DisplayColumn.LINEAR_ACCELERATION] = moreInfo || false; + displayColumns[DisplayColumn.STAY_ALIGNED] = moreInfo || false; displayColumns[DisplayColumn.URL] = moreInfo || false; const displayColumnsKeys = Object.keys(displayColumns).filter( (k) => displayColumns[k] @@ -362,6 +366,15 @@ export function TrackersTable({ ), })} + {column({ + id: DisplayColumn.STAY_ALIGNED, + label: l10n.getString('tracker-table-column-stay_aligned'), + labelClassName: 'w-36', + row: ({ tracker }) => ( + + ), + })} + {column({ id: DisplayColumn.URL, label: l10n.getString('tracker-table-column-url'), diff --git a/gui/src/components/widgets/IMUVisualizerWidget.tsx b/gui/src/components/widgets/IMUVisualizerWidget.tsx index 5cecbbf9fa..4a130f38cd 100644 --- a/gui/src/components/widgets/IMUVisualizerWidget.tsx +++ b/gui/src/components/widgets/IMUVisualizerWidget.tsx @@ -12,6 +12,7 @@ import { useLocalization } from '@fluent/react'; import { Vector3Object } from '@/maths/vector3'; import { Gltf } from '@react-three/drei'; import { ErrorBoundary } from 'react-error-boundary'; +import { StayAlignedInfo } from '@/components/tracker/StayAlignedInfo'; const groundColor = '#4444aa'; @@ -157,6 +158,15 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) { )} + {!!tracker.stayAlignedYawCorrectionInDeg && ( +
+ + {l10n.getString('widget-imu_visualizer-stay_aligned')} + + +
+ )} + {!enabled && (