Skip to content

Commit

Permalink
feature: cell ID text with toggle for visibility (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinShetty authored Feb 7, 2025
1 parent 82fe3d3 commit 3b3344d
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 46 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ This is a work-in-progress and has not yet reached feature parity with [Kei18/ma
- [x] Agent coloring
- [x] Per-agent path drawing
- [x] Keybindings for animation control
- [ ] Built-in screenshot capture with keybind
- [x] Built-in screenshot capture with keybind
- [ ] Show goal location
- [ ] Render line between agent current location and goal

## Features

Expand Down
80 changes: 48 additions & 32 deletions src/AnimationControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import DirectionsOutlinedIcon from '@mui/icons-material/DirectionsOutlined';
import FilterCenterFocusOutlinedIcon from '@mui/icons-material/FilterCenterFocusOutlined';
import ScreenshotMonitorOutlinedIcon from '@mui/icons-material/ScreenshotMonitorOutlined';
import StartIcon from '@mui/icons-material/Start';
import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
import SmartToyIcon from '@mui/icons-material/SmartToy';

const STEP_SIZE_INCREMENT = 0.2;
const STEP_SIZE_MAX = 10;
Expand All @@ -33,8 +35,9 @@ const FIT_VIEW_KEY = 'f';
const SHOW_AGENT_ID_KEY = 'a';
const STEP_SIZE_UP_KEY = 'ArrowUp';
const STEP_SIZE_DOWN_KEY = 'ArrowDown';
const TRACE_PATHS_KEY = 't';
const TRACE_PATHS_KEY = 'p';
const SCREENSHOT_KEY = 's';
const SHOW_CELL_ID_KEY = 'c';

interface AnimationControlProps {
playAnimation: boolean;
Expand All @@ -53,6 +56,8 @@ interface AnimationControlProps {
onTracePathsChange: (tracePaths: boolean) => void;
canScreenshot: boolean;
takeScreenshot: () => void;
showCellId: boolean;
setShowCellId: (showCellId: boolean) => void;
}

function AnimationControl({
Expand All @@ -72,6 +77,8 @@ function AnimationControl({
onTracePathsChange,
canScreenshot,
takeScreenshot,
showCellId,
setShowCellId,
}: AnimationControlProps) {
const handleSliderChange = (event: Event, value: number | number[]) => {
event.preventDefault();
Expand Down Expand Up @@ -108,6 +115,8 @@ function AnimationControl({
onTracePathsChange(!tracePaths);
} else if (event.key === SCREENSHOT_KEY) {
takeScreenshot();
} else if (event.key === SHOW_CELL_ID_KEY) {
setShowCellId(!showCellId);
}
};
window.addEventListener('keydown', handleKeyDown);
Expand All @@ -117,10 +126,36 @@ function AnimationControl({
}, [playAnimation, onPlayAnimationChange, loopAnimation, onFitView,
onLoopAnimationChange, onRestart, onShowAgentIdChange, onSkipBackward,
onSkipForward, onStepSizeChange, showAgentId, stepSize, onTracePathsChange, tracePaths,
takeScreenshot]);
takeScreenshot, showCellId, setShowCellId]);

return (
<Stack direction="column" spacing={2}>
<Stack direction="row" spacing={2} justifyContent="center">
<Tooltip
title={
<div style={{ textAlign: 'center' }}>
Adjust animation step size
({STEP_SIZE_UP_KEY}/{STEP_SIZE_DOWN_KEY})
</div>
}
>
<Slider
value={stepSize}
step={STEP_SIZE_INCREMENT}
marks
min={STEP_SIZE_MIN}
max={STEP_SIZE_MAX}
valueLabelDisplay="auto"
onChange={handleSliderChange}
sx={{ width: '50%', height: "auto"}}
/>
</Tooltip>
<Tooltip title="Reset step size">
<Button onClick={() => onStepSizeChange(1)}>
<RestartAltIcon />
</Button>
</Tooltip>
</Stack>
<Box display="flex" justifyContent="center">
<ButtonGroup size="large" variant="outlined">
<Tooltip title={`Backward one step (${STEP_BACKWARD_KEY})`}>
Expand Down Expand Up @@ -156,18 +191,25 @@ function AnimationControl({
<RepeatIcon />}
</Button>
</Tooltip>
</ButtonGroup>
</Box>
<Box display="flex" justifyContent="center">
<ButtonGroup size="large" variant="outlined">
<Tooltip title={`Reset view (${FIT_VIEW_KEY})`}>
<Button onClick={onFitView}>
<FilterCenterFocusOutlinedIcon />
</Button>
</Tooltip>
</ButtonGroup>
</Box>
<Box display="flex" justifyContent="center">
<ButtonGroup size="large" variant="outlined">
<Tooltip title={(showAgentId ? "Hide agent ID" : "Show agent ID") + ` (${SHOW_AGENT_ID_KEY})`}>
<Button onClick={() => onShowAgentIdChange(!showAgentId)}>
{showAgentId ?
<SmartToyIcon />:
<SmartToyOutlinedIcon />}
</Button>
</Tooltip>
<Tooltip title={(showCellId ? "Hide cell ID" : "Show cell ID") + ` (${SHOW_CELL_ID_KEY})`}>
<Button onClick={() => setShowCellId(!showCellId)}>
{showCellId ?
<LooksOneIcon />:
<LooksOneOutlinedIcon />}
</Button>
Expand All @@ -188,32 +230,6 @@ function AnimationControl({
</Tooltip>
</ButtonGroup>
</Box>
<Stack direction="row" spacing={2} justifyContent="center">
<Tooltip
title={
<div style={{ textAlign: 'center' }}>
Adjust animation step size
({STEP_SIZE_UP_KEY}/{STEP_SIZE_DOWN_KEY})
</div>
}
>
<Slider
value={stepSize}
step={STEP_SIZE_INCREMENT}
marks
min={STEP_SIZE_MIN}
max={STEP_SIZE_MAX}
valueLabelDisplay="auto"
onChange={handleSliderChange}
sx={{ width: '50%', height: "auto"}}
/>
</Tooltip>
<Tooltip title="Reset speed">
<Button onClick={() => onStepSizeChange(1)}>
<RestartAltIcon />
</Button>
</Tooltip>
</Stack>
</Stack>
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ function App() {
const [showAgentId, setShowAgentId] = React.useState<boolean>(false);
const [tracePaths, setTracePaths] = React.useState<boolean>(true);
const [canScreenshot, setCanScreenshot] = React.useState<boolean>(true);
const [showCellId, setShowCellId] = React.useState<boolean>(false);

const handleSkipBackward = () => {
if (pixiAppRef.current?.skipBackward) {
Expand Down Expand Up @@ -70,6 +71,7 @@ function App() {
showAgentId={showAgentId}
tracePaths={tracePaths}
setCanScreenshot={setCanScreenshot}
showCellId={showCellId}
/>
</Grid>
<Grid size={4}>
Expand All @@ -93,6 +95,8 @@ function App() {
onTracePathsChange={(tracePaths: boolean) => setTracePaths(tracePaths)}
canScreenshot={canScreenshot}
takeScreenshot={handleTakeScreenshot}
showCellId={showCellId}
setShowCellId={setShowCellId}
/>
</Grid>
</Grid>
Expand Down
6 changes: 6 additions & 0 deletions src/ConfigBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ interface ConfigBarProps {
onTracePathsChange: (tracePaths: boolean) => void;
canScreenshot: boolean;
takeScreenshot: () => void;
showCellId: boolean;
setShowCellId: (showCellId: boolean) => void;
}

function ConfigBar({
Expand All @@ -50,6 +52,8 @@ function ConfigBar({
onTracePathsChange,
canScreenshot,
takeScreenshot,
showCellId,
setShowCellId,
}: ConfigBarProps) {
const repoName = "JustinShetty/mapf-visualizer";
const [mapFile, setMapFile] = React.useState<File | null>(null);
Expand Down Expand Up @@ -213,6 +217,8 @@ function ConfigBar({
onTracePathsChange={onTracePathsChange}
canScreenshot={canScreenshot}
takeScreenshot={takeScreenshot}
showCellId={showCellId}
setShowCellId={setShowCellId}
/>
<Divider />
<a target="_blank" href={`https://github.com/${repoName}`} style={{ color: 'white', width: 'fit-content' }}>
Expand Down
54 changes: 41 additions & 13 deletions src/PixiApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Coordinate } from './Graph';
import { BACKGROUND_COLOR, GRID_COLOR, TEXT_COLOR, AGENT_COLORS } from './Params';

const GRID_UNIT_TO_PX: number = 100;
const FONT_SUPER_RESOLUTION_SCALE = 3;

interface PixiAppProps {
width: number;
Expand All @@ -19,19 +20,37 @@ interface PixiAppProps {
showAgentId: boolean;
tracePaths: boolean;
setCanScreenshot: (canScreenshot: boolean) => void;
showCellId: boolean,
}

function drawGrid(viewport: Viewport, graph: Graph) : PIXI.Container {
function drawGrid(viewport: Viewport, graph: Graph, showCellId: boolean) : PIXI.Container {
const grid = viewport.addChild(new PIXI.Container());

for (let x: number = 0; x < graph.width; x++) {
for (let y: number = 0; y < graph.height; y++) {
const cell = grid.addChild(new PIXI.Graphics());
cell.rect(x*GRID_UNIT_TO_PX, y*GRID_UNIT_TO_PX, GRID_UNIT_TO_PX, GRID_UNIT_TO_PX)
.stroke({color: GRID_COLOR, width: 10});
const cellContainer = grid.addChild(new PIXI.Container());
const cellGraphic = cellContainer.addChild(new PIXI.Graphics());
const cellX = x * GRID_UNIT_TO_PX;
const cellY = y * GRID_UNIT_TO_PX;
const strokeWidth = GRID_UNIT_TO_PX / 10;
cellGraphic.rect(cellX, cellY, GRID_UNIT_TO_PX, GRID_UNIT_TO_PX)
.stroke({color: GRID_COLOR, width: strokeWidth});
if (graph.obstacles.has(new Coordinate(x, y).toString())) {
cell.fill({color: GRID_COLOR});
cellGraphic.fill({color: GRID_COLOR});
}
const idText = cellContainer.addChild(new PIXI.Text({
text: `${x + y * graph.width}`,
style: {
fontFamily: 'Arial',
fontSize: cellGraphic.width / 6,
fill: TEXT_COLOR,
}
}));
idText.style.fontSize *= FONT_SUPER_RESOLUTION_SCALE;
idText.scale.set(1 / FONT_SUPER_RESOLUTION_SCALE, 1 / FONT_SUPER_RESOLUTION_SCALE);
idText.x = cellX + strokeWidth;
idText.y = cellY + strokeWidth;
idText.visible = showCellId;
}
}

Expand All @@ -51,6 +70,7 @@ const PixiApp = forwardRef(({
showAgentId,
tracePaths,
setCanScreenshot,
showCellId,
}: PixiAppProps, ref) => {
// this is a mess of state and refs, but how I got everything to work...
// maybe someday I will clean this up or maybe someone who knows React better than me can help
Expand Down Expand Up @@ -131,7 +151,7 @@ const PixiApp = forwardRef(({
}, [viewport, grid]);

const moveAndRotateSprites = useCallback((agents: PIXI.Container[], currentTime: number) => {
if (solution === null) return
if (solution === null) return;

const currentTimestep = Math.floor(currentTime);
const interpolationProgress = currentTime - currentTimestep;
Expand Down Expand Up @@ -265,13 +285,12 @@ const PixiApp = forwardRef(({
// Create agents based on the first configuration
const agents = viewport.addChild(new PIXI.Container());
agentsRef.current = agents;
let agentId = 0;
solution[0].forEach(() => {
solution[0].forEach((_pose, agentId) => {
// build agent
const agent = agents.addChild(new PIXI.Container());
const circleContainer = agent.addChild(new PIXI.Container());
const circle = circleContainer.addChild(new PIXI.Graphics());
const agentColor = AGENT_COLORS[agentId++ % AGENT_COLORS.length];
const agentColor = AGENT_COLORS[agentId % AGENT_COLORS.length];
circle
.circle(0, 0, GRID_UNIT_TO_PX/3)
.fill(agentColor);
Expand All @@ -290,9 +309,8 @@ const PixiApp = forwardRef(({
fill: TEXT_COLOR,
}
}));
const fontSuperResolutionScale = 2;
idText.style.fontSize *= fontSuperResolutionScale;
idText.scale.set(1 / fontSuperResolutionScale, 1 / fontSuperResolutionScale);
idText.style.fontSize *= FONT_SUPER_RESOLUTION_SCALE;
idText.scale.set(1 / FONT_SUPER_RESOLUTION_SCALE, 1 / FONT_SUPER_RESOLUTION_SCALE);
idText.x = -idText.width / 2;
idText.y = -idText.height / 2;
});
Expand Down Expand Up @@ -384,7 +402,7 @@ const PixiApp = forwardRef(({
useEffect(() => {
if (app && viewport) {
if (grid) viewport.removeChild(grid);
if (graph) setGrid(drawGrid(viewport, graph));
if (graph) setGrid(drawGrid(viewport, graph, showCellId));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [app, graph, viewport]); // Excluding 'grid' to prevent infinite loop
Expand Down Expand Up @@ -412,6 +430,16 @@ const PixiApp = forwardRef(({
tracePathsRef.current = tracePaths;
}, [tracePaths]);

useEffect(() => {
if (!grid) return;
grid.children.forEach((cellContainer) => {
const idText = cellContainer.children[1];
if (idText) {
idText.visible = showCellId;
}
});
}, [showCellId, grid]);

return <canvas ref={canvasRef} />
});

Expand Down
3 changes: 3 additions & 0 deletions src/Visualizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface VisualizerProps {
showAgentId: boolean;
tracePaths: boolean;
setCanScreenshot: (canScreenshot: boolean) => void;
showCellId: boolean;
}

function Visualizer({
Expand All @@ -26,6 +27,7 @@ function Visualizer({
showAgentId,
tracePaths,
setCanScreenshot,
showCellId,
}: VisualizerProps) {
const [viewportSize, setViewportSize] = useState<{ width: number; height: number } | null>(null);
const boxRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -61,6 +63,7 @@ function Visualizer({
showAgentId={showAgentId}
tracePaths={tracePaths}
setCanScreenshot={setCanScreenshot}
showCellId={showCellId}
/>
}
</div>
Expand Down

0 comments on commit 3b3344d

Please sign in to comment.