mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-27 02:01:40 +00:00
Improve UI stability, reliability, and readability (#1104)
closes #1090 closes #1030 Also fixes various styling issues and overflow issues for mobile support
This commit is contained in:
@@ -29,61 +29,83 @@ const fpsTooLow = computed<boolean>(() => {
|
||||
|
||||
return currFPS - targetFPS < -5 && currFPS !== 0 && !driverMode && gpuAccel && isReflective;
|
||||
});
|
||||
|
||||
const performanceRecommendation = computed<string>(() => {
|
||||
if (
|
||||
fpsTooLow.value &&
|
||||
!useCameraSettingsStore().currentPipelineSettings.inputShouldShow &&
|
||||
useCameraSettingsStore().currentPipelineSettings.pipelineType === PipelineType.Reflective
|
||||
) {
|
||||
return "HSV thresholds are too broad; narrow them for better performance";
|
||||
} else if (fpsTooLow.value && useCameraSettingsStore().currentPipelineSettings.inputShouldShow) {
|
||||
return "Stop viewing the raw stream for better performance";
|
||||
} else {
|
||||
return `${Math.min(Math.round(useStateStore().currentPipelineResults?.latency || 0), 9999)} ms latency`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card color="primary" height="100%" style="display: flex; flex-direction: column" dark>
|
||||
<v-card-title
|
||||
class="pb-0 mb-0 pl-4 pt-1"
|
||||
style="min-height: 50px; justify-content: space-between; align-content: center"
|
||||
>
|
||||
<div class="pt-2">
|
||||
<span class="mr-4">Cameras</span>
|
||||
<v-row>
|
||||
<v-col class="align-self-center text-no-wrap">
|
||||
<v-card-title>Cameras</v-card-title>
|
||||
</v-col>
|
||||
<v-col class="align-self-center" style="text-align: right; margin-right: 12px; padding-left: 24px">
|
||||
<v-chip
|
||||
label
|
||||
:color="fpsTooLow ? 'error' : 'transparent'"
|
||||
:text-color="fpsTooLow ? '#C7EA46' : '#ff4d00'"
|
||||
style="font-size: 1rem; padding: 0; margin: 0"
|
||||
>
|
||||
<span class="pr-1">
|
||||
Processing @ {{ Math.round(useStateStore().currentPipelineResults?.fps || 0) }} FPS –
|
||||
</span>
|
||||
<span
|
||||
v-if="
|
||||
fpsTooLow &&
|
||||
!useCameraSettingsStore().currentPipelineSettings.inputShouldShow &&
|
||||
useCameraSettingsStore().currentPipelineSettings.pipelineType === PipelineType.Reflective
|
||||
"
|
||||
>
|
||||
HSV thresholds are too broad; narrow them for better performance
|
||||
</span>
|
||||
<span v-else-if="fpsTooLow && useCameraSettingsStore().currentPipelineSettings.inputShouldShow">
|
||||
stop viewing the raw stream for better performance
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ Math.min(Math.round(useStateStore().currentPipelineResults?.latency || 0), 9999) }} ms latency
|
||||
</span>
|
||||
<span class="pr-1"
|
||||
>Processing @ {{ Math.round(useStateStore().currentPipelineResults?.fps || 0) }} FPS –</span
|
||||
><span>{{ performanceRecommendation }}</span>
|
||||
</v-chip>
|
||||
</div>
|
||||
<div>
|
||||
</v-col>
|
||||
<v-col
|
||||
class="align-self-center"
|
||||
style="
|
||||
width: min-content;
|
||||
flex-grow: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-right: 24px;
|
||||
padding: 0;
|
||||
"
|
||||
>
|
||||
<v-switch
|
||||
v-model="driverMode"
|
||||
:disabled="useCameraSettingsStore().isCalibrationMode || useCameraSettingsStore().pipelineNames.length === 0"
|
||||
label="Driver Mode"
|
||||
style="margin-left: auto"
|
||||
style="margin: 0; padding: 0; padding-left: 18px; margin-top: 14px"
|
||||
color="accent"
|
||||
class="pt-2"
|
||||
/>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-divider style="border-color: white" />
|
||||
<v-row class="pl-3 pr-3 pt-3 pb-3" style="flex-wrap: nowrap; justify-content: center">
|
||||
<v-col v-show="value.includes(0)" style="max-width: 500px; display: flex; align-items: center">
|
||||
<v-row class="stream-viewer-container pa-3">
|
||||
<v-col v-show="value.includes(0)" class="stream-view">
|
||||
<photon-camera-stream id="input-camera-stream" stream-type="Raw" style="width: 100%; height: auto" />
|
||||
</v-col>
|
||||
<v-col v-show="value.includes(1)" style="max-width: 500px; display: flex; align-items: center">
|
||||
<v-col v-show="value.includes(1)" class="stream-view">
|
||||
<photon-camera-stream id="output-camera-stream" stream-type="Processed" style="width: 100%; height: auto" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.stream-viewer-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.stream-view {
|
||||
max-width: 500px;
|
||||
}
|
||||
@media only screen and (max-width: 512px) {
|
||||
.stream-view {
|
||||
min-width: 80%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -39,11 +39,11 @@ const processingMode = computed<number>({
|
||||
<p style="color: white">Processing Mode</p>
|
||||
<v-btn-toggle v-model="processingMode" mandatory dark class="fill">
|
||||
<v-btn color="secondary">
|
||||
<v-icon>mdi-square-outline</v-icon>
|
||||
<v-icon left>mdi-square-outline</v-icon>
|
||||
<span>2D</span>
|
||||
</v-btn>
|
||||
<v-btn color="secondary" :disabled="!useCameraSettingsStore().isCurrentVideoFormatCalibrated">
|
||||
<v-icon>mdi-cube-outline</v-icon>
|
||||
<v-icon left>mdi-cube-outline</v-icon>
|
||||
<span>3D</span>
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
@@ -54,12 +54,12 @@ const processingMode = computed<number>({
|
||||
<p style="color: white">Stream Display</p>
|
||||
<v-btn-toggle v-model="localValue" :multiple="true" mandatory dark class="fill">
|
||||
<v-btn color="secondary" class="fill">
|
||||
<v-icon>mdi-import</v-icon>
|
||||
<span>Raw</span>
|
||||
<v-icon left class="mode-btn-icon">mdi-import</v-icon>
|
||||
<span class="mode-btn-label">Raw</span>
|
||||
</v-btn>
|
||||
<v-btn color="secondary" class="fill">
|
||||
<v-icon>mdi-export</v-icon>
|
||||
<span>Processed</span>
|
||||
<v-icon left class="mode-btn-icon">mdi-export</v-icon>
|
||||
<span class="mode-btn-label">Processed</span>
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
</v-col>
|
||||
@@ -82,4 +82,13 @@ th {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 351px) {
|
||||
.mode-btn-icon {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.mode-btn-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { PipelineType } from "@/types/PipelineTypes";
|
||||
import PvSelect from "@/components/common/pv-select.vue";
|
||||
import PvSlider from "@/components/common/pv-slider.vue";
|
||||
import PvSwitch from "@/components/common/pv-switch.vue";
|
||||
import { computed, getCurrentInstance } from "vue";
|
||||
import { useStateStore } from "@/stores/StateStore";
|
||||
import type { ActivePipelineSettings } from "@/types/PipelineTypes";
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
|
||||
// TODO fix pipeline typing in order to fix this, the store settings call should be able to infer that only valid pipeline type settings are exposed based on pre-checks for the entire config section
|
||||
// Defer reference to store access method
|
||||
const currentPipelineSettings = useCameraSettingsStore().currentPipelineSettings;
|
||||
const currentPipelineSettings = computed<ActivePipelineSettings>(
|
||||
() => useCameraSettingsStore().currentPipelineSettings
|
||||
);
|
||||
|
||||
const interactiveCols = computed(
|
||||
() =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { PipelineType } from "@/types/PipelineTypes";
|
||||
import { PipelineType, type ActivePipelineSettings } from "@/types/PipelineTypes";
|
||||
import PvSlider from "@/components/common/pv-slider.vue";
|
||||
import PvSwitch from "@/components/common/pv-switch.vue";
|
||||
import PvRangeSlider from "@/components/common/pv-range-slider.vue";
|
||||
@@ -10,7 +10,9 @@ import { useStateStore } from "@/stores/StateStore";
|
||||
|
||||
// TODO fix pipeline typing in order to fix this, the store settings call should be able to infer that only valid pipeline type settings are exposed based on pre-checks for the entire config section
|
||||
// Defer reference to store access method
|
||||
const currentPipelineSettings = useCameraSettingsStore().currentPipelineSettings;
|
||||
const currentPipelineSettings = computed<ActivePipelineSettings>(
|
||||
() => useCameraSettingsStore().currentPipelineSettings
|
||||
);
|
||||
|
||||
const interactiveCols = computed(
|
||||
() =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { PipelineType } from "@/types/PipelineTypes";
|
||||
import { type ActivePipelineSettings, PipelineType } from "@/types/PipelineTypes";
|
||||
import PvRangeSlider from "@/components/common/pv-range-slider.vue";
|
||||
import PvSelect from "@/components/common/pv-select.vue";
|
||||
import PvSlider from "@/components/common/pv-slider.vue";
|
||||
@@ -9,7 +9,9 @@ import { useStateStore } from "@/stores/StateStore";
|
||||
|
||||
// TODO fix pipeline typing in order to fix this, the store settings call should be able to infer that only valid pipeline type settings are exposed based on pre-checks for the entire config section
|
||||
// Defer reference to store access method
|
||||
const currentPipelineSettings = useCameraSettingsStore().currentPipelineSettings;
|
||||
const currentPipelineSettings = computed<ActivePipelineSettings>(
|
||||
() => useCameraSettingsStore().currentPipelineSettings
|
||||
);
|
||||
|
||||
// TODO fix pv-range-slider so that store access doesn't need to be deferred
|
||||
const contourArea = computed<[number, number]>({
|
||||
@@ -26,23 +28,23 @@ const contourFullness = computed<[number, number]>({
|
||||
});
|
||||
const contourPerimeter = computed<[number, number]>({
|
||||
get: () =>
|
||||
currentPipelineSettings.pipelineType === PipelineType.ColoredShape
|
||||
? (Object.values(currentPipelineSettings.contourPerimeter) as [number, number])
|
||||
currentPipelineSettings.value.pipelineType === PipelineType.ColoredShape
|
||||
? (Object.values(currentPipelineSettings.value.contourPerimeter) as [number, number])
|
||||
: ([0, 0] as [number, number]),
|
||||
set: (v) => {
|
||||
if (currentPipelineSettings.pipelineType === PipelineType.ColoredShape) {
|
||||
currentPipelineSettings.contourPerimeter = v;
|
||||
if (currentPipelineSettings.value.pipelineType === PipelineType.ColoredShape) {
|
||||
currentPipelineSettings.value.contourPerimeter = v;
|
||||
}
|
||||
}
|
||||
});
|
||||
const contourRadius = computed<[number, number]>({
|
||||
get: () =>
|
||||
currentPipelineSettings.pipelineType === PipelineType.ColoredShape
|
||||
? (Object.values(currentPipelineSettings.contourRadius) as [number, number])
|
||||
currentPipelineSettings.value.pipelineType === PipelineType.ColoredShape
|
||||
? (Object.values(currentPipelineSettings.value.contourRadius) as [number, number])
|
||||
: ([0, 0] as [number, number]),
|
||||
set: (v) => {
|
||||
if (currentPipelineSettings.pipelineType === PipelineType.ColoredShape) {
|
||||
currentPipelineSettings.contourRadius = v;
|
||||
if (currentPipelineSettings.value.pipelineType === PipelineType.ColoredShape) {
|
||||
currentPipelineSettings.value.contourRadius = v;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -103,8 +105,8 @@ const interactiveCols = computed(
|
||||
v-model="contourPerimeter"
|
||||
label="Perimeter"
|
||||
tooltip="Min and max perimeter of the shape, in pixels"
|
||||
min="0"
|
||||
max="4000"
|
||||
:min="0"
|
||||
:max="4000"
|
||||
:slider-cols="interactiveCols"
|
||||
@input="(value) => useCameraSettingsStore().changeCurrentPipelineSetting({ contourPerimeter: value }, false)"
|
||||
/>
|
||||
|
||||
@@ -6,6 +6,7 @@ import PvSelect from "@/components/common/pv-select.vue";
|
||||
import { computed, getCurrentInstance } from "vue";
|
||||
import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore";
|
||||
import { useStateStore } from "@/stores/StateStore";
|
||||
import { getResolutionString } from "@/lib/PhotonUtils";
|
||||
|
||||
// Due to something with libcamera or something else IDK much about, the 90° rotations need to be disabled if the libcamera drivers are being used.
|
||||
const cameraRotations = computed(() =>
|
||||
@@ -30,7 +31,7 @@ const getNumberOfSkippedDivisors = () => streamDivisors.length - getFilteredStre
|
||||
|
||||
const cameraResolutions = computed(() =>
|
||||
useCameraSettingsStore().currentCameraSettings.validVideoFormats.map(
|
||||
(f) => `${f.resolution.width} X ${f.resolution.height} at ${f.fps} FPS, ${f.pixelFormat}`
|
||||
(f) => `${getResolutionString(f.resolution)} at ${f.fps} FPS, ${f.pixelFormat}`
|
||||
)
|
||||
);
|
||||
const handleResolutionChange = (value: number) => {
|
||||
@@ -48,7 +49,11 @@ const streamResolutions = computed(() => {
|
||||
const streamDivisors = getFilteredStreamDivisors();
|
||||
const currentResolution = useCameraSettingsStore().currentVideoFormat.resolution;
|
||||
return streamDivisors.map(
|
||||
(x) => `${Math.floor(currentResolution.width / x)} X ${Math.floor(currentResolution.height / x)}`
|
||||
(x) =>
|
||||
`${getResolutionString({
|
||||
width: Math.floor(currentResolution.width / x),
|
||||
height: Math.floor(currentResolution.height / x)
|
||||
})}`
|
||||
);
|
||||
});
|
||||
const handleStreamResolutionChange = (value: number) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import PvSelect from "@/components/common/pv-select.vue";
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { PipelineType, RobotOffsetPointMode } from "@/types/PipelineTypes";
|
||||
import { type ActivePipelineSettings, PipelineType, RobotOffsetPointMode } from "@/types/PipelineTypes";
|
||||
import PvSwitch from "@/components/common/pv-switch.vue";
|
||||
import { computed, getCurrentInstance } from "vue";
|
||||
import { RobotOffsetType } from "@/types/SettingTypes";
|
||||
@@ -40,7 +40,11 @@ const offsetPoints = computed<MetricItem[]>(() => {
|
||||
}
|
||||
});
|
||||
|
||||
const currentPipelineSettings = useCameraSettingsStore().currentPipelineSettings;
|
||||
// TODO fix pipeline typing in order to fix this, the store settings call should be able to infer that only valid pipeline type settings are exposed based on pre-checks for the entire config section
|
||||
// Defer reference to store access method
|
||||
const currentPipelineSettings = computed<ActivePipelineSettings>(
|
||||
() => useCameraSettingsStore().currentPipelineSettings
|
||||
);
|
||||
|
||||
const interactiveCols = computed(
|
||||
() =>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { PipelineType } from "@/types/PipelineTypes";
|
||||
import { type ActivePipelineSettings, PipelineType } from "@/types/PipelineTypes";
|
||||
import { useStateStore } from "@/stores/StateStore";
|
||||
import { angleModulus, toDeg } from "@/lib/MathUtils";
|
||||
import { computed } from "vue";
|
||||
|
||||
const wrapToPi = (delta: number): number => {
|
||||
let ret = delta;
|
||||
while (ret < -Math.PI) ret += Math.PI * 2;
|
||||
while (ret > Math.PI) ret -= Math.PI * 2;
|
||||
return ret;
|
||||
};
|
||||
// TODO fix pipeline typing in order to fix this, the store settings call should be able to infer that only valid pipeline type settings are exposed based on pre-checks for the entire config section
|
||||
// Defer reference to store access method
|
||||
const currentPipelineSettings = computed<ActivePipelineSettings>(
|
||||
() => useCameraSettingsStore().currentPipelineSettings
|
||||
);
|
||||
|
||||
const calculateStdDev = (values: number[]): number => {
|
||||
if (values.length < 2) return 0;
|
||||
@@ -21,14 +22,9 @@ const calculateStdDev = (values: number[]): number => {
|
||||
|
||||
// Borrowed from WPILib's Rotation2d
|
||||
const hypot = Math.hypot(cosmean, sinmean);
|
||||
let mean;
|
||||
if (hypot > 1e-6) {
|
||||
mean = Math.atan2(sinmean / hypot, cosmean / hypot);
|
||||
} else {
|
||||
mean = 0;
|
||||
}
|
||||
const mean = hypot > 1e-6 ? Math.atan2(sinmean / hypot, cosmean / hypot) : 0;
|
||||
|
||||
return Math.sqrt(values.map((x) => Math.pow(wrapToPi(x - mean), 2)).reduce((a, b) => a + b) / values.length);
|
||||
return Math.sqrt(values.map((x) => Math.pow(angleModulus(x - mean), 2)).reduce((a, b) => a + b) / values.length);
|
||||
};
|
||||
const resetCurrentBuffer = () => {
|
||||
// Need to clear the array in place
|
||||
@@ -38,71 +34,78 @@ const resetCurrentBuffer = () => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-row align="start" class="pb-4" style="height: 300px">
|
||||
<v-simple-table fixed-header dense dark>
|
||||
<v-row align="start" class="pb-4">
|
||||
<v-simple-table dense class="pt-2 pb-12">
|
||||
<template #default>
|
||||
<thead style="font-size: 1.25rem">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
v-if="
|
||||
useCameraSettingsStore().currentPipelineType === PipelineType.AprilTag ||
|
||||
useCameraSettingsStore().currentPipelineType === PipelineType.Aruco
|
||||
currentPipelineSettings.pipelineType === PipelineType.AprilTag ||
|
||||
currentPipelineSettings.pipelineType === PipelineType.Aruco
|
||||
"
|
||||
class="text-center"
|
||||
class="text-center white--text"
|
||||
>
|
||||
Fiducial ID
|
||||
</th>
|
||||
<template v-if="!useCameraSettingsStore().currentPipelineSettings.solvePNPEnabled">
|
||||
<th class="text-center">Pitch θ°</th>
|
||||
<th class="text-center">Yaw θ°</th>
|
||||
<th class="text-center">Skew θ°</th>
|
||||
<th class="text-center">Area %</th>
|
||||
<th class="text-center white--text">Pitch θ°</th>
|
||||
<th class="text-center white--text">Yaw θ°</th>
|
||||
<th class="text-center white--text">Skew θ°</th>
|
||||
<th class="text-center white--text">Area %</th>
|
||||
</template>
|
||||
<template v-else>
|
||||
<th class="text-center">X meters</th>
|
||||
<th class="text-center">Y meters</th>
|
||||
<th class="text-center">Z Angle θ°</th>
|
||||
<th class="text-center white--text">X meters</th>
|
||||
<th class="text-center white--text">Y meters</th>
|
||||
<th class="text-center white--text">Z Angle θ°</th>
|
||||
</template>
|
||||
<template
|
||||
v-if="
|
||||
(useCameraSettingsStore().currentPipelineType === PipelineType.AprilTag ||
|
||||
useCameraSettingsStore().currentPipelineType === PipelineType.Aruco) &&
|
||||
(currentPipelineSettings.pipelineType === PipelineType.AprilTag ||
|
||||
currentPipelineSettings.pipelineType === PipelineType.Aruco) &&
|
||||
useCameraSettingsStore().currentPipelineSettings.solvePNPEnabled
|
||||
"
|
||||
>
|
||||
<th class="text-center">Ambiguity Ratio</th>
|
||||
<th class="text-center white--text">Ambiguity Ratio</th>
|
||||
</template>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(target, index) in useStateStore().currentPipelineResults?.targets" :key="index">
|
||||
<tr
|
||||
v-for="(target, index) in useStateStore().currentPipelineResults?.targets"
|
||||
:key="index"
|
||||
class="white--text"
|
||||
>
|
||||
<td
|
||||
v-if="
|
||||
useCameraSettingsStore().currentPipelineType === PipelineType.AprilTag ||
|
||||
useCameraSettingsStore().currentPipelineType === PipelineType.Aruco
|
||||
currentPipelineSettings.pipelineType === PipelineType.AprilTag ||
|
||||
currentPipelineSettings.pipelineType === PipelineType.Aruco
|
||||
"
|
||||
class="text-center"
|
||||
>
|
||||
{{ target.fiducialId }}
|
||||
</td>
|
||||
<template v-if="!useCameraSettingsStore().currentPipelineSettings.solvePNPEnabled">
|
||||
<td>{{ target.pitch.toFixed(2) }}°</td>
|
||||
<td>{{ target.yaw.toFixed(2) }}°</td>
|
||||
<td>{{ target.skew.toFixed(2) }}°</td>
|
||||
<td>{{ target.area.toFixed(2) }}°</td>
|
||||
<td class="text-center">{{ target.pitch.toFixed(2) }}°</td>
|
||||
<td class="text-center">{{ target.yaw.toFixed(2) }}°</td>
|
||||
<td class="text-center">{{ target.skew.toFixed(2) }}°</td>
|
||||
<td class="text-center">{{ target.area.toFixed(2) }}°</td>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td>{{ target.pose?.x.toFixed(2) }} m</td>
|
||||
<td>{{ target.pose?.y.toFixed(2) }} m</td>
|
||||
<td>{{ (((target.pose?.angle_z || 0) * 180.0) / Math.PI).toFixed(2) }}°</td>
|
||||
<td class="text-center">{{ target.pose?.x.toFixed(2) }} m</td>
|
||||
<td class="text-center">{{ target.pose?.y.toFixed(2) }} m</td>
|
||||
<td class="text-center">{{ toDeg(target.pose?.angle_z || 0).toFixed(2) }}°</td>
|
||||
</template>
|
||||
<template
|
||||
v-if="
|
||||
(useCameraSettingsStore().currentPipelineType === PipelineType.AprilTag ||
|
||||
useCameraSettingsStore().currentPipelineType === PipelineType.Aruco) &&
|
||||
(currentPipelineSettings.pipelineType === PipelineType.AprilTag ||
|
||||
currentPipelineSettings.pipelineType === PipelineType.Aruco) &&
|
||||
useCameraSettingsStore().currentPipelineSettings.solvePNPEnabled
|
||||
"
|
||||
>
|
||||
<td>{{ target.ambiguity >= 0 ? target.ambiguity.toFixed(2) : "(In Multi-Target)" }}</td>
|
||||
<td class="text-center">
|
||||
{{ target.ambiguity >= 0 ? target.ambiguity.toFixed(2) : "(In Multi-Target)" }}
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -111,9 +114,9 @@ const resetCurrentBuffer = () => {
|
||||
</v-row>
|
||||
<v-container
|
||||
v-if="
|
||||
(useCameraSettingsStore().currentPipelineType === PipelineType.AprilTag ||
|
||||
useCameraSettingsStore().currentPipelineType === PipelineType.Aruco) &&
|
||||
useCameraSettingsStore().currentPipelineSettings.doMultiTarget &&
|
||||
(currentPipelineSettings.pipelineType === PipelineType.AprilTag ||
|
||||
currentPipelineSettings.pipelineType === PipelineType.Aruco) &&
|
||||
currentPipelineSettings.doMultiTarget &&
|
||||
useCameraSettingsStore().isCurrentVideoFormatCalibrated &&
|
||||
useCameraSettingsStore().currentPipelineSettings.solvePNPEnabled
|
||||
"
|
||||
@@ -122,46 +125,57 @@ const resetCurrentBuffer = () => {
|
||||
<v-card-subtitle class="ma-0 pa-0 pb-4" style="font-size: 16px"
|
||||
>Multi-tag pose, field-to-camera</v-card-subtitle
|
||||
>
|
||||
<v-simple-table fixed-header height="100%" dense dark>
|
||||
<thead style="font-size: 1.25rem">
|
||||
<th class="text-center">X meters</th>
|
||||
<th class="text-center">Y meters</th>
|
||||
<th class="text-center">Z meters</th>
|
||||
<th class="text-center">X Angle θ°</th>
|
||||
<th class="text-center">Y Angle θ°</th>
|
||||
<th class="text-center">Z Angle θ°</th>
|
||||
<th class="text-center">Tags</th>
|
||||
</thead>
|
||||
<tbody v-show="useStateStore().currentPipelineResults?.multitagResult">
|
||||
<td>{{ useStateStore().currentPipelineResults?.multitagResult?.bestTransform.x.toFixed(2) }} m</td>
|
||||
<td>{{ useStateStore().currentPipelineResults?.multitagResult?.bestTransform.y.toFixed(2) }} m</td>
|
||||
<td>{{ useStateStore().currentPipelineResults?.multitagResult?.bestTransform.z.toFixed(2) }} m</td>
|
||||
<td>
|
||||
{{
|
||||
(
|
||||
(useStateStore().currentPipelineResults?.multitagResult?.bestTransform.angle_x || 0) *
|
||||
(180.0 / Math.PI)
|
||||
).toFixed(2)
|
||||
}}°
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
(
|
||||
(useStateStore().currentPipelineResults?.multitagResult?.bestTransform.angle_y || 0) *
|
||||
(180.0 / Math.PI)
|
||||
).toFixed(2)
|
||||
}}°
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
(
|
||||
(useStateStore().currentPipelineResults?.multitagResult?.bestTransform.angle_z || 0) *
|
||||
(180.0 / Math.PI)
|
||||
).toFixed(2)
|
||||
}}°
|
||||
</td>
|
||||
<td>{{ useStateStore().currentPipelineResults?.multitagResult?.fiducialIDsUsed }}</td>
|
||||
</tbody>
|
||||
<v-simple-table dense>
|
||||
<template #default>
|
||||
<thead>
|
||||
<tr class="white--text">
|
||||
<th class="text-center white--text">X meters</th>
|
||||
<th class="text-center white--text">Y meters</th>
|
||||
<th class="text-center white--text">Z meters</th>
|
||||
<th class="text-center white--text">X Angle θ°</th>
|
||||
<th class="text-center white--text">Y Angle θ°</th>
|
||||
<th class="text-center white--text">Z Angle θ°</th>
|
||||
<th class="text-center white--text">Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-show="useStateStore().currentPipelineResults?.multitagResult">
|
||||
<tr>
|
||||
<td class="text-center white--text">
|
||||
{{ useStateStore().currentPipelineResults?.multitagResult?.bestTransform.x.toFixed(2) }} m
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{ useStateStore().currentPipelineResults?.multitagResult?.bestTransform.y.toFixed(2) }} m
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{ useStateStore().currentPipelineResults?.multitagResult?.bestTransform.z.toFixed(2) }} m
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
toDeg(useStateStore().currentPipelineResults?.multitagResult?.bestTransform.angle_x || 0).toFixed(
|
||||
2
|
||||
)
|
||||
}}°
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
toDeg(useStateStore().currentPipelineResults?.multitagResult?.bestTransform.angle_y || 0).toFixed(
|
||||
2
|
||||
)
|
||||
}}°
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
toDeg(useStateStore().currentPipelineResults?.multitagResult?.bestTransform.angle_z || 0).toFixed(
|
||||
2
|
||||
)
|
||||
}}°
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{ useStateStore().currentPipelineResults?.multitagResult?.fiducialIDsUsed }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</template>
|
||||
</v-simple-table>
|
||||
</v-row>
|
||||
<v-row class="pb-4 white--text" style="display: flex; flex-direction: column">
|
||||
@@ -172,53 +186,65 @@ const resetCurrentBuffer = () => {
|
||||
<v-btn color="secondary" class="mb-4 mt-1" style="width: min-content" depressed @click="resetCurrentBuffer"
|
||||
>Reset Samples</v-btn
|
||||
>
|
||||
<v-simple-table fixed-header height="100%" dense dark>
|
||||
<thead style="font-size: 1.25rem">
|
||||
<th class="text-center">X meters</th>
|
||||
<th class="text-center">Y meters</th>
|
||||
<th class="text-center">Z meters</th>
|
||||
<th class="text-center">X Angle θ°</th>
|
||||
<th class="text-center">Y Angle θ°</th>
|
||||
<th class="text-center">Z Angle θ°</th>
|
||||
</thead>
|
||||
<tbody v-show="useStateStore().currentPipelineResults?.multitagResult">
|
||||
<td>
|
||||
{{
|
||||
calculateStdDev(useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.x) || []).toFixed(5)
|
||||
}} m
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
calculateStdDev(useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.y) || []).toFixed(5)
|
||||
}} m
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
calculateStdDev(useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.z) || []).toFixed(5)
|
||||
}} m
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
calculateStdDev(
|
||||
useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.angle_x * (180.0 / Math.PI)) || []
|
||||
).toFixed(5)
|
||||
}}°
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
calculateStdDev(
|
||||
useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.angle_y * (180.0 / Math.PI)) || []
|
||||
).toFixed(5)
|
||||
}}°
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
calculateStdDev(
|
||||
useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.angle_z * (180.0 / Math.PI)) || []
|
||||
).toFixed(5)
|
||||
}}°
|
||||
</td>
|
||||
</tbody>
|
||||
<v-simple-table dense>
|
||||
<template #default>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center white--text">X meters</th>
|
||||
<th class="text-center white--text">Y meters</th>
|
||||
<th class="text-center white--text">Z meters</th>
|
||||
<th class="text-center white--text">X Angle θ°</th>
|
||||
<th class="text-center white--text">Y Angle θ°</th>
|
||||
<th class="text-center white--text">Z Angle θ°</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-show="useStateStore().currentPipelineResults?.multitagResult">
|
||||
<tr>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
calculateStdDev(useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.x) || []).toFixed(
|
||||
5
|
||||
)
|
||||
}} m
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
calculateStdDev(useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.y) || []).toFixed(
|
||||
5
|
||||
)
|
||||
}} m
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
calculateStdDev(useStateStore().currentMultitagBuffer?.map((v) => v.bestTransform.z) || []).toFixed(
|
||||
5
|
||||
)
|
||||
}} m
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
calculateStdDev(
|
||||
useStateStore().currentMultitagBuffer?.map((v) => toDeg(v.bestTransform.angle_x)) || []
|
||||
).toFixed(5)
|
||||
}}°
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
calculateStdDev(
|
||||
useStateStore().currentMultitagBuffer?.map((v) => toDeg(v.bestTransform.angle_y)) || []
|
||||
).toFixed(5)
|
||||
}}°
|
||||
</td>
|
||||
<td class="text-center white--text">
|
||||
{{
|
||||
calculateStdDev(
|
||||
useStateStore().currentMultitagBuffer?.map((v) => toDeg(v.bestTransform.angle_z)) || []
|
||||
).toFixed(5)
|
||||
}}°
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</template>
|
||||
</v-simple-table>
|
||||
</v-row>
|
||||
</v-container>
|
||||
@@ -227,23 +253,30 @@ const resetCurrentBuffer = () => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.v-data-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
background-color: #006492 !important;
|
||||
width: 100%;
|
||||
font-size: 1rem !important;
|
||||
|
||||
th,
|
||||
td {
|
||||
background-color: #006492 !important;
|
||||
font-size: 1rem !important;
|
||||
thead {
|
||||
tr {
|
||||
th {
|
||||
font-size: 1rem !important;
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
font-family: monospace !important;
|
||||
}
|
||||
|
||||
tbody :hover td {
|
||||
background-color: #005281 !important;
|
||||
tbody {
|
||||
:hover {
|
||||
td {
|
||||
background-color: #005281 !important;
|
||||
}
|
||||
}
|
||||
tr {
|
||||
td {
|
||||
font-size: 1rem !important;
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
|
||||
Reference in New Issue
Block a user