All files / src/components/VirtualWalkthrough CameraInfo.tsx

0% Statements 0/23
0% Branches 0/4
0% Functions 0/7
0% Lines 0/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100                                                                                                                                                                                                       
import React, { useEffect, useState } from 'react';
 
import { Box, Typography, useTheme } from '@mui/material';
 
import { getRgbaFromHex } from '../../utils/color';
 
const UPDATE_FREQUENCY_MS = 200;
const COORDINATE_LABELS = ['X', 'Y', 'Z'] as const;
 
const formatNumber = (num: number) => num.toFixed(6);
 
export interface CameraInfoStrings {
  cameraInfo: string;
  cameraPosition: string;
  cameraFocusPoint: string;
}
 
export interface CameraState {
  position: [number, number, number];
  focus: [number, number, number];
}
 
interface CoordinateDisplayProps {
  label: string | undefined;
  coordinates: [number, number, number];
  textColor: string;
}
 
const CoordinateDisplay = ({ label, coordinates, textColor }: CoordinateDisplayProps) => (
  <Box>
    <Typography variant='caption' sx={{ color: textColor, fontWeight: 'bold' }}>
      {label}:
    </Typography>
    {coordinates.map((value, index) => (
      <Typography
        key={COORDINATE_LABELS[index]}
        variant='caption'
        sx={{ color: textColor, display: 'block', fontFamily: 'monospace' }}
      >
        {COORDINATE_LABELS[index]}: {formatNumber(value)}
      </Typography>
    ))}
  </Box>
);
 
interface CameraInfoProps {
  strings: CameraInfoStrings;
  getCameraState: () => CameraState | null;
}
 
const CameraInfo = ({ strings, getCameraState }: CameraInfoProps) => {
  const theme = useTheme();
  const [cameraState, setCameraState] = useState<CameraState | null>(null);
 
  useEffect(() => {
    const updateCameraState = () => {
      const state = getCameraState();
      if (state) {
        setCameraState(state);
      }
    };
 
    updateCameraState();
    const intervalId = setInterval(updateCameraState, UPDATE_FREQUENCY_MS);
 
    return () => clearInterval(intervalId);
  }, [getCameraState]);
 
  if (!cameraState) {
    return null;
  }
 
  const textColor = theme.palette.TwClrIcnInfo as string;
 
  return (
    <Box
      sx={{
        position: 'absolute',
        bottom: 16,
        left: 16,
        backgroundColor: getRgbaFromHex(theme.palette.TwClrIcnOnBrand as string, 0.9),
        padding: 2,
        borderRadius: 1,
        pointerEvents: 'auto',
        minWidth: 220,
      }}
    >
      <Typography variant='body2' sx={{ color: textColor, fontWeight: 'bold', marginBottom: 1 }}>
        {strings.cameraInfo}
      </Typography>
      <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
        <CoordinateDisplay label={strings.cameraPosition} coordinates={cameraState.position} textColor={textColor} />
        <CoordinateDisplay label={strings.cameraFocusPoint} coordinates={cameraState.focus} textColor={textColor} />
      </Box>
    </Box>
  );
};
 
export default CameraInfo;