import { useEffect, useMemo } from 'react'; import { useParams } from 'react-router-dom'; import { ViewerScaffold } from '../components/viewer/ViewerScaffold'; import { NotFoundPage } from './NotFoundPage'; import { getMachineBySlug } from '../modules/machines/selectors'; import { useUrlViewState } from '../hooks/useUrlViewState'; import { useViewerStore } from '../store/viewerStore'; import type { PartRenderState } from '../types/viewer'; export function ViewerPage() { const { slug } = useParams(); const machine = getMachineBySlug(slug ?? ''); const { viewState } = useUrlViewState(); const initializeMachine = useViewerStore((state) => state.initializeMachine); const setExplodedDistance = useViewerStore((state) => state.setExplodedDistance); const setDisplayMode = useViewerStore((state) => state.setDisplayMode); const setCrossSectionAxis = useViewerStore((state) => state.setCrossSectionAxis); const setCrossSectionOffset = useViewerStore((state) => state.setCrossSectionOffset); const setShowAnnotations = useViewerStore((state) => state.setShowAnnotations); const setCameraPreset = useViewerStore((state) => state.setCameraPreset); const setCameraOverride = useViewerStore((state) => state.setCameraOverride); const partIds = useMemo(() => machine?.parts.map((part) => part.id) ?? [], [machine]); const initialPartState = useMemo>>(() => { const hiddenPartIds = new Set(viewState.hiddenPartIds); return partIds.reduce>>((accumulator, partId) => { accumulator[partId] = { visible: !hiddenPartIds.has(partId), opacity: viewState.opacityByPartId[partId] ?? 1 }; return accumulator; }, {}); }, [partIds, viewState.hiddenPartIds, viewState.opacityByPartId]); useEffect(() => { if (!machine) { return; } initializeMachine(machine.id, partIds, { defaultRpm: machine.animation.defaultRpm, initialPartState }); }, [initialPartState, initializeMachine, machine, partIds]); useEffect(() => { if (!machine) { return; } if (viewState.explodedDistance !== undefined) { setExplodedDistance(viewState.explodedDistance); } if (viewState.displayMode) { setDisplayMode(viewState.displayMode); } if (viewState.crossSectionAxis) { setCrossSectionAxis(viewState.crossSectionAxis); } if (viewState.crossSectionOffset !== undefined) { setCrossSectionOffset(viewState.crossSectionOffset); } if (viewState.annotations !== undefined) { setShowAnnotations(viewState.annotations); } if (viewState.camera?.position && viewState.camera.target) { setCameraOverride(viewState.camera.position, viewState.camera.target); } else if (viewState.camera?.preset) { setCameraPreset(viewState.camera.preset); } }, [ machine, setCameraOverride, setCameraPreset, setCrossSectionAxis, setCrossSectionOffset, setDisplayMode, setExplodedDistance, setShowAnnotations, viewState.annotations, viewState.camera, viewState.crossSectionAxis, viewState.crossSectionOffset, viewState.displayMode, viewState.explodedDistance ]); if (!machine) { return ; } return ; }