1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
4import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong';
5import CloseIcon from '@mui/icons-material/Close';
7import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
8import TravelExploreIcon from '@mui/icons-material/TravelExplore';
9import TuneIcon from '@mui/icons-material/Tune';
10import VideocamIcon from '@mui/icons-material/Videocam';
11import VideocamOffIcon from '@mui/icons-material/VideocamOff';
27} from '@mui/material';
28import { useState } from 'react';
29import { useScenarioStore, VisualizationLayers } from '@/stores/scenarioStore';
31// TODO: users should be able to drag the view controls button or pane to relocate it in the preview area. It should dynamically determine whether to expand upwards or downwards depending on whether it is opening from the bottom of the preview area or the top of the preview area.
33export default function ViewControls() {
34 const frameScene = useScenarioStore((s) => s.frameScene);
35 const focusOnItem = useScenarioStore((s) => s.focusOnItem);
36 const toggleFollowItem = useScenarioStore((s) => s.toggleFollowItem);
37 const selectedItemId = useScenarioStore((s) => s.selectedItemId);
38 const viewControlAction = useScenarioStore((s) => s.viewControlAction);
39 const visibility = useScenarioStore((s) => s.visibility);
40 const toggleLayer = useScenarioStore((s) => s.toggleLayer);
42 const [expanded, setExpanded] = useState(true);
44 // Helper to determine if we are following the currently selected item
46 viewControlAction.type === 'follow' &&
47 viewControlAction.targetId === selectedItemId;
49 const handleToggle = (key: keyof VisualizationLayers) => {
55 <Tooltip title="Open View Controls" placement="right">
63 justifyContent: 'center',
68 <IconButton onClick={() => setExpanded(true)} size="small">
69 <TuneIcon fontSize="small" />
83 flexDirection: 'column',
86 backgroundColor: 'background.paper',
89 {/* 1. Header & Camera Controls (Always Visible) */}
95 justifyContent: 'space-between',
96 bgcolor: 'action.hover',
98 borderColor: 'divider',
101 <Stack direction="row" spacing={1}>
102 <Tooltip title="Frame Scene">
103 <IconButton size="small" onClick={frameScene}>
104 <TravelExploreIcon fontSize="small" />
107 <Tooltip title="Focus on Selected">
113 focusOnItem(selectedItemId)
115 disabled={!selectedItemId}
118 <CenterFocusStrongIcon fontSize="small" />
124 isFollowing ? 'Stop Following' : 'Follow Selected'
132 toggleFollowItem(selectedItemId)
134 disabled={!selectedItemId}
135 color={isFollowing ? 'secondary' : 'default'}
140 color: isFollowing ? 'white' : 'inherit',
149 <VideocamIcon fontSize="small" />
151 <VideocamOffIcon fontSize="small" />
157 <IconButton size="small" onClick={() => setExpanded(false)}>
158 <CloseIcon fontSize="small" />
162 {/* 2. Scrollable Settings Area */}
163 <Box sx={{ overflowY: 'auto', p: 0 }}>
164 {/* SECTION: SCENE OBJECTS */}
165 <Accordion defaultExpanded disableGutters elevation={0}>
166 <AccordionSummary expandIcon={<ExpandMoreIcon />}>
167 <Typography variant="subtitle2">
171 <AccordionDetails sx={{ pt: 0, pb: 2 }}>
172 <Stack spacing={0.5}>
177 checked={visibility.showPlatforms}
179 handleToggle('showPlatforms')
184 <Typography variant="body2">
189 <Collapse in={visibility.showPlatforms}>
194 flexDirection: 'column',
202 visibility.showPlatformLabels
212 <Typography variant="caption">
221 checked={visibility.showAxes}
223 handleToggle('showAxes')
228 <Typography variant="caption">
238 visibility.showVelocities
248 <Typography variant="caption">
258 visibility.showPatterns
261 handleToggle('showPatterns')
266 <Typography variant="caption">
276 visibility.showBoresights
286 <Typography variant="caption">
294 <Divider sx={{ my: 1 }} />
300 checked={visibility.showMotionPaths}
302 handleToggle('showMotionPaths')
307 <Typography variant="body2">
318 {/* SECTION: RF LINKS */}
319 <Accordion defaultExpanded disableGutters elevation={0}>
320 <AccordionSummary expandIcon={<ExpandMoreIcon />}>
321 <Typography variant="subtitle2">RF Links</Typography>
323 <AccordionDetails sx={{ pt: 0 }}>
324 <Stack spacing={0.5}>
329 checked={visibility.showLinks}
331 handleToggle('showLinks')
336 <Typography variant="body2">
341 <Collapse in={visibility.showLinks}>
348 visibility.showLinkLabels
358 <Typography variant="body2">
365 color="text.secondary"
366 sx={{ mt: 1, display: 'block' }}
374 flexDirection: 'column',
382 visibility.showLinkMonostatic
392 <Typography variant="caption">
402 visibility.showLinkIlluminator
406 'showLinkIlluminator'
412 <Typography variant="caption">
413 Bistatic (Illuminator)
422 visibility.showLinkScattered
432 <Typography variant="caption">
442 visibility.showLinkDirect
452 <Typography variant="caption">
453 Direct (Interference)