This commit is contained in:
parent
93bce6b497
commit
5a46f836f8
7 changed files with 8 additions and 262 deletions
|
@ -1,10 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { makeStyles } from "@material-ui/styles";
|
import { makeStyles } from "@material-ui/styles";
|
||||||
import { Paper, Divider } from "@material-ui/core";
|
import { Paper, Divider } from "@material-ui/core";
|
||||||
import { MenuHeader } from "./MenuHeader";
|
|
||||||
import { MenuSolverControls } from "./MenuSolverControls";
|
import { MenuSolverControls } from "./MenuSolverControls";
|
||||||
import { MenuMetrics } from "./MenuMetrics";
|
import { MenuMetrics } from "./MenuMetrics";
|
||||||
import { MenuPointControls } from "./MenuPointControls";
|
|
||||||
import { OtherControls } from "./OtherControls";
|
import { OtherControls } from "./OtherControls";
|
||||||
import { MemoryUsage } from "./MemoryUsage";
|
import { MemoryUsage } from "./MemoryUsage";
|
||||||
|
|
||||||
|
@ -28,9 +27,9 @@ export const Menu = ({
|
||||||
onStart,
|
onStart,
|
||||||
onPause,
|
onPause,
|
||||||
onUnPause,
|
onUnPause,
|
||||||
onFullSpeed,
|
|
||||||
onStop,
|
onStop,
|
||||||
onRandomizePoints
|
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
|
@ -38,22 +37,21 @@ export const Menu = ({
|
||||||
<Paper classes={{ root: classes.wrapper }} style={{
|
<Paper classes={{ root: classes.wrapper }} style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
width: "370px",
|
width: "370px",
|
||||||
height: "495px",
|
height: "625px",
|
||||||
top: "80px",
|
top: "80px",
|
||||||
left: "20px",
|
left: "20px",
|
||||||
color: "black",
|
color: "white",
|
||||||
padding: "15px",
|
padding: "15px",
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
boxShadow: "2px 2px 10px rgba(0, 0, 0, 0.3)",
|
boxShadow: "2px 2px 10px rgba(0, 0, 0, 0.3)",
|
||||||
}}>
|
}}>
|
||||||
|
<MenuMetrics />
|
||||||
<Divider />
|
<Divider />
|
||||||
<MenuSolverControls
|
<MenuSolverControls
|
||||||
onStart={onStart}
|
onStart={onStart}
|
||||||
onPause={onPause}
|
onPause={onPause}
|
||||||
onUnPause={onUnPause}
|
onUnPause={onUnPause}
|
||||||
onStop={onStop}
|
onStop={onStop}
|
||||||
onFullSpeed={onFullSpeed}
|
|
||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider />
|
||||||
<MemoryUsage />
|
<MemoryUsage />
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { useDispatch } from "react-redux";
|
|
||||||
import { Grid, Typography, IconButton, Tooltip } from "@material-ui/core";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import {
|
|
||||||
faInfoCircle,
|
|
||||||
faBriefcase,
|
|
||||||
faDonate
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { faGithub } from "@fortawesome/free-brands-svg-icons";
|
|
||||||
import { makeStyles } from "@material-ui/styles";
|
|
||||||
import { MenuSection } from "./MenuSection";
|
|
||||||
|
|
||||||
import * as actions from "../store/actions";
|
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
|
||||||
root: {
|
|
||||||
paddingTop: theme.spacing(3),
|
|
||||||
paddingBottom: theme.spacing(3)
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontSize: "1.2rem"
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const MenuHeader = props => {
|
|
||||||
const classes = useStyles();
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const onOpenSiteInfo = () => {
|
|
||||||
dispatch(actions.toggleSiteInfoOpen());
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuSection>
|
|
||||||
<Grid container justify="space-between" alignItems="center">
|
|
||||||
<Typography
|
|
||||||
gutterBottom
|
|
||||||
display="inline"
|
|
||||||
variant="button"
|
|
||||||
component="h1"
|
|
||||||
classes={{ root: classes.title }}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faBriefcase} width="0" /> TSPVIS
|
|
||||||
</Typography>
|
|
||||||
<Typography gutterBottom display="inline" color="textSecondary">
|
|
||||||
<Tooltip title="Source code">
|
|
||||||
<IconButton
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/jhackshaw/tspvis"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faGithub} size="xs" width="0" />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip title="General site information">
|
|
||||||
<IconButton onClick={onOpenSiteInfo} edge="end">
|
|
||||||
<FontAwesomeIcon icon={faInfoCircle} size="xs" width="0" />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Typography variant="subtitle2" color="textSecondary">
|
|
||||||
Visualize algorithms for the traveling salesman problem. Use the
|
|
||||||
controls below to plot points, choose an algorithm, and control
|
|
||||||
execution.
|
|
||||||
<br />
|
|
||||||
(Hint: try a construction alogorithm followed by an improvement
|
|
||||||
algorithm)
|
|
||||||
</Typography>
|
|
||||||
</MenuSection>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,141 +0,0 @@
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
|
||||||
import {
|
|
||||||
ButtonGroup,
|
|
||||||
Button,
|
|
||||||
Slider,
|
|
||||||
Grid,
|
|
||||||
Typography,
|
|
||||||
makeStyles
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import {
|
|
||||||
faRandom,
|
|
||||||
faSave,
|
|
||||||
faMousePointer,
|
|
||||||
faMapMarked
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
|
|
||||||
import { MenuSection } from "./MenuSection";
|
|
||||||
import { MenuItem } from "./MenuItem";
|
|
||||||
import * as selectors from "../store/selectors";
|
|
||||||
import * as actions from "../store/actions";
|
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
|
||||||
grow: {
|
|
||||||
flexGrow: 1
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// [0, 1/2, 1, 3, 12]
|
|
||||||
let cache = ["1e+0", "1e+0"];
|
|
||||||
const possRoutes = n => {
|
|
||||||
if (n <= 2) {
|
|
||||||
return "1e+0";
|
|
||||||
}
|
|
||||||
if (typeof cache[n - 1] !== "undefined") {
|
|
||||||
return cache[n - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = 1;
|
|
||||||
|
|
||||||
for (let i = 1; i <= n; i++) {
|
|
||||||
result *= i;
|
|
||||||
cache[i] = (result / 2).toExponential(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cache[n - 1];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MenuPointControls = ({ onRandomizePoints }) => {
|
|
||||||
const classes = useStyles();
|
|
||||||
const [possiblePaths, setPossiblePaths] = useState("0");
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const pointCount = useSelector(selectors.selectPointCount);
|
|
||||||
const running = useSelector(selectors.selectRunning);
|
|
||||||
const definingPoints = useSelector(selectors.selectDefiningPoints);
|
|
||||||
|
|
||||||
const onDefaultMap = () => {
|
|
||||||
dispatch(actions.setDefaultMap());
|
|
||||||
};
|
|
||||||
|
|
||||||
const onToggleDefiningPoints = () => {
|
|
||||||
const action = definingPoints
|
|
||||||
? actions.stopDefiningPoints()
|
|
||||||
: actions.startDefiningPoints();
|
|
||||||
dispatch(action);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPointCountChange = (_, newCount) => {
|
|
||||||
dispatch(actions.setPointCount(newCount));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setPossiblePaths(possRoutes(pointCount));
|
|
||||||
}, [pointCount]);
|
|
||||||
|
|
||||||
const [num, exp] = possiblePaths.split("e+");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MenuSection>
|
|
||||||
<MenuItem title="Points">
|
|
||||||
<ButtonGroup
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
color="secondary"
|
|
||||||
size="large"
|
|
||||||
disabled={running}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
onClick={onRandomizePoints}
|
|
||||||
disabled={definingPoints || pointCount < 3}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faRandom} width="0" />
|
|
||||||
</Button>
|
|
||||||
<Button onClick={onToggleDefiningPoints}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={definingPoints ? faSave : faMousePointer}
|
|
||||||
width="0"
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<Button disabled={definingPoints} onClick={onDefaultMap}>
|
|
||||||
<FontAwesomeIcon icon={faMapMarked} width="0" />
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
</MenuItem>
|
|
||||||
|
|
||||||
<MenuItem title="Number of random points">
|
|
||||||
<Slider
|
|
||||||
value={pointCount}
|
|
||||||
onChange={onPointCountChange}
|
|
||||||
step={1}
|
|
||||||
min={3}
|
|
||||||
max={200}
|
|
||||||
valueLabelDisplay="auto"
|
|
||||||
color="secondary"
|
|
||||||
disabled={running || definingPoints}
|
|
||||||
/>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem row>
|
|
||||||
<Grid item container justify="space-between">
|
|
||||||
<Typography
|
|
||||||
display="inline"
|
|
||||||
variant="button"
|
|
||||||
color="textSecondary"
|
|
||||||
component="div"
|
|
||||||
>
|
|
||||||
Possible Paths:{" "}
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
classes={{ root: classes.grow }}
|
|
||||||
align="right"
|
|
||||||
display="inline"
|
|
||||||
component="span"
|
|
||||||
>
|
|
||||||
{num} x 10<sup>{exp}</sup>
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</MenuItem>
|
|
||||||
</MenuSection>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -30,20 +30,12 @@ export const MenuSolverControls = ({
|
||||||
onStart,
|
onStart,
|
||||||
onPause,
|
onPause,
|
||||||
onUnPause,
|
onUnPause,
|
||||||
onFullSpeed,
|
|
||||||
onStop
|
onStop
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const algorithms = useAlgorithmInfo();
|
const algorithms = useAlgorithmInfo();
|
||||||
const selectedAlgorithm = useSelector(selectors.selectAlgorithm);
|
const selectedAlgorithm = useSelector(selectors.selectAlgorithm);
|
||||||
const delay = useSelector(selectors.selectDelay);
|
const delay = useSelector(selectors.selectDelay);
|
||||||
const evaluatingDetailLevel = useSelector(
|
|
||||||
selectors.selectEvaluatingDetailLevel
|
|
||||||
);
|
|
||||||
const maxEvaluatingDetailLevel = useSelector(
|
|
||||||
selectors.selectMaxEvaluatingDetailLevel
|
|
||||||
);
|
|
||||||
const showBestPath = useSelector(selectors.selectShowBestPath);
|
|
||||||
const running = useSelector(selectors.selectRunning);
|
const running = useSelector(selectors.selectRunning);
|
||||||
const fullSpeed = useSelector(selectors.selectFullSpeed);
|
const fullSpeed = useSelector(selectors.selectFullSpeed);
|
||||||
const paused = useSelector(selectors.selectPaused);
|
const paused = useSelector(selectors.selectPaused);
|
||||||
|
@ -61,24 +53,11 @@ export const MenuSolverControls = ({
|
||||||
dispatch(actions.setDelay(newDelay));
|
dispatch(actions.setDelay(newDelay));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onEvaluatingDetailLevelChange = (onLevel, offLevel) => event => {
|
|
||||||
const level = event.target.checked ? onLevel : offLevel;
|
|
||||||
dispatch(actions.setEvaluatingDetailLevel(level));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onShowBestPathChange = event => {
|
|
||||||
dispatch(actions.setShowBestPath(event.target.checked));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onReset = () => {
|
const onReset = () => {
|
||||||
onStop();
|
onStop();
|
||||||
dispatch(actions.resetSolverState());
|
dispatch(actions.resetSolverState());
|
||||||
};
|
};
|
||||||
|
|
||||||
const onShowAlgInfo = () => {
|
|
||||||
dispatch(actions.toggleAlgInfoOpen());
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MenuSection highlight>
|
<MenuSection highlight>
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const Navbar = () => {
|
||||||
const leftStyle = {
|
const leftStyle = {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '15px', // Increased spacing between the logo and text
|
gap: '15px',
|
||||||
fontSize: '20px',
|
fontSize: '20px',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
};
|
};
|
||||||
|
@ -57,7 +57,7 @@ export const Navbar = () => {
|
||||||
About
|
About
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/source-code"
|
to="https://forge.techtransthai.org/deepseekers/traveling-salesman-bangkok"
|
||||||
style={linkStyle}
|
style={linkStyle}
|
||||||
onMouseOver={(e) => e.target.style.textDecoration = 'underline'}
|
onMouseOver={(e) => e.target.style.textDecoration = 'underline'}
|
||||||
onMouseOut={(e) => e.target.style.textDecoration = 'none'}>
|
onMouseOut={(e) => e.target.style.textDecoration = 'none'}>
|
||||||
|
|
|
@ -4,10 +4,8 @@ export * from "./IntroductionModal";
|
||||||
export * from "./Layout";
|
export * from "./Layout";
|
||||||
export * from "./MapPlot";
|
export * from "./MapPlot";
|
||||||
export * from "./Menu";
|
export * from "./Menu";
|
||||||
export * from "./MenuHeader";
|
|
||||||
export * from "./MenuItem";
|
export * from "./MenuItem";
|
||||||
export * from "./MenuMetrics";
|
export * from "./MenuMetrics";
|
||||||
export * from "./MenuPointControls";
|
|
||||||
export * from "./MenuSection";
|
export * from "./MenuSection";
|
||||||
export * from "./MenuSolverControls";
|
export * from "./MenuSolverControls";
|
||||||
export * from "../context/PreSetTheme";
|
export * from "../context/PreSetTheme";
|
||||||
|
|
|
@ -32,17 +32,8 @@ const IndexPage = () => {
|
||||||
selectors.selectEvaluatingDetailLevel
|
selectors.selectEvaluatingDetailLevel
|
||||||
);
|
);
|
||||||
const points = useSelector(selectors.selectPoints);
|
const points = useSelector(selectors.selectPoints);
|
||||||
const pointCount = useSelector(selectors.selectPointCount);
|
|
||||||
const definingPoints = useSelector(selectors.selectDefiningPoints);
|
|
||||||
|
|
||||||
const solver = useSolverWorker(dispatch, algorithm);
|
const solver = useSolverWorker(dispatch, algorithm);
|
||||||
|
|
||||||
const onRandomizePoints = useCallback(() => {
|
|
||||||
if (!definingPoints) {
|
|
||||||
const bounds = mapRef.current.getBounds();
|
|
||||||
dispatch(actions.randomizePoints(bounds, pointCount));
|
|
||||||
}
|
|
||||||
}, [mapRef, dispatch, pointCount, definingPoints]);
|
|
||||||
|
|
||||||
const start = useCallback(() => {
|
const start = useCallback(() => {
|
||||||
dispatch(actions.startSolving(points, delay, evaluatingDetailLevel));
|
dispatch(actions.startSolving(points, delay, evaluatingDetailLevel));
|
||||||
|
@ -51,10 +42,6 @@ const IndexPage = () => {
|
||||||
);
|
);
|
||||||
}, [solver, dispatch, delay, points, evaluatingDetailLevel]);
|
}, [solver, dispatch, delay, points, evaluatingDetailLevel]);
|
||||||
|
|
||||||
const fullSpeed = useCallback(() => {
|
|
||||||
dispatch(actions.goFullSpeed());
|
|
||||||
solver.postMessage(actions.goFullSpeed());
|
|
||||||
}, [solver, dispatch]);
|
|
||||||
|
|
||||||
const pause = useCallback(() => {
|
const pause = useCallback(() => {
|
||||||
dispatch(actions.pause());
|
dispatch(actions.pause());
|
||||||
|
@ -94,9 +81,7 @@ const IndexPage = () => {
|
||||||
onStart={start}
|
onStart={start}
|
||||||
onPause={pause}
|
onPause={pause}
|
||||||
onUnPause={unpause}
|
onUnPause={unpause}
|
||||||
onFullSpeed={fullSpeed}
|
|
||||||
onStop={stop}
|
onStop={stop}
|
||||||
onRandomizePoints={onRandomizePoints}
|
|
||||||
/>
|
/>
|
||||||
<MapPlot ref={mapRef}></MapPlot>
|
<MapPlot ref={mapRef}></MapPlot>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue