diff --git a/build.gradle b/build.gradle index bdb042259..8a3a2243b 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,6 @@ ext { frcYear = "2026beta" mrcalVersion = "dev-v2025.0.0-3-g2dc275f"; - pubVersion = versionString isDev = pubVersion.startsWith("dev") diff --git a/photon-client/src/components/app/photon-3d-visualizer.vue b/photon-client/src/components/app/photon-3d-visualizer.vue index 52e98e7a7..cc954c7f4 100644 --- a/photon-client/src/components/app/photon-3d-visualizer.vue +++ b/photon-client/src/components/app/photon-3d-visualizer.vue @@ -8,8 +8,10 @@ import { onBeforeUnmount, onMounted, watchEffect } from "vue"; const { ArrowHelper, BoxGeometry, + CameraHelper, Color, ConeGeometry, + Group, Mesh, MeshNormalMaterial, PerspectiveCamera, @@ -20,6 +22,18 @@ const { } = await import("three"); const { TrackballControls } = await import("three/examples/jsm/controls/TrackballControls"); +import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore"; +import { createPerspectiveCamera } from "@/lib/ThreeUtils"; +import { useTheme } from "vuetify"; + +const theme = useTheme(); + +const calibrationCoeffs = useCameraSettingsStore().getCalibrationCoeffs( + useCameraSettingsStore().currentCameraSettings.validVideoFormats[ + useCameraSettingsStore().currentPipelineSettings.cameraVideoModeIndex + ].resolution +); + const props = defineProps<{ targets: PhotonTarget[]; }>(); @@ -32,15 +46,18 @@ let controls: TrackballControls | undefined; let previousTargets: Object3D[] = []; const drawTargets = (targets: PhotonTarget[]) => { // Check here, since if we check in watchEffect this never gets called - if (scene === undefined || camera === undefined || renderer === undefined || controls === undefined) { + if (!scene || !camera || !renderer || !controls) { return; } + if (theme.global.name.value === "LightTheme") scene.background = new Color(0xa9a9a9); + else scene.background = new Color(0x000000); + scene.remove(...previousTargets); previousTargets = []; targets.forEach((target) => { - if (target.pose === undefined) return; + if (!target.pose) return; const geometry = new BoxGeometry(0.3 / 5, 0.2, 0.2); const material = new MeshNormalMaterial(); @@ -70,6 +87,18 @@ const drawTargets = (targets: PhotonTarget[]) => { previousTargets.push(arrow); }); + if (calibrationCoeffs) { + // And show camera frustum + const calibCamera = createPerspectiveCamera(calibrationCoeffs.resolution, calibrationCoeffs.cameraIntrinsics, 10); + const helper = new CameraHelper(calibCamera); + const helperGroup = new Group(); + helperGroup.add(helper); + // Flip to +Z forward + helperGroup.rotateX(-Math.PI / 2.0); + helperGroup.rotateY(-Math.PI / 2.0); + previousTargets.push(helperGroup); + } + if (previousTargets.length > 0) { scene.add(...previousTargets); } @@ -78,7 +107,7 @@ const onWindowResize = () => { const container = document.getElementById("container"); const canvas = document.getElementById("view"); - if (container === null || canvas === null || camera === undefined || renderer === undefined) { + if (!container || !canvas || !camera || !renderer) { return; } @@ -89,7 +118,7 @@ const onWindowResize = () => { renderer.setSize(canvas.clientWidth, canvas.clientHeight); }; const resetCamFirstPerson = () => { - if (scene === undefined || camera === undefined || controls === undefined) { + if (!scene || !camera || !controls) { return; } @@ -103,7 +132,7 @@ const resetCamFirstPerson = () => { } }; const resetCamThirdPerson = () => { - if (scene === undefined || camera === undefined || controls === undefined) { + if (!scene || !camera || !controls) { return; } @@ -122,10 +151,11 @@ onMounted(async () => { camera = new PerspectiveCamera(75, 800 / 800, 0.1, 1000); const canvas = document.getElementById("view"); - if (canvas === null) return; + if (!canvas) return; renderer = new WebGLRenderer({ canvas: canvas }); - scene.background = new Color(0xa9a9a9); + if (theme.global.name.value === "LightTheme") scene.background = new Color(0xa9a9a9); + else scene.background = new Color(0x000000); onWindowResize(); window.addEventListener("resize", onWindowResize); @@ -169,7 +199,7 @@ onMounted(async () => { controls.update(); const animate = () => { - if (scene === undefined || camera === undefined || renderer === undefined || controls === undefined) { + if (!scene || !camera || !renderer || !controls) { return; } @@ -192,18 +222,31 @@ watchEffect(() => { diff --git a/photon-client/src/components/app/photon-calibration-visualizer.vue b/photon-client/src/components/app/photon-calibration-visualizer.vue new file mode 100644 index 000000000..e63798c6d --- /dev/null +++ b/photon-client/src/components/app/photon-calibration-visualizer.vue @@ -0,0 +1,371 @@ + + + diff --git a/photon-client/src/components/cameras/CameraCalibrationCard.vue b/photon-client/src/components/cameras/CameraCalibrationCard.vue index c0ca6f8fb..55e3e16e2 100644 --- a/photon-client/src/components/cameras/CameraCalibrationCard.vue +++ b/photon-client/src/components/cameras/CameraCalibrationCard.vue @@ -38,7 +38,8 @@ const getUniqueVideoFormatsByResolution = (): VideoFormat[] => { if (!skip) { const calib = useCameraSettingsStore().getCalibrationCoeffs(format.resolution); if (calib !== undefined) { - // For each error, square it, sum the squares, and divide by total points N + // Mean overall reprojection error + // Calculated as average of each observation's mean error if (calib.meanErrors.length) format.mean = calib.meanErrors.reduce((a, b) => a + b, 0) / calib.meanErrors.length; else format.mean = NaN; @@ -249,27 +250,31 @@ const setSelectedVideoFormat = (format: VideoFormat) => { Horizontal FOV Vertical FOV Diagonal FOV - Info - - {{ getResolutionString(value.resolution) }} - - {{ value.mean !== undefined ? (isNaN(value.mean) ? "Unknown" : value.mean.toFixed(2) + "px") : "-" }} - - {{ value.horizontalFOV !== undefined ? value.horizontalFOV.toFixed(2) + "°" : "-" }} - {{ value.verticalFOV !== undefined ? value.verticalFOV.toFixed(2) + "°" : "-" }} - {{ value.diagonalFOV !== undefined ? value.diagonalFOV.toFixed(2) + "°" : "-" }} - - + View calibration information + diff --git a/photon-client/src/components/cameras/CameraCalibrationInfoCard.vue b/photon-client/src/components/cameras/CameraCalibrationInfoCard.vue index cb5b02b27..f93b1ca19 100644 --- a/photon-client/src/components/cameras/CameraCalibrationInfoCard.vue +++ b/photon-client/src/components/cameras/CameraCalibrationInfoCard.vue @@ -1,4 +1,5 @@ +