feat: add locale selector; move audio; detect connectivity
This commit is contained in:
parent
ada2b49700
commit
84dabc67f2
72 changed files with 183 additions and 123 deletions
4
client/src/globals.d.ts
vendored
4
client/src/globals.d.ts
vendored
|
@ -49,8 +49,8 @@ interface TelemetryData {
|
||||||
'jerk-x': number
|
'jerk-x': number
|
||||||
'jerk-y': number
|
'jerk-y': number
|
||||||
'voltage': number
|
'voltage': number
|
||||||
'acc-profile': Mode | '-999'
|
'acc-profile': Mode
|
||||||
'gear': Gear | '-999'
|
'gear': Gear
|
||||||
'ebrake': boolean
|
'ebrake': boolean
|
||||||
'reorient': boolean
|
'reorient': boolean
|
||||||
'gpws': boolean
|
'gpws': boolean
|
||||||
|
|
|
@ -18,6 +18,7 @@ export const setStationaryTelemetry = () => {
|
||||||
'ebrake': false,
|
'ebrake': false,
|
||||||
'reorient': false,
|
'reorient': false,
|
||||||
'gpws': false,
|
'gpws': false,
|
||||||
|
'connected': true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { Notifications } from '../../Notifications/notifications'
|
import { Notifications } from '../../Notifications/notifications'
|
||||||
import { settingsStore } from '../../stores/settingsStore'
|
import { settingsStore } from '../../stores/settingsStore'
|
||||||
import AppContainer from '../AppContainer.svelte'
|
import AppContainer from '../AppContainer.svelte'
|
||||||
|
import SettingsSelector from './SettingsSelector.svelte'
|
||||||
import SettingsInput from './SettingsInput.svelte'
|
import SettingsInput from './SettingsInput.svelte'
|
||||||
import SettingsToggle from './SettingsToggle.svelte'
|
import SettingsToggle from './SettingsToggle.svelte'
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
class="flex gap-6 bg-blue-200 bg-opacity-25 backdrop-blur-xl media-background rounded-3xl flex-wrap px-10 py-20"
|
class="flex gap-6 bg-blue-200 bg-opacity-25 backdrop-blur-xl media-background rounded-3xl flex-wrap px-10 py-20"
|
||||||
>
|
>
|
||||||
<h1 class="text-5xl font-medium text-slate-100 basis-full">Settings</h1>
|
<h1 class="text-5xl font-medium text-slate-100 basis-full">Settings</h1>
|
||||||
|
<p class="text-slate-300">Hover over setting names to see helpful tooltips</p>
|
||||||
<h2 class="text-2xl font-medium text-slate-200 mt-4 basis-full">General</h2>
|
<h2 class="text-2xl font-medium text-slate-200 mt-4 basis-full">General</h2>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<SettingsToggle
|
<SettingsToggle
|
||||||
|
@ -39,6 +41,12 @@
|
||||||
>
|
>
|
||||||
RNG Weight
|
RNG Weight
|
||||||
</SettingsInput>
|
</SettingsInput>
|
||||||
|
<SettingsSelector
|
||||||
|
setting="voiceLang"
|
||||||
|
options={['en-US', 'en-RU']}
|
||||||
|
tooltip="Selects the language/locale used for Jankboard voice prompts. Does not affect application language (ie. Jankboard itself will always be in English)."
|
||||||
|
>Voice Prompt Language</SettingsSelector
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
class="mt-10 px-4 py-2 bg-amber-600 hover:brightness-75 text-medium rounded-lg w-min"
|
class="mt-10 px-4 py-2 bg-amber-600 hover:brightness-75 text-medium rounded-lg w-min"
|
||||||
on:click={resetSettings}>Reset</button
|
on:click={resetSettings}>Reset</button
|
||||||
|
|
46
client/src/lib/Apps/Settings/SettingsSelector.svelte
Normal file
46
client/src/lib/Apps/Settings/SettingsSelector.svelte
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<!--
|
||||||
|
@component
|
||||||
|
|
||||||
|
A selector component that updates settings with the selected value. Designed
|
||||||
|
to be used with settings which have a fixed amount of set values. Only works with
|
||||||
|
settings with number or string values. Prefer the Toggle input type for boolean
|
||||||
|
settings.
|
||||||
|
|
||||||
|
@param setting - The setting to be toggled
|
||||||
|
@param options - The options to be shown in the selector. Must be possible (valid)
|
||||||
|
values for the setting.
|
||||||
|
@param tooltip - Helpful tooltip for the setting
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import { settingsStore } from '../../stores/settingsStore'
|
||||||
|
import type { SettingsStoreData } from '../../stores/settingsStore'
|
||||||
|
import { tooltip as tooltipAction } from '@svelte-plugins/tooltips'
|
||||||
|
|
||||||
|
export let setting: keyof SettingsStoreData
|
||||||
|
export let options: string[] | number[]
|
||||||
|
export let tooltip: string = ''
|
||||||
|
|
||||||
|
if (typeof setting !== 'string') {
|
||||||
|
throw new Error('Selector setting must be a string')
|
||||||
|
}
|
||||||
|
|
||||||
|
let selected: string | number = $settingsStore[setting] as string | number
|
||||||
|
|
||||||
|
// Setting is guaranteed to be string by guard clause above
|
||||||
|
// @ts-expect-error
|
||||||
|
$: selected && settingsStore.update(setting, selected)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex gap-2 my-1">
|
||||||
|
<select bind:value={selected} class="w-min bg-slate-400 text-md">
|
||||||
|
{#each options as option}
|
||||||
|
<option value={option}>{option}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
<label
|
||||||
|
class="text-xl font-medium text-slate-100"
|
||||||
|
for={setting}
|
||||||
|
use:tooltipAction={{ content: tooltip, action: 'hover', arrow: false }}
|
||||||
|
><slot /></label
|
||||||
|
>
|
||||||
|
</div>
|
|
@ -4,6 +4,7 @@
|
||||||
@param accx - Acceleration in x
|
@param accx - Acceleration in x
|
||||||
@param accy - Acceleration in y
|
@param accy - Acceleration in y
|
||||||
@param orientation - Heading in degrees
|
@param orientation - Heading in degrees
|
||||||
|
@param placeholder - Whether or not to show the placeholder skeleton
|
||||||
|
|
||||||
Displays the heading direction and acceleration as human readable text
|
Displays the heading direction and acceleration as human readable text
|
||||||
-->
|
-->
|
||||||
|
@ -15,9 +16,9 @@
|
||||||
export let accx: number
|
export let accx: number
|
||||||
export let accy: number
|
export let accy: number
|
||||||
export let orientation: number
|
export let orientation: number
|
||||||
|
export let placeholder: boolean
|
||||||
|
|
||||||
$: accResolved = Math.hypot(accx, accy)
|
$: accResolved = Math.hypot(accx, accy)
|
||||||
$: placeholder = accx === -999 && accy === -999
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 text-center">
|
<div class="flex flex-col gap-2 text-center">
|
||||||
|
|
|
@ -29,11 +29,18 @@
|
||||||
selectedGear={$telemetryReadonlyStore.gear}
|
selectedGear={$telemetryReadonlyStore.gear}
|
||||||
selectedMode={$telemetryReadonlyStore['acc-profile']}
|
selectedMode={$telemetryReadonlyStore['acc-profile']}
|
||||||
voltage={$telemetryReadonlyStore.voltage}
|
voltage={$telemetryReadonlyStore.voltage}
|
||||||
|
placeholder={!$telemetryReadonlyStore.connected}
|
||||||
/>
|
/>
|
||||||
<div class="h-0.5 mt-1 w-full bg-slate-300 border-0"></div>
|
<div class="h-0.5 mt-1 w-full bg-slate-300 border-0"></div>
|
||||||
<div class="mt-8 flex justify-between">
|
<div class="mt-8 flex justify-between">
|
||||||
<Speedometer speed={speedResolved} />
|
<Speedometer
|
||||||
<SpeedLimit speedLimit={-999} />
|
speed={speedResolved}
|
||||||
|
placeholder={!$telemetryReadonlyStore.connected}
|
||||||
|
/>
|
||||||
|
<SpeedLimit
|
||||||
|
speedLimit={5}
|
||||||
|
placeholder={!$telemetryReadonlyStore.connected}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -47,6 +54,7 @@
|
||||||
accx={$telemetryReadonlyStore['accx']}
|
accx={$telemetryReadonlyStore['accx']}
|
||||||
accy={$telemetryReadonlyStore['accy']}
|
accy={$telemetryReadonlyStore['accy']}
|
||||||
orientation={$telemetryReadonlyStore['orientation']}
|
orientation={$telemetryReadonlyStore['orientation']}
|
||||||
|
placeholder={!$telemetryReadonlyStore.connected}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<MediaDisplay />
|
<MediaDisplay />
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
@component
|
@component
|
||||||
|
|
||||||
@param speedLimit - Speed limit in Miles Per Hour (MPH)
|
@param speedLimit - Speed limit in Miles Per Hour (MPH)
|
||||||
|
@param placeholder - Whether or not to show the placeholder skeleton
|
||||||
|
|
||||||
Displays the speed limit
|
Displays the speed limit
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let speedLimit: number = 5.0
|
export let speedLimit: number
|
||||||
|
export let placeholder: boolean
|
||||||
$: placeholder = speedLimit === -999
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
@component
|
@component
|
||||||
|
|
||||||
@param speed - Speed in meters per second
|
@param speed - Speed in meters per second
|
||||||
|
@param placeholder - Whether or not to show the placeholder skeleton
|
||||||
|
|
||||||
Displays the speed in miles per hour
|
Displays the speed in miles per hour
|
||||||
-->
|
-->
|
||||||
|
@ -10,10 +11,9 @@
|
||||||
import { mps2mph } from '../utils/unitConversions'
|
import { mps2mph } from '../utils/unitConversions'
|
||||||
|
|
||||||
export let speed: number = 0.0
|
export let speed: number = 0.0
|
||||||
|
export let placeholder: boolean
|
||||||
|
|
||||||
$: formatted = mps2mph(speed).toFixed(1)
|
$: formatted = mps2mph(speed).toFixed(1)
|
||||||
|
|
||||||
$: placeholder = speed === Math.hypot(-999, -999)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
|
@ -24,10 +24,5 @@
|
||||||
>
|
>
|
||||||
{placeholder ? '-----' : formatted}
|
{placeholder ? '-----' : formatted}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="text-2xl font-medium transition" class:placeholder>MPH</div>
|
||||||
class="text-2xl font-medium transition"
|
|
||||||
class:placeholder={speed === Math.hypot(-999, -999)}
|
|
||||||
>
|
|
||||||
MPH
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let voltage: number
|
export let voltage: number
|
||||||
|
export let placeholder: boolean
|
||||||
|
|
||||||
$: formatted = voltage.toFixed(1)
|
$: formatted = voltage.toFixed(1)
|
||||||
|
|
||||||
$: placeholder = voltage === -999
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span class="flex gap-1">
|
<span class="flex gap-1">
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let selectedGear: Gear | '-999'
|
export let selectedGear: Gear
|
||||||
|
export let placeholder: boolean
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex justify-center w-full transition">
|
<div class="flex justify-center w-full transition">
|
||||||
<div
|
<div
|
||||||
class="flex flex-row gap-2 text-neutral-400 text-xl font-bold"
|
class="flex flex-row gap-2 text-neutral-400 text-xl font-bold"
|
||||||
class:placeholder={selectedGear === '-999'}
|
class:placeholder
|
||||||
>
|
>
|
||||||
<div class:highlighted={selectedGear === 'park'}>P</div>
|
<div class:highlighted={selectedGear === 'park' && !placeholder}>P</div>
|
||||||
<div class:highlighted={selectedGear === 'reverse'}>R</div>
|
<div class:highlighted={selectedGear === 'reverse' && !placeholder}>R</div>
|
||||||
<div class:highlighted={selectedGear === 'neutral'}>N</div>
|
<div class:highlighted={selectedGear === 'neutral' && !placeholder}>N</div>
|
||||||
<div class:highlighted={selectedGear === 'low'}>L</div>
|
<div class:highlighted={selectedGear === 'low' && !placeholder}>L</div>
|
||||||
<div class:highlighted={selectedGear === 'auto'}>A</div>
|
<div class:highlighted={selectedGear === 'auto' && !placeholder}>A</div>
|
||||||
<div class:highlighted={selectedGear === 'drive'}>D</div>
|
<div class:highlighted={selectedGear === 'drive' && !placeholder}>D</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,29 +2,34 @@
|
||||||
@component
|
@component
|
||||||
|
|
||||||
@param selectedMode - Selected mode
|
@param selectedMode - Selected mode
|
||||||
|
@param placeholder - Whether or not to show the placeholder skeleton
|
||||||
|
|
||||||
Displays the drive mode
|
Displays the drive mode
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { fade } from 'svelte/transition'
|
import { fade } from 'svelte/transition'
|
||||||
|
|
||||||
export let selectedMode: Mode | '-999'
|
export let selectedMode: Mode
|
||||||
|
export let placeholder: boolean
|
||||||
|
|
||||||
let modeText = ''
|
let modeText = ''
|
||||||
|
|
||||||
$: switch (selectedMode) {
|
$: {
|
||||||
case 'chill':
|
if (placeholder) {
|
||||||
modeText = 'CHILL'
|
|
||||||
break
|
|
||||||
case 'cruise':
|
|
||||||
modeText = 'CRUISE'
|
|
||||||
break
|
|
||||||
case 'ludicrous':
|
|
||||||
modeText = 'LUDICROUS'
|
|
||||||
break
|
|
||||||
case '-999':
|
|
||||||
modeText = 'DISCONNECTED'
|
modeText = 'DISCONNECTED'
|
||||||
break
|
} else {
|
||||||
|
switch (selectedMode) {
|
||||||
|
case 'chill':
|
||||||
|
modeText = 'CHILL'
|
||||||
|
break
|
||||||
|
case 'cruise':
|
||||||
|
modeText = 'CRUISE'
|
||||||
|
break
|
||||||
|
case 'ludicrous':
|
||||||
|
modeText = 'LUDICROUS'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
@param selectedGear - Selected gear
|
@param selectedGear - Selected gear
|
||||||
@param selectedMode - Selected mode
|
@param selectedMode - Selected mode
|
||||||
@param voltage - Battery voltage
|
@param voltage - Battery voltage
|
||||||
|
@param placeholder - Whether or not to show placeholder skeleton UIs
|
||||||
|
|
||||||
Displays the top bar of the dashboard
|
Displays the top bar of the dashboard
|
||||||
-->
|
-->
|
||||||
|
@ -13,19 +14,20 @@
|
||||||
import GearSelector from './GearSelector.svelte'
|
import GearSelector from './GearSelector.svelte'
|
||||||
import ModeSelector from './ModeSelector.svelte'
|
import ModeSelector from './ModeSelector.svelte'
|
||||||
|
|
||||||
export let selectedGear: Gear | '-999'
|
export let selectedGear: Gear
|
||||||
export let selectedMode: Mode | '-999'
|
export let selectedMode: Mode
|
||||||
export let voltage: number
|
export let voltage: number
|
||||||
|
export let placeholder: boolean
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-row w-full justify-between">
|
<div class="flex flex-row w-full justify-between">
|
||||||
<div>
|
<div>
|
||||||
<GearSelector {selectedGear} />
|
<GearSelector {selectedGear} {placeholder} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ModeSelector {selectedMode} />
|
<ModeSelector {selectedMode} {placeholder} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<BatteryDisplay {voltage} />
|
<BatteryDisplay {voltage} {placeholder} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { T, useTask } from "@threlte/core";
|
import { T, useTask } from '@threlte/core'
|
||||||
import { ContactShadows, Float, Grid, OrbitControls } from "@threlte/extras";
|
import { ContactShadows, Float, Grid, OrbitControls } from '@threlte/extras'
|
||||||
import Controls from "./Controls.svelte";
|
import Controls from './Controls.svelte'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Vector3,
|
Vector3,
|
||||||
|
@ -9,15 +9,15 @@
|
||||||
type Group,
|
type Group,
|
||||||
type Object3D,
|
type Object3D,
|
||||||
type Object3DEventMap,
|
type Object3DEventMap,
|
||||||
} from "three";
|
} from 'three'
|
||||||
import {
|
import {
|
||||||
telemetryReadonlyStore,
|
telemetryReadonlyStore,
|
||||||
telemetryStore,
|
telemetryStore,
|
||||||
} from "../../stores/telemetryStore";
|
} from '../../stores/telemetryStore'
|
||||||
import { get } from "svelte/store";
|
import { get } from 'svelte/store'
|
||||||
import { Vector2 } from "three";
|
import { Vector2 } from 'three'
|
||||||
import { SmoothMotionController } from "./smoothMotionController";
|
import { SmoothMotionController } from './smoothMotionController'
|
||||||
import { onMount } from "svelte";
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
/* This is the root scene where the robot visualization is built.
|
/* This is the root scene where the robot visualization is built.
|
||||||
It renders an infinite grid (it's not actually infinite, but we shouldn't run out
|
It renders an infinite grid (it's not actually infinite, but we shouldn't run out
|
||||||
|
@ -32,91 +32,91 @@
|
||||||
is the most esoteric and jank code ever written.
|
is the most esoteric and jank code ever written.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let shouldOrbit = true;
|
let shouldOrbit = true
|
||||||
|
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
const maxAngularVelocity = 2; // Max angular velocity, in radians per second
|
const maxAngularVelocity = 2 // Max angular velocity, in radians per second
|
||||||
const stoppingThreshold = 0.005; // Threshold in radians for when to consider the rotation close enough to stop
|
const stoppingThreshold = 0.005 // Threshold in radians for when to consider the rotation close enough to stop
|
||||||
|
|
||||||
// Proportional control factor
|
// Proportional control factor
|
||||||
const kP = 2; // Adjust this value based on responsiveness and stability needs
|
const kP = 2 // Adjust this value based on responsiveness and stability needs
|
||||||
|
|
||||||
// Sync robot orientation with target rotation
|
// Sync robot orientation with target rotation
|
||||||
let targetRot = 0;
|
let targetRot = 0
|
||||||
|
|
||||||
// Updates rotation to match target with PID controller (intended to be invoked in useTask)
|
// Updates rotation to match target with PID controller (intended to be invoked in useTask)
|
||||||
let rot = 0; // (initial) rotation in radians
|
let rot = 0 // (initial) rotation in radians
|
||||||
let angularVelocity = 0;
|
let angularVelocity = 0
|
||||||
const updateRotation = (delta: number) => {
|
const updateRotation = (delta: number) => {
|
||||||
let angleDifference = targetRot - rot;
|
let angleDifference = targetRot - rot
|
||||||
|
|
||||||
// Normalize angle difference to the range [-π, π]
|
// Normalize angle difference to the range [-π, π]
|
||||||
angleDifference = ((angleDifference + Math.PI) % (2 * Math.PI)) - Math.PI;
|
angleDifference = ((angleDifference + Math.PI) % (2 * Math.PI)) - Math.PI
|
||||||
|
|
||||||
// Calculate the desired angular velocity based on the angle difference
|
// Calculate the desired angular velocity based on the angle difference
|
||||||
let desiredVelocity =
|
let desiredVelocity =
|
||||||
Math.sign(angleDifference) *
|
Math.sign(angleDifference) *
|
||||||
Math.min(maxAngularVelocity, Math.abs(kP * angleDifference));
|
Math.min(maxAngularVelocity, Math.abs(kP * angleDifference))
|
||||||
|
|
||||||
// If the object is very close to the target, adjust the desired velocity to zero to prevent overshooting
|
// If the object is very close to the target, adjust the desired velocity to zero to prevent overshooting
|
||||||
if (Math.abs(angleDifference) < stoppingThreshold) {
|
if (Math.abs(angleDifference) < stoppingThreshold) {
|
||||||
desiredVelocity = 0;
|
desiredVelocity = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust angular velocity towards desired velocity
|
// Adjust angular velocity towards desired velocity
|
||||||
angularVelocity = desiredVelocity;
|
angularVelocity = desiredVelocity
|
||||||
|
|
||||||
// Update rotation
|
// Update rotation
|
||||||
rot += angularVelocity * delta;
|
rot += angularVelocity * delta
|
||||||
|
|
||||||
// Normalize rot to the range [0, 2π]
|
// Normalize rot to the range [0, 2π]
|
||||||
if (rot < 0) rot += 2 * Math.PI;
|
if (rot < 0) rot += 2 * Math.PI
|
||||||
else if (rot > 2 * Math.PI) rot -= 2 * Math.PI;
|
else if (rot > 2 * Math.PI) rot -= 2 * Math.PI
|
||||||
|
|
||||||
// Snap to the target rotation to prevent tiny oscillations if close enough
|
// Snap to the target rotation to prevent tiny oscillations if close enough
|
||||||
if (Math.abs(angleDifference) < stoppingThreshold) {
|
if (Math.abs(angleDifference) < stoppingThreshold) {
|
||||||
rot = targetRot;
|
rot = targetRot
|
||||||
angularVelocity = 0;
|
angularVelocity = 0
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let robotPos: Vector3 = new Vector3(0, 0, 0);
|
let robotPos: Vector3 = new Vector3(0, 0, 0)
|
||||||
|
|
||||||
const robotPosition = new Vector2(0, 0); // Initial position
|
const robotPosition = new Vector2(0, 0) // Initial position
|
||||||
const initialVelocity = { x: 0, y: 0 }; // Initial velocity
|
const initialVelocity = { x: 0, y: 0 } // Initial velocity
|
||||||
// The smooth motion controller utilizes a cubic hermite spline to interpolate between
|
// The smooth motion controller utilizes a cubic hermite spline to interpolate between
|
||||||
// the current simulation velocity and the robot's actual velocity
|
// the current simulation velocity and the robot's actual velocity
|
||||||
const controller = new SmoothMotionController(robotPosition, initialVelocity);
|
const controller = new SmoothMotionController(robotPosition, initialVelocity)
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
telemetryReadonlyStore.subscribe((value) => {
|
telemetryReadonlyStore.subscribe(value => {
|
||||||
targetRot = (value["orientation"] * Math.PI) / 180; // convert deg to rad
|
targetRot = (value['orientation'] * Math.PI) / 180 // convert deg to rad
|
||||||
controller.setTargetVelocity({
|
controller.setTargetVelocity({
|
||||||
x: value["chassis-x-speed"],
|
x: value['chassis-x-speed'],
|
||||||
y: value["chassis-y-speed"],
|
y: value['chassis-y-speed'],
|
||||||
});
|
})
|
||||||
shouldOrbit = value.gear === "park" || value.gear === "-999";
|
// shouldOrbit = value.gear === "park" || value.gear === "-999";
|
||||||
if (shouldOrbit) {
|
if (shouldOrbit) {
|
||||||
robotPos = new Vector3(0, 0, 0);
|
robotPos = new Vector3(0, 0, 0)
|
||||||
controller.reset();
|
controller.reset()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
useTask((delta) => {
|
useTask(delta => {
|
||||||
if (!shouldOrbit) {
|
if (!shouldOrbit) {
|
||||||
updateRotation(delta);
|
updateRotation(delta)
|
||||||
|
|
||||||
controller.update(delta);
|
controller.update(delta)
|
||||||
robotPos.x = controller.getPosition().x;
|
robotPos.x = controller.getPosition().x
|
||||||
robotPos.z = controller.getPosition().y;
|
robotPos.z = controller.getPosition().y
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
let capsule: Group<Object3DEventMap>;
|
let capsule: Group<Object3DEventMap>
|
||||||
let capRef: Group<Object3DEventMap>;
|
let capRef: Group<Object3DEventMap>
|
||||||
$: if (capsule) {
|
$: if (capsule) {
|
||||||
capRef = capsule;
|
capRef = capsule
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -145,7 +145,6 @@
|
||||||
|
|
||||||
<ContactShadows scale={10} blur={2} far={2.5} opacity={0.5} />
|
<ContactShadows scale={10} blur={2} far={2.5} opacity={0.5} />
|
||||||
|
|
||||||
|
|
||||||
<!-- <Hornet
|
<!-- <Hornet
|
||||||
position.y={2}
|
position.y={2}
|
||||||
position.z={robotPos.z}
|
position.z={robotPos.z}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
/* stores global app wide settings */
|
/* stores global app wide settings */
|
||||||
|
|
||||||
import { writable } from "svelte/store";
|
import { writable } from 'svelte/store'
|
||||||
|
|
||||||
type SupportedLanguage = "en" | "rus";
|
type SupportedLanguage = 'en-US' | 'en-RU'
|
||||||
|
|
||||||
export interface SettingsStoreData {
|
export interface SettingsStoreData {
|
||||||
disableAnnoyances: boolean;
|
disableAnnoyances: boolean
|
||||||
goWoke: boolean;
|
goWoke: boolean
|
||||||
fastStartup: boolean;
|
fastStartup: boolean
|
||||||
randomWeight: number;
|
randomWeight: number
|
||||||
voiceLang: SupportedLanguage;
|
voiceLang: SupportedLanguage
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaults: SettingsStoreData = {
|
export const defaults: SettingsStoreData = {
|
||||||
|
@ -17,26 +17,26 @@ export const defaults: SettingsStoreData = {
|
||||||
goWoke: false, // go woke (for showing parents or other officials where DEI has taken over), disables "offensive" sequences
|
goWoke: false, // go woke (for showing parents or other officials where DEI has taken over), disables "offensive" sequences
|
||||||
fastStartup: false, // skip the loading splash screen (for development purposes. Setting this from within the app has no effect.)
|
fastStartup: false, // skip the loading splash screen (for development purposes. Setting this from within the app has no effect.)
|
||||||
randomWeight: 1, // the weight of random events (multiplied by the original probability)
|
randomWeight: 1, // the weight of random events (multiplied by the original probability)
|
||||||
voiceLang: "en",
|
voiceLang: 'en-US',
|
||||||
};
|
}
|
||||||
|
|
||||||
const createSequenceStore = () => {
|
const createSequenceStore = () => {
|
||||||
const { subscribe, set, update } = writable<SettingsStoreData>(defaults);
|
const { subscribe, set, update } = writable<SettingsStoreData>(defaults)
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
update: (
|
update: (
|
||||||
data: keyof SettingsStoreData,
|
data: keyof SettingsStoreData,
|
||||||
newValue: SettingsStoreData[typeof data]
|
newValue: SettingsStoreData[typeof data]
|
||||||
) => {
|
) => {
|
||||||
update((store) => {
|
update(store => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
store[data] = newValue;
|
store[data] = newValue
|
||||||
return store;
|
return store
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
reset: () => set(defaults),
|
reset: () => set(defaults),
|
||||||
set: (data: SettingsStoreData) => set(data),
|
set: (data: SettingsStoreData) => set(data),
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const settingsStore = createSequenceStore();
|
export const settingsStore = createSequenceStore()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { get } from "svelte/store";
|
import { get } from 'svelte/store'
|
||||||
import { settingsStore } from "../stores/settingsStore";
|
import { settingsStore } from '../stores/settingsStore'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the voice audio path for the given audio file.
|
* Retrieves the voice audio path for the given audio file.
|
||||||
|
@ -8,18 +8,13 @@ import { settingsStore } from "../stores/settingsStore";
|
||||||
* @param lang - the language of the audio
|
* @param lang - the language of the audio
|
||||||
* @return the path of the audio file
|
* @return the path of the audio file
|
||||||
*/
|
*/
|
||||||
type SupportedLanguage = "en" | "rus";
|
type SupportedLanguage = 'en-US' | 'en-RU'
|
||||||
|
|
||||||
let currentLang = "en";
|
|
||||||
|
|
||||||
settingsStore.subscribe((data) => {
|
|
||||||
currentLang = data.voiceLang;
|
|
||||||
});
|
|
||||||
export default function getVoicePath(audio: string, lang?: SupportedLanguage) {
|
export default function getVoicePath(audio: string, lang?: SupportedLanguage) {
|
||||||
console.log(get(settingsStore).voiceLang);
|
console.log(get(settingsStore).voiceLang)
|
||||||
if (!lang) {
|
if (!lang) {
|
||||||
return `/static/voices/${get(settingsStore).voiceLang}/${audio}.wav`;
|
return `/static/voices/${get(settingsStore).voiceLang}/${audio}.wav`
|
||||||
}
|
}
|
||||||
|
|
||||||
return `/static/voices/${lang}/${audio}.wav`;
|
return `/static/voices/${lang}/${audio}.wav`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue