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 { makeStyles } from "@material-ui/styles";
|
||||
import { Paper, Divider } from "@material-ui/core";
|
||||
import { MenuHeader } from "./MenuHeader";
|
||||
|
||||
import { MenuSolverControls } from "./MenuSolverControls";
|
||||
import { MenuMetrics } from "./MenuMetrics";
|
||||
import { MenuPointControls } from "./MenuPointControls";
|
||||
import { OtherControls } from "./OtherControls";
|
||||
import { MemoryUsage } from "./MemoryUsage";
|
||||
|
||||
|
@ -28,9 +27,9 @@ export const Menu = ({
|
|||
onStart,
|
||||
onPause,
|
||||
onUnPause,
|
||||
onFullSpeed,
|
||||
|
||||
onStop,
|
||||
onRandomizePoints
|
||||
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
|
@ -38,22 +37,21 @@ export const Menu = ({
|
|||
<Paper classes={{ root: classes.wrapper }} style={{
|
||||
position: "fixed",
|
||||
width: "370px",
|
||||
height: "495px",
|
||||
height: "625px",
|
||||
top: "80px",
|
||||
left: "20px",
|
||||
color: "black",
|
||||
color: "white",
|
||||
padding: "15px",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "2px 2px 10px rgba(0, 0, 0, 0.3)",
|
||||
}}>
|
||||
|
||||
<MenuMetrics />
|
||||
<Divider />
|
||||
<MenuSolverControls
|
||||
onStart={onStart}
|
||||
onPause={onPause}
|
||||
onUnPause={onUnPause}
|
||||
onStop={onStop}
|
||||
onFullSpeed={onFullSpeed}
|
||||
/>
|
||||
<Divider />
|
||||
<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,
|
||||
onPause,
|
||||
onUnPause,
|
||||
onFullSpeed,
|
||||
onStop
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const algorithms = useAlgorithmInfo();
|
||||
const selectedAlgorithm = useSelector(selectors.selectAlgorithm);
|
||||
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 fullSpeed = useSelector(selectors.selectFullSpeed);
|
||||
const paused = useSelector(selectors.selectPaused);
|
||||
|
@ -61,24 +53,11 @@ export const MenuSolverControls = ({
|
|||
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 = () => {
|
||||
onStop();
|
||||
dispatch(actions.resetSolverState());
|
||||
};
|
||||
|
||||
const onShowAlgInfo = () => {
|
||||
dispatch(actions.toggleAlgInfoOpen());
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuSection highlight>
|
||||
|
|
|
@ -20,7 +20,7 @@ export const Navbar = () => {
|
|||
const leftStyle = {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '15px', // Increased spacing between the logo and text
|
||||
gap: '15px',
|
||||
fontSize: '20px',
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
|
@ -57,7 +57,7 @@ export const Navbar = () => {
|
|||
About
|
||||
</Link>
|
||||
<Link
|
||||
to="/source-code"
|
||||
to="https://forge.techtransthai.org/deepseekers/traveling-salesman-bangkok"
|
||||
style={linkStyle}
|
||||
onMouseOver={(e) => e.target.style.textDecoration = 'underline'}
|
||||
onMouseOut={(e) => e.target.style.textDecoration = 'none'}>
|
||||
|
|
|
@ -4,10 +4,8 @@ export * from "./IntroductionModal";
|
|||
export * from "./Layout";
|
||||
export * from "./MapPlot";
|
||||
export * from "./Menu";
|
||||
export * from "./MenuHeader";
|
||||
export * from "./MenuItem";
|
||||
export * from "./MenuMetrics";
|
||||
export * from "./MenuPointControls";
|
||||
export * from "./MenuSection";
|
||||
export * from "./MenuSolverControls";
|
||||
export * from "../context/PreSetTheme";
|
||||
|
|
|
@ -32,17 +32,8 @@ const IndexPage = () => {
|
|||
selectors.selectEvaluatingDetailLevel
|
||||
);
|
||||
const points = useSelector(selectors.selectPoints);
|
||||
const pointCount = useSelector(selectors.selectPointCount);
|
||||
const definingPoints = useSelector(selectors.selectDefiningPoints);
|
||||
|
||||
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(() => {
|
||||
dispatch(actions.startSolving(points, delay, evaluatingDetailLevel));
|
||||
|
@ -51,10 +42,6 @@ const IndexPage = () => {
|
|||
);
|
||||
}, [solver, dispatch, delay, points, evaluatingDetailLevel]);
|
||||
|
||||
const fullSpeed = useCallback(() => {
|
||||
dispatch(actions.goFullSpeed());
|
||||
solver.postMessage(actions.goFullSpeed());
|
||||
}, [solver, dispatch]);
|
||||
|
||||
const pause = useCallback(() => {
|
||||
dispatch(actions.pause());
|
||||
|
@ -94,9 +81,7 @@ const IndexPage = () => {
|
|||
onStart={start}
|
||||
onPause={pause}
|
||||
onUnPause={unpause}
|
||||
onFullSpeed={fullSpeed}
|
||||
onStop={stop}
|
||||
onRandomizePoints={onRandomizePoints}
|
||||
/>
|
||||
<MapPlot ref={mapRef}></MapPlot>
|
||||
</Layout>
|
||||
|
|
Loading…
Add table
Reference in a new issue