├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── debug.log
├── package-lock.json
├── package.json
├── public
├── index.html
├── location.png
├── manifest.json
└── robots.txt
├── src
├── App.css
├── App.test.tsx
├── App.tsx
├── components
│ ├── Controller
│ │ ├── Controller.module.css
│ │ └── Controller.tsx
│ ├── MobileController
│ │ ├── MobileController.module.css
│ │ └── MobileController.tsx
│ └── UI
│ │ ├── Dropdown
│ │ ├── Dropdown.module.css
│ │ └── Dropdown.tsx
│ │ └── Navbar
│ │ ├── Navbar.module.css
│ │ └── Navbar.tsx
├── containers
│ └── PathfindingVisualizer
│ │ ├── 4913
│ │ ├── PathfindingVisualizer.module.css
│ │ ├── PathfindingVisualizer.tsx
│ │ └── Vertices
│ │ ├── Vertex
│ │ ├── Vertex.module.css
│ │ └── Vertex.tsx
│ │ ├── VertexRow
│ │ ├── VertexRow.module.css
│ │ └── VertexRow.tsx
│ │ ├── Vertices.module.css
│ │ └── Vertices.tsx
├── fonts
│ ├── Avengers
│ │ └── Avengers.ttf
│ ├── Lakers
│ │ └── lakers.ttf
│ ├── Montserrat
│ │ ├── Montserrat-Black.ttf
│ │ ├── Montserrat-BlackItalic.ttf
│ │ ├── Montserrat-Bold.ttf
│ │ ├── Montserrat-BoldItalic.ttf
│ │ ├── Montserrat-ExtraBold.ttf
│ │ ├── Montserrat-ExtraBoldItalic.ttf
│ │ ├── Montserrat-ExtraLight.ttf
│ │ ├── Montserrat-ExtraLightItalic.ttf
│ │ ├── Montserrat-Italic.ttf
│ │ ├── Montserrat-Light.ttf
│ │ ├── Montserrat-LightItalic.ttf
│ │ ├── Montserrat-Medium.ttf
│ │ ├── Montserrat-MediumItalic.ttf
│ │ ├── Montserrat-Regular.ttf
│ │ ├── Montserrat-SemiBold.ttf
│ │ ├── Montserrat-SemiBoldItalic.ttf
│ │ ├── Montserrat-Thin.ttf
│ │ ├── Montserrat-ThinItalic.ttf
│ │ └── OFL.txt
│ ├── OnePiece
│ │ └── OnePiece.ttf
│ ├── Pokemon
│ │ ├── PokemonHollow.ttf
│ │ └── PokemonSolid.ttf
│ ├── TheOffice
│ │ ├── TheOffice.ttf
│ │ └── readme.html
│ ├── avengers.css
│ ├── lakers.css
│ ├── montserrat.css
│ ├── onePiece.css
│ ├── pokemon.css
│ └── theOffice.css
├── index.css
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
├── serviceWorker.ts
├── setupTests.ts
├── store
│ ├── actions
│ │ ├── drag.ts
│ │ ├── graph.ts
│ │ ├── index.ts
│ │ └── types.ts
│ └── reducers
│ │ ├── drag.ts
│ │ ├── graph.ts
│ │ └── index.ts
└── utils
│ ├── colors.ts
│ ├── pathfinding
│ ├── algorithms
│ │ ├── aStar.ts
│ │ ├── breadthFirstSearch.ts
│ │ ├── buildPath.ts
│ │ ├── depthFirstSearch.ts
│ │ ├── dijkstra.ts
│ │ ├── graph.ts
│ │ ├── graphTypes.ts
│ │ ├── greedyBestFirstSearch.ts
│ │ ├── index.ts
│ │ └── priorityQueue.ts
│ ├── pathfindingAlgorithms.ts
│ ├── pathfindingOptions.ts
│ └── pathfindingStates.ts
│ ├── position.ts
│ └── themes
│ ├── avengers.ts
│ ├── car.ts
│ ├── hunterxhunter.ts
│ ├── img
│ ├── badge.png
│ ├── bigmom.png
│ ├── bigmomCursor.png
│ ├── blackbeard.png
│ ├── blackbeardCursor.png
│ ├── building.png
│ ├── buildingCursor.png
│ ├── captainAmerica.png
│ ├── captainMarvel.png
│ ├── car.png
│ ├── carObstacle.png
│ ├── carObstacleCursor.png
│ ├── celtics.png
│ ├── celticsCursor.png
│ ├── chineseFood.png
│ ├── chineseFoodCursor.png
│ ├── clippers.png
│ ├── clippersCursor.png
│ ├── cone.png
│ ├── coneCursor.png
│ ├── gasStation.png
│ ├── gasStationCursor.png
│ ├── gauntlet.png
│ ├── ging.png
│ ├── gon.png
│ ├── groudon.png
│ ├── groudonCursor.png
│ ├── hill.png
│ ├── hillCursor.png
│ ├── hisoka.png
│ ├── hisokaCursor.png
│ ├── holly.jpg
│ ├── ironMan.png
│ ├── kaido.png
│ ├── kaidoCursor.png
│ ├── kyogre.png
│ ├── kyogreCursor.png
│ ├── lakers.png
│ ├── latias.png
│ ├── latiasCursor.png
│ ├── location.png
│ ├── meruem.png
│ ├── meruemCursor.png
│ ├── michael.png
│ ├── phone.png
│ ├── phoneCursor.png
│ ├── pistons.png
│ ├── pistonsCursor.png
│ ├── pokemonPlayer.png
│ ├── rain.png
│ ├── rainCursor.png
│ ├── rayquaza.png
│ ├── rayquazaCursor.png
│ ├── razor.png
│ ├── razorCursor.png
│ ├── road-horizontal.jpg
│ ├── road-vertical.jpg
│ ├── shanks.png
│ ├── shanksCursor.png
│ ├── spurs.png
│ ├── strawhat.png
│ ├── sunny.png
│ ├── thanos.png
│ ├── thor.png
│ ├── treasure.png
│ ├── tree.png
│ ├── trophy.png
│ ├── troupe.png
│ └── troupeCursor.png
│ ├── index.ts
│ ├── lakers.ts
│ ├── onePiece.ts
│ ├── pokemon.ts
│ └── theOffice.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "'s",
4 | "Choo",
5 | "Choo's",
6 | "Choo\\'s",
7 | "DARKBLUE",
8 | "Dropdowns",
9 | "Groudon",
10 | "Kyogre",
11 | "LIGHTBLUE",
12 | "Latias",
13 | "Meruem",
14 | "THEOFFICE",
15 | "bigmom",
16 | "browserslist",
17 | "darr",
18 | "focusable",
19 | "ging",
20 | "hunterxhunter",
21 | "kaido",
22 | "larr",
23 | "pathfinding",
24 | "rarr",
25 | "rayquaza",
26 | "thanos",
27 | "uarr"
28 | ]
29 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pathfinding Visualizer
2 |
3 | This is my version of the pathfinding visualization project. Like my sorting visualization project, this project was also inspired by [Clement Mihailescu](https://github.com/clementmihailescu) (make sure you checkout his company AlgoExpert on [algoexpert.io](https://algoexpert.io)) to practice for coding interviews).
4 |
5 | The pathfinding algorithms implemented were Dijkstra's algorithm, A\*, Greedy Best-First Search, Breadth-First Search, and Depth-First Search.
6 |
7 | For this project I wanted to make fun themes on some of my favorite things including One Piece, Avengers, Lakers, The Office (U.S. of course), and more.
8 |
9 | [Live Demo](https://princhcanal.github.io/pathfinding-visualizer)
10 |
11 | The rest of this README was automatically generated by Create React App.
12 |
13 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
14 |
15 | ## Available Scripts
16 |
17 | In the project directory, you can run:
18 |
19 | ### `npm start`
20 |
21 | Runs the app in the development mode.
22 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
23 |
24 | The page will reload if you make edits.
25 | You will also see any lint errors in the console.
26 |
27 | ### `npm test`
28 |
29 | Launches the test runner in the interactive watch mode.
30 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
31 |
32 | ### `npm run build`
33 |
34 | Builds the app for production to the `build` folder.
35 | It correctly bundles React in production mode and optimizes the build for the best performance.
36 |
37 | The build is minified and the filenames include the hashes.
38 | Your app is ready to be deployed!
39 |
40 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
41 |
42 | ### `npm run eject`
43 |
44 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
45 |
46 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
47 |
48 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
49 |
50 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
51 |
52 | ## Learn More
53 |
54 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
55 |
56 | To learn React, check out the [React documentation](https://reactjs.org/).
57 |
--------------------------------------------------------------------------------
/debug.log:
--------------------------------------------------------------------------------
1 | [0721/160747.768:ERROR:http_transport_win.cc(276)] WinHttpSendRequest: The operation completed successfully. (0x0)
2 | [0721/162552.354:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
3 | [0721/162552.356:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
4 | [0721/162552.357:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
5 | [0721/162552.357:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
6 | [0721/162552.357:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
7 | [0721/162552.357:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
8 | [0721/162552.358:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
9 | [0721/162552.360:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
10 | [0721/162552.360:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
11 | [0721/162552.360:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
12 | [0721/162552.360:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
13 | [0721/162552.360:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
14 | [0721/162552.361:ERROR:process_reader_win.cc(151)] SuspendThread: Access is denied. (0x5)
15 | [0721/162552.361:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022)
16 | [0721/162552.362:ERROR:exception_snapshot_win.cc(98)] thread ID 20420 not found in process
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pathfinding-visualizer",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^4.2.4",
7 | "@testing-library/react": "^9.5.0",
8 | "@testing-library/user-event": "^7.2.1",
9 | "@types/jest": "^24.9.1",
10 | "@types/node": "^12.12.47",
11 | "@types/react": "^16.9.41",
12 | "@types/react-dom": "^16.9.8",
13 | "@types/react-redux": "^7.1.9",
14 | "@types/redux": "^3.6.0",
15 | "react": "^16.13.1",
16 | "react-dom": "^16.13.1",
17 | "react-redux": "^7.2.0",
18 | "react-scripts": "3.4.1",
19 | "redux": "^4.0.5",
20 | "redux-thunk": "^2.3.0",
21 | "typescript": "^3.7.5"
22 | },
23 | "scripts": {
24 | "start": "react-scripts start",
25 | "build": "react-scripts build",
26 | "test": "react-scripts test",
27 | "eject": "react-scripts eject",
28 | "deploy": "npm run build && gh-pages -d build",
29 | "deployy": "gh-pages -d build"
30 | },
31 | "eslintConfig": {
32 | "extends": "react-app"
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | },
46 | "homepage": "https://princhcanal.github.io/pathfinding-visualizer",
47 | "devDependencies": {
48 | "gh-pages": "^3.1.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 |
28 | Pathfinding Visualizer
29 |
30 |
31 | You need to enable JavaScript to run this app.
32 |
33 |
43 |
44 |
--------------------------------------------------------------------------------
/public/location.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/public/location.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "theme_color": "#000000",
7 | "background_color": "#ffffff"
8 | }
9 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/App.css
--------------------------------------------------------------------------------
/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render( );
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useLayoutEffect, useEffect, useCallback } from 'react';
2 | import './App.css';
3 |
4 | import PathfindingVisualizer from './containers/PathfindingVisualizer/PathfindingVisualizer';
5 | import { useDispatch } from 'react-redux';
6 | import * as Actions from './store/actions';
7 |
8 | const App = () => {
9 | const dispatch = useDispatch();
10 |
11 | const updateSize = useCallback(() => {
12 | if (window.innerWidth <= 500) {
13 | dispatch(Actions.setNumRows(11));
14 | dispatch(Actions.setNumCols(9));
15 | } else if (window.innerWidth <= 600) {
16 | dispatch(Actions.setNumRows(13));
17 | dispatch(Actions.setNumCols(13));
18 | } else if (window.innerWidth <= 820) {
19 | dispatch(Actions.setNumRows(13));
20 | dispatch(Actions.setNumCols(17));
21 | } else if (window.innerWidth <= 960) {
22 | dispatch(Actions.setNumRows(13));
23 | dispatch(Actions.setNumCols(21));
24 | } else if (window.innerWidth <= 1040) {
25 | dispatch(Actions.setNumRows(15));
26 | dispatch(Actions.setNumCols(25));
27 | } else {
28 | dispatch(Actions.setNumRows(17));
29 | dispatch(Actions.setNumCols(33));
30 | }
31 | }, [dispatch]);
32 |
33 | useLayoutEffect(() => {
34 | window.addEventListener('resize', updateSize);
35 |
36 | return () => window.removeEventListener('resize', updateSize);
37 | }, [updateSize]);
38 |
39 | useEffect(() => {
40 | updateSize();
41 | }, [updateSize]);
42 |
43 | return (
44 |
47 | );
48 | };
49 |
50 | export default App;
51 |
--------------------------------------------------------------------------------
/src/components/Controller/Controller.module.css:
--------------------------------------------------------------------------------
1 | .Controller {
2 | color: var(--color-4);
3 | width: 90%;
4 | margin: 0 auto;
5 | border-radius: 0.3rem;
6 | }
7 |
8 | .Buttons {
9 | display: flex;
10 | justify-content: space-around;
11 | padding: 1rem 0;
12 | }
13 |
14 | .Button {
15 | font-size: inherit;
16 | background-color: transparent;
17 | border: none;
18 | transition: all 0.2s;
19 | color: inherit;
20 | padding: 0.5rem 1rem;
21 | border-radius: 0.3rem;
22 | }
23 |
24 | .Button:hover {
25 | background-color: var(--color-4);
26 | color: var(--color-3);
27 | }
28 |
29 | .Button:active {
30 | color: red;
31 | }
32 |
33 | @media (hover: none) {
34 | .Button:hover {
35 | background-color: transparent;
36 | color: inherit;
37 | }
38 | }
39 |
40 | .Dropdowns {
41 | display: flex;
42 | justify-content: space-around;
43 | align-items: center;
44 | flex-wrap: wrap;
45 | padding: 1rem 0;
46 | /* border: 1px solid var(--color-1); */
47 | }
48 |
49 | .Dropdown {
50 | background-color: var(--color-4);
51 | color: var(--color-3);
52 | border: none;
53 | text-align-last: center;
54 | transition: all 0.2s;
55 | border-radius: 0.3rem;
56 | padding: 0.5rem 1rem;
57 | /* margin-left: 1rem; */
58 | }
59 |
60 | .Dropdown:active {
61 | outline: none;
62 | }
63 |
64 | .Dropdown option {
65 | background-color: var(--color-1);
66 | }
67 |
--------------------------------------------------------------------------------
/src/components/Controller/Controller.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | RefObject,
3 | MouseEvent,
4 | ForwardRefExoticComponent,
5 | RefAttributes,
6 | useState,
7 | } from 'react';
8 | import { useDispatch, useSelector } from 'react-redux';
9 |
10 | import styles from './Controller.module.css';
11 | import * as Actions from '../../store/actions';
12 | import * as Position from '../../utils/position';
13 | import { StoreState } from '../../store/reducers';
14 | import { Graph } from '../../utils/pathfinding/algorithms/graph';
15 | import { Vertex } from '../../store/reducers/graph';
16 | import { GraphTheme } from '../../utils/themes';
17 | import { PathfindingStates } from '../../utils/pathfinding/pathfindingStates';
18 |
19 | import Dropdown, { DropdownProps, DropdownRef } from '../UI/Dropdown/Dropdown';
20 | import { pathfindingOptions } from '../../utils/pathfinding/pathfindingOptions';
21 | import { pathfindingAlgorithms } from '../../utils/pathfinding/pathfindingAlgorithms';
22 | import { themes, themeOptions, obstacleOptions } from '../../utils/themes/';
23 |
24 | interface ControllerProps {
25 | classNames?: string[];
26 | verticesRef: RefObject;
27 | }
28 |
29 | export type Handle = T extends ForwardRefExoticComponent<
30 | DropdownProps & RefAttributes
31 | >
32 | ? T2
33 | : never;
34 |
35 | const Controller = (props: ControllerProps) => {
36 | let classNames = [styles.Controller];
37 | if (props.classNames) {
38 | classNames.push(...props.classNames);
39 | }
40 | const dispatch = useDispatch();
41 |
42 | const [obstacles, setObstacles] = useState(obstacleOptions['car']);
43 | const [speed, setSpeed] = useState(25);
44 | const graph = useSelector((state) => state.graph.graph);
45 | const numRows = useSelector(
46 | (state) => state.graph.numRows
47 | );
48 | const numCols = useSelector(
49 | (state) => state.graph.numCols
50 | );
51 | const startVertex = useSelector(
52 | (state) => state.graph.startVertex
53 | );
54 | const endVertex = useSelector(
55 | (state) => state.graph.endVertex
56 | );
57 | const setTimeouts = useSelector(
58 | (state) => state.graph.setTimeouts
59 | );
60 | const currentAlgorithm = useSelector(
61 | (state) => state.graph.currentAlgorithm
62 | );
63 | const theme = useSelector(
64 | (state) => state.graph.theme
65 | );
66 | let obstacleDropdown: Handle;
67 |
68 | const handleAnimation = (start: number, end: number) => {
69 | dispatch(Actions.clearPath(props.verticesRef));
70 | dispatch(Actions.setIsAnimating(true));
71 |
72 | let animations = currentAlgorithm(graph, start, end);
73 |
74 | for (let i = 0; i < animations.length; i++) {
75 | let animation = animations[i];
76 | let row = Math.floor(animation.index / numCols);
77 | let column = animation.index % numCols;
78 | let vertex: Vertex;
79 |
80 | vertex = Position.getVertex(
81 | row,
82 | column,
83 | numRows,
84 | numCols,
85 | props.verticesRef
86 | );
87 |
88 | setTimeouts.push(
89 | setTimeout(() => {
90 | if (animation.state === PathfindingStates.VISITING) {
91 | theme.visiting(vertex.element);
92 | if (i > 0) {
93 | let prevAnimation = animations[i - 1];
94 | let prevVertex = Position.getVertexAbsolute(
95 | prevAnimation.index,
96 | numRows,
97 | numCols,
98 | props.verticesRef
99 | );
100 | theme.visited(prevVertex.element);
101 | }
102 | } else if (
103 | animation.state ===
104 | PathfindingStates.PATH_HORIZONTAL_START
105 | ) {
106 | theme.pathHorizontalStart(vertex.element);
107 | } else if (
108 | animation.state ===
109 | PathfindingStates.PATH_VERTICAL_START
110 | ) {
111 | theme.pathVerticalStart(vertex.element);
112 | } else if (
113 | animation.state ===
114 | PathfindingStates.PATH_HORIZONTAL_END
115 | ) {
116 | theme.pathHorizontalEnd(vertex.element);
117 | } else if (
118 | animation.state === PathfindingStates.PATH_VERTICAL_END
119 | ) {
120 | theme.pathVerticalEnd(vertex.element);
121 | } else if (
122 | animation.state === PathfindingStates.PATH_HORIZONTAL
123 | ) {
124 | theme.pathHorizontal(vertex.element);
125 | } else if (
126 | animation.state === PathfindingStates.PATH_VERTICAL
127 | ) {
128 | theme.pathVertical(vertex.element);
129 | } else if (
130 | animation.state === PathfindingStates.DONE ||
131 | animation.state === PathfindingStates.NO_PATH
132 | ) {
133 | dispatch(Actions.setIsAnimating(false));
134 | dispatch(Actions.setIsDoneAnimating(true));
135 | }
136 | }, speed * i)
137 | );
138 | }
139 | };
140 |
141 | const handleFindPath = () =>
142 | handleAnimation(
143 | startVertex?.absoluteIndex ? startVertex.absoluteIndex : 0,
144 | endVertex?.absoluteIndex ? endVertex?.absoluteIndex : 0
145 | );
146 |
147 | const handleClearPath = () => {
148 | dispatch(Actions.setIsAnimating(false));
149 | dispatch(Actions.setIsDoneAnimating(false));
150 | dispatch(Actions.clearPath(props.verticesRef));
151 | };
152 | const handleClearObstacles = () => {
153 | dispatch(Actions.onClearWalls(props.verticesRef));
154 | if (obstacleDropdown.heading) {
155 | obstacleDropdown.heading.children[0].innerHTML = obstacles['wall'];
156 | }
157 | };
158 | const handleReset = () => {
159 | dispatch(Actions.setIsAnimating(false));
160 | dispatch(Actions.setIsDoneAnimating(false));
161 | dispatch(Actions.clearPath(props.verticesRef));
162 | handleClearObstacles();
163 | dispatch(
164 | Actions.setStartVertex(
165 | Math.floor(numRows / 2),
166 | Math.floor(numCols / 4),
167 | props.verticesRef
168 | )
169 | );
170 | dispatch(
171 | Actions.setEndVertex(
172 | Math.floor(numRows / 2),
173 | Math.floor(numCols / 4) * 3,
174 | props.verticesRef
175 | )
176 | );
177 | dispatch(Actions.initGraph(props.verticesRef));
178 | dispatch(Actions.clearPath(props.verticesRef));
179 | };
180 |
181 | const handleAlgorithmChanged = (e: MouseEvent) => {
182 | dispatch(
183 | Actions.setCurrentAlgorithm(
184 | pathfindingAlgorithms[
185 | e.currentTarget.getAttribute('value') as string
186 | ]
187 | )
188 | );
189 | };
190 |
191 | const handleObstacleChanged = (e: MouseEvent) => {
192 | const obstacle = e.currentTarget.getAttribute('value') as string;
193 | dispatch(Actions.onSetObstacleRef(obstacle));
194 |
195 | for (let i = 0; i < numRows; i++) {
196 | for (let j = 0; j < numCols; j++) {
197 | let vertex = Position.getVertex(
198 | i,
199 | j,
200 | numRows,
201 | numCols,
202 | props.verticesRef
203 | );
204 |
205 | if (
206 | vertex.absoluteIndex !== startVertex?.absoluteIndex &&
207 | vertex.absoluteIndex !== endVertex?.absoluteIndex
208 | ) {
209 | if (obstacle === 'wall') {
210 | theme.cursorWall(vertex.element);
211 | } else if (obstacle === 'obstacle1') {
212 | theme.cursorObstacle1(vertex.element);
213 | } else if (obstacle === 'obstacle2') {
214 | theme.cursorObstacle2(vertex.element);
215 | } else if (obstacle === 'obstacle3') {
216 | theme.cursorObstacle3(vertex.element);
217 | }
218 | }
219 | }
220 | }
221 | };
222 |
223 | const handleThemeChanged = (e: MouseEvent) => {
224 | const theme = e.currentTarget.getAttribute('value') as string;
225 | dispatch(Actions.setTheme(themes[theme]));
226 | setObstacles(obstacleOptions[theme]);
227 | themes[theme].bodyBackground(
228 | document.querySelector('body') as HTMLBodyElement
229 | );
230 | themes[theme].header(
231 | document.getElementById('Header') as HTMLDivElement
232 | );
233 | themes[theme].heading(
234 | document.getElementById('Heading') as HTMLHeadingElement
235 | );
236 | themes[theme].controller(
237 | document.getElementById('Controller') as HTMLDivElement
238 | );
239 | const buttons = document.querySelectorAll('.' + styles.Button);
240 | for (let i = 0; i < buttons.length; i++) {
241 | themes[theme].button(buttons[i] as HTMLButtonElement);
242 | }
243 | let dropdown: HTMLCollection;
244 | let dropdowns = document.querySelector('.' + styles.Dropdowns);
245 | if (dropdowns) {
246 | dropdown = dropdowns.children;
247 | for (let i = 0; i < dropdown.length; i++) {
248 | themes[theme].dropdown(
249 | dropdown[i].children[1].children[0] as HTMLHeadingElement
250 | );
251 | themes[theme].options(
252 | dropdown[i].children[1].children[1] as HTMLDivElement
253 | );
254 | let options = dropdown[i].children[1].children[1].children;
255 | for (let j = 0; j < options.length; j++) {
256 | themes[theme].option(
257 | options[j].children[0] as HTMLLIElement
258 | );
259 | }
260 | }
261 | }
262 | handleReset();
263 | };
264 |
265 | const handleSpeedChanged = (e: MouseEvent) => {
266 | setSpeed(parseInt(e.currentTarget.getAttribute('value') as string));
267 | };
268 |
269 | const speedOptions = {
270 | 10: 'Very Fast',
271 | 25: 'Fast',
272 | 100: 'Medium',
273 | 500: 'Slow',
274 | 2000: 'Very Slow',
275 | };
276 |
277 | return (
278 |
279 |
280 |
289 |
298 | (obstacleDropdown = o as DropdownRef)}
300 | name='obstacles'
301 | default={obstacles['wall']}
302 | options={obstacles}
303 | onChange={handleObstacleChanged}
304 | classNames={[styles.Dropdown]}
305 | label='Obstacle:'
306 | width='15rem'
307 | >
308 |
317 |
318 |
319 |
320 | Find path
321 |
322 |
323 | Clear path
324 |
325 |
329 | Clear Obstacles
330 |
331 |
332 | Reset
333 |
334 |
335 |
336 | );
337 | };
338 |
339 | export default Controller;
340 |
--------------------------------------------------------------------------------
/src/components/MobileController/MobileController.module.css:
--------------------------------------------------------------------------------
1 | .MobileController {
2 | display: none;
3 | /* position: fixed;
4 | bottom: 5%;
5 | left: 50%;
6 | transform: translateX(-50%); */
7 | justify-content: space-between;
8 | align-items: center;
9 | width: 90%;
10 | margin: 0 auto 3rem;
11 | background-color: rgba(0, 0, 0, 0.2);
12 | border-radius: 0.3rem;
13 | padding: 2rem 1rem;
14 | touch-action: manipulation;
15 | }
16 |
17 | .MobileController button {
18 | border: none;
19 | border-radius: 0.3rem;
20 | padding: 0.7rem 1rem;
21 | background-color: var(--color-1);
22 | color: var(--color-4);
23 | }
24 |
25 | .MobileController button:active {
26 | color: red;
27 | }
28 |
29 | .MobileController button.Selected {
30 | border: 2px solid var(--color-4);
31 | }
32 |
33 | @media (hover: none) {
34 | .MobileController {
35 | display: flex;
36 | }
37 | }
38 |
39 | .StartEndContainer {
40 | display: flex;
41 | justify-content: center;
42 | align-items: center;
43 | flex-basis: 30%;
44 | }
45 |
46 | .StartEnd {
47 | display: flex;
48 | flex-direction: column;
49 | justify-content: center;
50 | align-items: center;
51 | flex-basis: 50%;
52 | }
53 |
54 | .StartEnd button {
55 | margin: 1rem 0;
56 | }
57 |
58 | .DPadContainer {
59 | display: flex;
60 | justify-content: center;
61 | align-items: center;
62 | flex-basis: 30%;
63 | }
64 |
65 | .DPad {
66 | display: flex;
67 | flex-direction: column;
68 | flex-basis: 40%;
69 | }
70 |
71 | .Row_1,
72 | .Row_3 {
73 | display: flex;
74 | justify-content: center;
75 | align-items: center;
76 | }
77 |
78 | .Row_1 {
79 | padding-bottom: 0.5rem;
80 | }
81 |
82 | .Row_2 {
83 | display: flex;
84 | justify-content: space-between;
85 | align-items: center;
86 | }
87 |
88 | .Row_3 {
89 | padding-top: 0.5rem;
90 | }
91 |
92 | .Shake {
93 | animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
94 | transform: translate3d(0, 0, 0);
95 | backface-visibility: hidden;
96 | perspective: 1000px;
97 | }
98 |
99 | @keyframes shake {
100 | 10%,
101 | 90% {
102 | transform: translate3d(-1px, 0, 0);
103 | }
104 |
105 | 20%,
106 | 80% {
107 | transform: translate3d(2px, 0, 0);
108 | }
109 |
110 | 30%,
111 | 50%,
112 | 70% {
113 | transform: translate3d(-4px, 0, 0);
114 | }
115 |
116 | 40%,
117 | 60% {
118 | transform: translate3d(4px, 0, 0);
119 | }
120 | }
121 |
122 | @media only screen and (max-width: 1000px) {
123 | .DPad {
124 | flex-basis: 60%;
125 | }
126 | }
127 |
128 | @media only screen and (max-width: 800px) {
129 | .Row_1 {
130 | padding-bottom: 0.7rem;
131 | }
132 |
133 | .Row_3 {
134 | padding-top: 0.7rem;
135 | }
136 | .DPad {
137 | flex-basis: 75%;
138 | }
139 | }
140 |
141 | @media only screen and (max-width: 700px) {
142 | .DPad {
143 | flex-basis: 85%;
144 | }
145 | }
146 |
147 | @media only screen and (max-width: 600px) {
148 | .DPad {
149 | flex-basis: 95%;
150 | }
151 | }
152 |
153 | @media only screen and (max-width: 500px) {
154 | .DPad {
155 | flex-basis: 100%;
156 | }
157 | .DPadContainer {
158 | flex-basis: 35%;
159 | }
160 | }
161 |
162 | @media only screen and (max-width: 400px) {
163 | .DPadContainer {
164 | flex-basis: 40%;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/components/MobileController/MobileController.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, RefObject } from 'react';
2 |
3 | import styles from './MobileController.module.css';
4 | import {
5 | getNeighbors,
6 | getVertexAbsolute,
7 | Neighbors,
8 | } from '../../utils/position';
9 | import { StoreState } from '../../store/reducers';
10 | import { Vertex } from '../../store/reducers/graph';
11 | import { useSelector, useDispatch } from 'react-redux';
12 | import * as Actions from '../../store/actions';
13 | import { GraphTheme } from '../../utils/themes';
14 |
15 | interface MobileControllerProps {
16 | classNames?: string[];
17 | verticesRef: RefObject;
18 | }
19 |
20 | // FIXME: moves over walls
21 | // FIXME: responsive issues
22 | const MobileController = (props: MobileControllerProps) => {
23 | let classNames = [styles.MobileController];
24 | if (props.classNames) {
25 | classNames.push(...props.classNames);
26 | }
27 |
28 | const startVertex = useSelector(
29 | (state) => state.graph.startVertex
30 | );
31 | const endVertex = useSelector(
32 | (state) => state.graph.endVertex
33 | );
34 | const numRows = useSelector(
35 | (state) => state.graph.numRows
36 | );
37 | const numCols = useSelector(
38 | (state) => state.graph.numCols
39 | );
40 | const theme = useSelector(
41 | (state) => state.graph.theme
42 | );
43 | const isDoneAnimating = useSelector(
44 | (state) => state.drag.isDoneAnimating
45 | );
46 | const obstacleIndices = useSelector(
47 | (state) => state.drag.obstacleIndices
48 | );
49 | const controllerRef = useRef(null);
50 | const startEndRef = useRef(null);
51 | const dispatch = useDispatch();
52 | let start: Element;
53 | let startNeighbors: Neighbors;
54 | let startNeighborsTopVertex: Vertex;
55 | let startNeighborsBottomVertex: Vertex;
56 | let startNeighborsLeftVertex: Vertex;
57 | let startNeighborsRightVertex: Vertex;
58 |
59 | let end: Element;
60 | let endNeighbors: Neighbors;
61 | let endNeighborsTopVertex: Vertex;
62 | let endNeighborsBottomVertex: Vertex;
63 | let endNeighborsLeftVertex: Vertex;
64 | let endNeighborsRightVertex: Vertex;
65 |
66 | if (startEndRef.current) {
67 | start = startEndRef.current.children[0];
68 | end = startEndRef.current.children[1];
69 | startNeighbors = getNeighbors(
70 | startVertex?.absoluteIndex as number,
71 | startVertex?.row as number,
72 | numRows,
73 | numCols
74 | );
75 | endNeighbors = getNeighbors(
76 | endVertex?.absoluteIndex as number,
77 | endVertex?.row as number,
78 | numRows,
79 | numCols
80 | );
81 | if (
82 | startNeighbors.top !== -1 &&
83 | startNeighbors.top < numRows * numCols &&
84 | !obstacleIndices.includes(startNeighbors.top)
85 | ) {
86 | startNeighborsTopVertex = getVertexAbsolute(
87 | startNeighbors.top,
88 | numRows,
89 | numCols,
90 | props.verticesRef
91 | );
92 | }
93 | if (
94 | endNeighbors.top !== -1 &&
95 | endNeighbors.top < numRows * numCols &&
96 | !obstacleIndices.includes(endNeighbors.top)
97 | ) {
98 | endNeighborsTopVertex = getVertexAbsolute(
99 | endNeighbors.top,
100 | numRows,
101 | numCols,
102 | props.verticesRef
103 | );
104 | }
105 | if (
106 | startNeighbors.bottom !== -1 &&
107 | startNeighbors.bottom < numRows * numCols &&
108 | !obstacleIndices.includes(startNeighbors.bottom)
109 | ) {
110 | startNeighborsBottomVertex = getVertexAbsolute(
111 | startNeighbors.bottom,
112 | numRows,
113 | numCols,
114 | props.verticesRef
115 | );
116 | }
117 | if (
118 | endNeighbors.bottom !== -1 &&
119 | endNeighbors.bottom < numRows * numCols &&
120 | !obstacleIndices.includes(endNeighbors.bottom)
121 | ) {
122 | endNeighborsBottomVertex = getVertexAbsolute(
123 | endNeighbors.bottom,
124 | numRows,
125 | numCols,
126 | props.verticesRef
127 | );
128 | }
129 | if (
130 | startNeighbors.left !== -1 &&
131 | startNeighbors.left < numRows * numCols &&
132 | !obstacleIndices.includes(startNeighbors.left)
133 | ) {
134 | startNeighborsLeftVertex = getVertexAbsolute(
135 | startNeighbors.left,
136 | numRows,
137 | numCols,
138 | props.verticesRef
139 | );
140 | }
141 | if (
142 | endNeighbors.left !== -1 &&
143 | endNeighbors.left < numRows * numCols &&
144 | !obstacleIndices.includes(endNeighbors.left)
145 | ) {
146 | endNeighborsLeftVertex = getVertexAbsolute(
147 | endNeighbors.left,
148 | numRows,
149 | numCols,
150 | props.verticesRef
151 | );
152 | }
153 | if (
154 | startNeighbors.right !== -1 &&
155 | startNeighbors.right < numRows * numCols &&
156 | !obstacleIndices.includes(startNeighbors.right)
157 | ) {
158 | startNeighborsRightVertex = getVertexAbsolute(
159 | startNeighbors.right,
160 | numRows,
161 | numCols,
162 | props.verticesRef
163 | );
164 | }
165 | if (
166 | endNeighbors.right !== -1 &&
167 | endNeighbors.right < numRows * numCols &&
168 | !obstacleIndices.includes(endNeighbors.right)
169 | ) {
170 | endNeighborsRightVertex = getVertexAbsolute(
171 | endNeighbors.right,
172 | numRows,
173 | numCols,
174 | props.verticesRef
175 | );
176 | }
177 | }
178 |
179 | const handleStart = () => {
180 | if (startEndRef.current) {
181 | const start = startEndRef.current.children[0];
182 | const end = startEndRef.current.children[1];
183 | if (end.classList.contains(styles.Selected)) {
184 | end.classList.remove(styles.Selected);
185 | start.classList.add(styles.Selected);
186 | }
187 | }
188 | };
189 |
190 | const handleEnd = () => {
191 | if (startEndRef.current) {
192 | const start = startEndRef.current.children[0];
193 | const end = startEndRef.current.children[1];
194 | if (start.classList.contains(styles.Selected)) {
195 | start.classList.remove(styles.Selected);
196 | end.classList.add(styles.Selected);
197 | }
198 | }
199 | };
200 |
201 | const handleMove = (
202 | element: Element,
203 | elementNeighbor: number,
204 | vertex: Vertex
205 | ) => {
206 | if (startEndRef.current && startVertex && endVertex) {
207 | if (
208 | elementNeighbor !== -1 &&
209 | elementNeighbor !== endVertex.absoluteIndex &&
210 | elementNeighbor !== startVertex.absoluteIndex &&
211 | !obstacleIndices.includes(elementNeighbor)
212 | ) {
213 | if (element === start) {
214 | theme.start(vertex.element);
215 | theme.unvisited(startVertex?.element);
216 | dispatch(
217 | Actions.setStartVertex(
218 | vertex.row,
219 | vertex.column,
220 | props.verticesRef
221 | )
222 | );
223 | if (isDoneAnimating) {
224 | dispatch(Actions.clearPath(props.verticesRef));
225 | dispatch(
226 | Actions.recalculatePath(
227 | vertex.absoluteIndex,
228 | endVertex.absoluteIndex,
229 | props.verticesRef
230 | )
231 | );
232 | }
233 | } else if (element === end) {
234 | theme.end(vertex.element);
235 | theme.unvisited(endVertex?.element);
236 | dispatch(
237 | Actions.setEndVertex(
238 | vertex.row,
239 | vertex.column,
240 | props.verticesRef
241 | )
242 | );
243 | if (isDoneAnimating) {
244 | dispatch(Actions.clearPath(props.verticesRef));
245 | dispatch(
246 | Actions.recalculatePath(
247 | startVertex.absoluteIndex,
248 | vertex.absoluteIndex,
249 | props.verticesRef
250 | )
251 | );
252 | }
253 | }
254 | } else if (controllerRef.current) {
255 | controllerRef.current.classList.remove(styles.Shake);
256 | setTimeout(() => {
257 | if (controllerRef.current) {
258 | controllerRef.current.classList.add(styles.Shake);
259 | }
260 | }, 10);
261 | }
262 | }
263 | };
264 |
265 | const handleUp = () => {
266 | if (start.classList.contains(styles.Selected)) {
267 | handleMove(start, startNeighbors.top, startNeighborsTopVertex);
268 | } else if (end.classList.contains(styles.Selected)) {
269 | handleMove(end, endNeighbors.top, endNeighborsTopVertex);
270 | }
271 | };
272 | const handleDown = () => {
273 | if (start.classList.contains(styles.Selected)) {
274 | handleMove(
275 | start,
276 | startNeighbors.bottom,
277 | startNeighborsBottomVertex
278 | );
279 | } else if (end.classList.contains(styles.Selected)) {
280 | handleMove(end, endNeighbors.bottom, endNeighborsBottomVertex);
281 | }
282 | };
283 | const handleLeft = () => {
284 | if (start.classList.contains(styles.Selected)) {
285 | handleMove(start, startNeighbors.left, startNeighborsLeftVertex);
286 | } else if (end.classList.contains(styles.Selected)) {
287 | handleMove(end, endNeighbors.left, endNeighborsLeftVertex);
288 | }
289 | };
290 | const handleRight = () => {
291 | if (start.classList.contains(styles.Selected)) {
292 | handleMove(start, startNeighbors.right, startNeighborsRightVertex);
293 | } else if (end.classList.contains(styles.Selected)) {
294 | handleMove(end, endNeighbors.right, endNeighborsRightVertex);
295 | }
296 | };
297 |
298 | return (
299 |
300 |
301 |
302 |
303 | Start
304 |
305 | End
306 |
307 |
308 |
309 |
310 |
311 | ↑
312 |
313 |
314 | ←
315 | →
316 |
317 |
318 | ↓
319 |
320 |
321 |
322 |
323 | );
324 | };
325 |
326 | export default MobileController;
327 |
--------------------------------------------------------------------------------
/src/components/UI/Dropdown/Dropdown.module.css:
--------------------------------------------------------------------------------
1 | .Dropdown {
2 | display: flex;
3 | align-items: center;
4 | width: 70%;
5 | justify-content: space-between;
6 | }
7 |
8 | @media only screen and (max-width: 700px) {
9 | .Dropdown {
10 | width: 90%;
11 | }
12 | }
13 |
14 | @media only screen and (max-width: 550px) {
15 | .Dropdown {
16 | width: 100%;
17 | }
18 | }
19 |
20 | .Label {
21 | font-size: 1rem;
22 | }
23 |
24 | .Heading {
25 | background-color: var(--color-4);
26 | color: var(--color-3);
27 | border-radius: 0.3rem;
28 | text-align: center;
29 | font-size: 1.3rem;
30 | font-weight: 400;
31 | cursor: pointer;
32 | position: relative;
33 | padding: 0.3rem 0;
34 | }
35 |
36 | .Caret {
37 | position: absolute;
38 | right: 3%;
39 | top: 50%;
40 | font-size: 2.5rem;
41 | transform: translateY(-50%) rotate(90deg);
42 | transform-origin: center;
43 | transition: transform 0.3s;
44 | }
45 |
46 | .CaretRotate {
47 | transform: translateY(-50%) rotate(270deg);
48 | }
49 |
50 | .List {
51 | list-style: none;
52 | padding: 0;
53 | /* margin: 0 0 0 1rem; */
54 | margin: 0;
55 | position: relative;
56 | width: 21rem;
57 | }
58 |
59 | .Options {
60 | position: absolute;
61 | width: 100%;
62 | display: flex;
63 | flex-direction: column;
64 | background-color: var(--color-1);
65 | border-radius: 0.3rem;
66 | padding: 0.3rem;
67 | height: 14rem;
68 | transition: height 0.3s, padding 0.3s;
69 | z-index: 10;
70 | }
71 |
72 | .OptionsHidden {
73 | height: 0;
74 | padding: 0;
75 | }
76 |
77 | .OptionsZIndex {
78 | z-index: -10;
79 | }
80 |
81 | .Option {
82 | cursor: pointer;
83 | padding: 0.3rem 0.5rem;
84 | transition: opacity 0.3s, display 0.3s 0.3s, background-color 0.2s,
85 | color 0.2s;
86 | display: block;
87 | opacity: 1;
88 | }
89 |
90 | .OptionHidden {
91 | opacity: 0;
92 | }
93 |
94 | .Option:hover {
95 | background-color: var(--color-4);
96 | color: var(--color-3);
97 | }
98 |
99 | .Option:first-of-type {
100 | border-top-left-radius: 0.3rem;
101 | border-top-right-radius: 0.3rem;
102 | }
103 |
104 | .Option:last-of-type {
105 | border-bottom-left-radius: 0.3rem;
106 | border-bottom-right-radius: 0.3rem;
107 | }
108 |
--------------------------------------------------------------------------------
/src/components/UI/Dropdown/Dropdown.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | useRef,
3 | useImperativeHandle,
4 | forwardRef,
5 | MouseEvent,
6 | Ref,
7 | useEffect,
8 | } from 'react';
9 |
10 | import styles from './Dropdown.module.css';
11 |
12 | type Option = { [key: string]: string };
13 |
14 | export interface DropdownProps {
15 | name: string;
16 | options: Option;
17 | default: string;
18 | classNames?: string[];
19 | label?: string;
20 | width: string;
21 | onChange: (event: MouseEvent) => void;
22 | }
23 |
24 | export interface DropdownRef {
25 | heading: HTMLHeadingElement | null;
26 | }
27 |
28 | const Dropdown = forwardRef((props: DropdownProps, ref: Ref) => {
29 | let classNames = [styles.Dropdown];
30 | if (props.classNames) {
31 | classNames.push(...props.classNames);
32 | }
33 |
34 | const optionsRef = useRef(null);
35 | const headingRef = useRef(null);
36 |
37 | useImperativeHandle(
38 | ref,
39 | (): DropdownRef => ({
40 | heading: headingRef.current,
41 | })
42 | );
43 |
44 | useEffect(() => {
45 | document.addEventListener('click', (e: any) => {
46 | if (
47 | e.target.parentElement &&
48 | e.target.parentElement.parentElement &&
49 | !e.target.parentElement.parentElement.classList.contains(
50 | styles.Dropdown
51 | ) &&
52 | !e.target.parentElement.parentElement.classList.contains(
53 | styles.List
54 | )
55 | ) {
56 | if (optionsRef.current && headingRef.current) {
57 | optionsRef.current.classList.add(styles.OptionsHidden);
58 | setTimeout(() => {
59 | if (optionsRef.current) {
60 | optionsRef.current.classList.add(
61 | styles.OptionsZIndex
62 | );
63 | }
64 | }, 200);
65 | let options = optionsRef.current.children;
66 | for (let i = 0; i < options.length; i++) {
67 | options[i].children[0].classList.add(
68 | styles.OptionHidden
69 | );
70 | }
71 | headingRef.current.children[1].classList.remove(
72 | styles.CaretRotate
73 | );
74 | }
75 | }
76 | });
77 | }, []);
78 |
79 | let optionsStyles = [
80 | styles.Options,
81 | styles.OptionsHidden,
82 | styles.OptionsZIndex,
83 | ];
84 |
85 | let optionStyles = [styles.Option, styles.OptionHidden];
86 |
87 | const handleDropdownToggle = () => {
88 | if (optionsRef.current && headingRef.current) {
89 | optionsRef.current.classList.toggle(styles.OptionsHidden);
90 | if (optionsRef.current.classList.contains(styles.OptionsZIndex)) {
91 | setTimeout(() => {
92 | if (optionsRef.current)
93 | optionsRef.current.classList.toggle(
94 | styles.OptionsZIndex
95 | );
96 | }, 3);
97 | } else {
98 | setTimeout(() => {
99 | if (optionsRef.current)
100 | optionsRef.current.classList.toggle(
101 | styles.OptionsZIndex
102 | );
103 | }, 100);
104 | }
105 |
106 | let options = optionsRef.current.children;
107 | for (let i = 0; i < options.length; i++) {
108 | options[i].children[0].classList.toggle(styles.OptionHidden);
109 | }
110 | headingRef.current.children[1].classList.toggle(styles.CaretRotate);
111 | }
112 | };
113 |
114 | const handleOptionClicked = (e: MouseEvent) => {
115 | if (headingRef.current) {
116 | props.onChange(e);
117 | headingRef.current.children[0].innerHTML =
118 | e.currentTarget.innerHTML;
119 | headingRef.current.click();
120 | }
121 | };
122 |
123 | let options = Object.keys(props.options).map((option) => {
124 | return (
125 |
126 |
132 | {props.options[option]}
133 |
134 |
135 | );
136 | });
137 |
138 | return (
139 |
140 |
{props.label && props.label}
141 |
142 |
147 | {props.default}
148 | ‣
149 |
150 |
155 | {options}
156 |
157 |
158 |
159 | );
160 | });
161 |
162 | export default Dropdown;
163 |
--------------------------------------------------------------------------------
/src/components/UI/Navbar/Navbar.module.css:
--------------------------------------------------------------------------------
1 | .Navbar {
2 | width: 100%;
3 | background-color: var(--color-1);
4 | color: var(--color-4);
5 | display: flex;
6 | align-items: center;
7 | justify-content: space-between;
8 | }
9 |
10 | .Heading {
11 | width: 100%;
12 | text-align: center;
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/UI/Navbar/Navbar.tsx:
--------------------------------------------------------------------------------
1 | import React, { RefObject } from 'react';
2 | import styles from './Navbar.module.css';
3 |
4 | export interface NavbarProps {
5 | classNames?: string[];
6 | verticesRef: RefObject;
7 | }
8 |
9 | const Navbar = (props: NavbarProps) => {
10 | let classNames = [styles.Navbar];
11 | if (props.classNames) {
12 | classNames.push(...props.classNames);
13 | }
14 |
15 | return (
16 |
21 | );
22 | };
23 |
24 | export default Navbar;
25 |
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/4913:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/containers/PathfindingVisualizer/4913
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/PathfindingVisualizer.module.css:
--------------------------------------------------------------------------------
1 | .PathfindingVisualizer {
2 | /* display: flex;
3 | flex-direction: column;
4 | justify-content: space-between;
5 | align-items: center; */
6 | min-height: 100vh;
7 | }
8 |
9 | .buttons {
10 | display: flex;
11 | width: 100%;
12 | justify-content: center;
13 | }
14 |
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/PathfindingVisualizer.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useLayoutEffect, useEffect } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 |
4 | import Vertices from './Vertices/Vertices';
5 | import Navbar from '../../components/UI/Navbar/Navbar';
6 | import Controller from '../../components/Controller/Controller';
7 | import MobileController from '../../components/MobileController/MobileController';
8 |
9 | import * as Actions from '../../store/actions';
10 |
11 | import styles from './PathfindingVisualizer.module.css';
12 | import { StoreState } from '../../store/reducers';
13 |
14 | interface PathfindingVisualizerProps {}
15 |
16 | const PathfindingVisualizer = (props: PathfindingVisualizerProps) => {
17 | const dispatch = useDispatch();
18 | const numRows = useSelector(
19 | (state) => state.graph.numRows
20 | );
21 | const numCols = useSelector(
22 | (state) => state.graph.numCols
23 | );
24 | const dragWallIndices = useSelector(
25 | (state) => state.drag.wallIndices
26 | );
27 |
28 | const verticesRef = useRef(null);
29 |
30 | useLayoutEffect(() => {
31 | dispatch(Actions.setVerticesRef(verticesRef));
32 | dispatch(Actions.setObstacleRef(dragWallIndices));
33 | }, [dispatch, dragWallIndices]);
34 |
35 | useLayoutEffect(() => {
36 | dispatch(
37 | Actions.setStartVertex(
38 | Math.floor(numRows / 2),
39 | Math.floor(numCols / 4),
40 | verticesRef
41 | )
42 | );
43 | dispatch(
44 | Actions.setEndVertex(
45 | Math.floor(numRows / 2),
46 | Math.floor(numCols / 4) * 3,
47 | verticesRef
48 | )
49 | );
50 | }, [dispatch, verticesRef, numRows, numCols]);
51 |
52 | useEffect(() => {
53 | dispatch(Actions.initGraph(verticesRef));
54 | dispatch(Actions.clearPath(verticesRef));
55 | dispatch(Actions.setIsDoneAnimating(false));
56 | dispatch(Actions.onClearWalls(verticesRef));
57 | }, [dispatch, numRows, numCols]);
58 |
59 | // const handleTest = () => {
60 | // console.log(dragWallIndices, graphWallIndices);
61 | // console.log(dragObstacle1Indices, graphObstacle1Indices);
62 | // console.log(dragObstacle2Indices, graphObstacle2Indices);
63 | // console.log(dragObstacle3Indices, graphObstacle3Indices);
64 | // };
65 |
66 | return (
67 |
68 |
69 |
70 |
71 |
72 | {/*
73 | Test
74 |
*/}
75 |
76 | );
77 | };
78 |
79 | export default PathfindingVisualizer;
80 |
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/Vertices/Vertex/Vertex.module.css:
--------------------------------------------------------------------------------
1 | .Vertex {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | width: 100%;
6 | height: 100%;
7 | font-size: 50%;
8 | cursor: default;
9 | /* border-left: 0.1px solid var(--color-2); */
10 | }
11 |
12 | .Vertex:last-of-type {
13 | /* border-right: 0.1px solid var(--color-2); */
14 | }
15 |
16 | .Vertex > * {
17 | z-index: -10;
18 | }
19 |
20 | .Vertex p {
21 | text-align: center;
22 | font-weight: bold;
23 | font-size: large;
24 | margin: 0;
25 | padding: 0;
26 | display: flex;
27 | justify-content: center;
28 | align-items: center;
29 | }
30 |
31 | .Start:hover {
32 | cursor: grab;
33 | }
34 |
35 | .Vertex img {
36 | width: 100%;
37 | }
38 |
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/Vertices/Vertex/Vertex.tsx:
--------------------------------------------------------------------------------
1 | import React, { RefObject } from 'react';
2 | import { useDispatch } from 'react-redux';
3 | import * as actions from '../../../../store/actions';
4 |
5 | import styles from './Vertex.module.css';
6 |
7 | interface VertexRef {
8 | verticesRef: RefObject;
9 | absoluteIndex: number;
10 | }
11 |
12 | const Vertex = (props: VertexRef) => {
13 | const dispatch = useDispatch();
14 |
15 | return (
16 | {
19 | e.preventDefault();
20 | dispatch(actions.onMouseDown(e));
21 | }}
22 | onMouseOver={(e: any) => {
23 | e.preventDefault();
24 | dispatch(actions.onMouseOver(e, props.verticesRef));
25 | }}
26 | onMouseOut={(e: any) => {
27 | e.preventDefault();
28 | dispatch(actions.onMouseOut(e, props.verticesRef));
29 | }}
30 | onMouseUp={(e: any) => {
31 | e.preventDefault();
32 | dispatch(actions.onMouseUp(e, props.verticesRef));
33 | }}
34 | >
35 |
•
36 | {/*
{props.absoluteIndex} */}
37 |
38 | );
39 | };
40 |
41 | export default Vertex;
42 |
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/Vertices/VertexRow/VertexRow.module.css:
--------------------------------------------------------------------------------
1 | .VertexRow {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | /* border-top: 0.1px solid var(--color-2); */
6 | width: 100%;
7 | height: 10%;
8 | }
9 |
10 | .VertexRow:last-of-type {
11 | /* border-bottom: 0.1px solid var(--color-2); */
12 | }
13 |
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/Vertices/VertexRow/VertexRow.tsx:
--------------------------------------------------------------------------------
1 | import React, { RefObject } from 'react';
2 |
3 | import Vertex from '../Vertex/Vertex';
4 |
5 | import styles from './VertexRow.module.css';
6 | import * as Position from '../../../../utils/position';
7 | import { StoreState } from '../../../../store/reducers';
8 | import { useSelector } from 'react-redux';
9 |
10 | interface VertexRowProps {
11 | rowNumber: number;
12 | columns: number;
13 | verticesRef: RefObject;
14 | }
15 |
16 | const VertexRow = (props: VertexRowProps) => {
17 | let vertexRow: JSX.Element[] = [];
18 | const numRows = useSelector(
19 | (state) => state.graph.numRows
20 | );
21 | const numCols = useSelector(
22 | (state) => state.graph.numCols
23 | );
24 |
25 | for (let i = 0; i < props.columns; i++) {
26 | let absoluteIndex = Position.indexToAbsolute(
27 | props.rowNumber,
28 | i,
29 | numRows,
30 | numCols
31 | );
32 | vertexRow.push(
33 |
38 | );
39 | }
40 |
41 | return {vertexRow}
;
42 | };
43 |
44 | export default VertexRow;
45 |
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/Vertices/Vertices.module.css:
--------------------------------------------------------------------------------
1 | .Vertices {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | align-items: center;
6 | width: 90%;
7 | margin: 0 auto;
8 | padding: 0 0 1rem;
9 | height: 90vh;
10 | }
11 |
--------------------------------------------------------------------------------
/src/containers/PathfindingVisualizer/Vertices/Vertices.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useImperativeHandle, forwardRef } from 'react';
2 | import { useSelector, useDispatch } from 'react-redux';
3 | import { StoreState } from '../../../store/reducers';
4 | import * as Actions from '../../../store/actions';
5 |
6 | import styles from './Vertices.module.css';
7 |
8 | import VertexRow from './VertexRow/VertexRow';
9 | import { Vertex } from '../../../store/reducers/graph';
10 |
11 | interface VerticesProps {}
12 |
13 | export interface VerticesRef {
14 | vertices: HTMLDivElement | null;
15 | children: HTMLCollection | null;
16 | }
17 |
18 | const Vertices = (props: VerticesProps, ref: any) => {
19 | const dispatch = useDispatch();
20 | const numRows = useSelector(
21 | (state) => state.graph.numRows
22 | );
23 | const numCols = useSelector(
24 | (state) => state.graph.numCols
25 | );
26 | const startVertex = useSelector(
27 | (state) => state.graph.startVertex
28 | );
29 | const endVertex = useSelector(
30 | (state) => state.graph.endVertex
31 | );
32 | const wallIndices = useSelector(
33 | (state) => state.drag.wallIndices
34 | );
35 | const isStartMouseDown = useSelector(
36 | (state) => state.drag.isStartMouseDown
37 | );
38 | const isEndMouseDown = useSelector(
39 | (state) => state.drag.isEndMouseDown
40 | );
41 | const isMouseDown = useSelector(
42 | (state) => state.drag.isMouseDown
43 | );
44 | let verticesRef = useRef(null);
45 |
46 | useImperativeHandle(
47 | ref,
48 | (): VerticesRef => ({
49 | vertices: verticesRef.current,
50 | children: verticesRef.current ? verticesRef.current.children : null,
51 | })
52 | );
53 |
54 | let vertices: JSX.Element[] = [];
55 |
56 | for (let i = 0; i < numRows; i++) {
57 | vertices.push(
58 |
64 | );
65 | }
66 |
67 | const handleMouseLeave = (e: any) => {
68 | if (
69 | startVertex &&
70 | endVertex &&
71 | (isStartMouseDown || isEndMouseDown || isMouseDown)
72 | ) {
73 | dispatch(Actions.mouseUp(e));
74 | dispatch(
75 | Actions.setStartVertex(
76 | startVertex?.row,
77 | startVertex?.column,
78 | verticesRef
79 | )
80 | );
81 | dispatch(
82 | Actions.setEndVertex(
83 | endVertex?.row,
84 | endVertex?.column,
85 | verticesRef
86 | )
87 | );
88 | dispatch(
89 | Actions.clearPath(
90 | verticesRef,
91 | false,
92 | startVertex.absoluteIndex,
93 | endVertex.absoluteIndex
94 | )
95 | );
96 | dispatch(Actions.onSetWallIndices(wallIndices, verticesRef));
97 | }
98 | };
99 |
100 | return (
101 |
106 | {vertices}
107 |
108 | );
109 | };
110 |
111 | export default forwardRef(Vertices);
112 |
--------------------------------------------------------------------------------
/src/fonts/Avengers/Avengers.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Avengers/Avengers.ttf
--------------------------------------------------------------------------------
/src/fonts/Lakers/lakers.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Lakers/lakers.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-Black.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-BlackItalic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-Bold.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-BoldItalic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-ExtraBold.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-ExtraLight.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-Italic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-Light.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-LightItalic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-Medium.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-MediumItalic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-Regular.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-SemiBold.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-Thin.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/Montserrat-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Montserrat/Montserrat-ThinItalic.ttf
--------------------------------------------------------------------------------
/src/fonts/Montserrat/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright 2011 The Montserrat Project Authors (https://github.com/JulietaUla/Montserrat)
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/src/fonts/OnePiece/OnePiece.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/OnePiece/OnePiece.ttf
--------------------------------------------------------------------------------
/src/fonts/Pokemon/PokemonHollow.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Pokemon/PokemonHollow.ttf
--------------------------------------------------------------------------------
/src/fonts/Pokemon/PokemonSolid.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/Pokemon/PokemonSolid.ttf
--------------------------------------------------------------------------------
/src/fonts/TheOffice/TheOffice.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/fonts/TheOffice/TheOffice.ttf
--------------------------------------------------------------------------------
/src/fonts/TheOffice/readme.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | American Typewriter RegularFontsgeek
8 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
--------------------------------------------------------------------------------
/src/fonts/avengers.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Avengers';
3 | font-style: normal;
4 | font-weight: 700;
5 | font-display: swap;
6 | src: url(./Avengers/Avengers.ttf);
7 | }
8 |
--------------------------------------------------------------------------------
/src/fonts/lakers.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Lakers';
3 | font-style: normal;
4 | font-weight: 700;
5 | font-display: swap;
6 | src: url(./Lakers//lakers.ttf);
7 | }
8 |
--------------------------------------------------------------------------------
/src/fonts/montserrat.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Montserrat';
3 | font-style: normal;
4 | font-weight: 400;
5 | font-display: swap;
6 | src: url(./Montserrat/Montserrat-Regular.ttf);
7 | }
8 |
9 | @font-face {
10 | font-family: 'Montserrat';
11 | font-style: bold;
12 | font-weight: 700;
13 | font-display: swap;
14 | src: url(./Montserrat/Montserrat-Bold.ttf);
15 | }
16 |
17 | @font-face {
18 | font-family: 'Montserrat';
19 | font-style: lighter;
20 | font-weight: 300;
21 | font-display: swap;
22 | src: url(./Montserrat/Montserrat-Light.ttf);
23 | }
24 |
--------------------------------------------------------------------------------
/src/fonts/onePiece.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'One Piece';
3 | font-style: normal;
4 | font-weight: 700;
5 | font-display: swap;
6 | src: url(./OnePiece/OnePiece.ttf);
7 | }
8 |
--------------------------------------------------------------------------------
/src/fonts/pokemon.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Pokemon';
3 | font-style: normal;
4 | font-weight: 700;
5 | font-display: swap;
6 | src: url(./Pokemon/PokemonSolid.ttf);
7 | }
8 |
--------------------------------------------------------------------------------
/src/fonts/theOffice.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'The Office';
3 | font-style: normal;
4 | font-weight: 700;
5 | font-display: swap;
6 | src: url(./TheOffice/TheOffice.ttf);
7 | }
8 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url(./fonts/montserrat.css);
2 | @import url(./fonts/avengers.css);
3 | @import url(./fonts/onePiece.css);
4 | @import url(./fonts/lakers.css);
5 | @import url(./fonts/pokemon.css);
6 | @import url(./fonts/theOffice.css);
7 |
8 | :root {
9 | --color-1: #292f36;
10 | --color-2: #4ecdc4;
11 | --color-3: #ffffff;
12 | --color-4: #ff6b6b;
13 | }
14 |
15 | *,
16 | *::after,
17 | *::before {
18 | font-family: inherit;
19 | box-sizing: border-box;
20 | }
21 |
22 | html {
23 | font-size: 100%;
24 | }
25 |
26 | @media only screen and (max-width: 508px) {
27 | html {
28 | font-size: 80%;
29 | }
30 | }
31 |
32 | @media only screen and (max-width: 406px) {
33 | html {
34 | font-size: 70%;
35 | }
36 | }
37 |
38 | @media only screen and (max-width: 355px) {
39 | html {
40 | font-size: 60%;
41 | }
42 | }
43 |
44 | @media only screen and (max-width: 304px) {
45 | html {
46 | font-size: 50%;
47 | }
48 | }
49 |
50 | body {
51 | margin: 0;
52 | min-height: 100vh;
53 | background-color: var(--color-3);
54 | font-family: 'Montserrat', sans-serif;
55 | }
56 |
57 | ::selection {
58 | background-color: var(--color-4);
59 | color: var(--color-3);
60 | }
61 |
62 | button,
63 | select,
64 | option {
65 | outline: none;
66 | cursor: pointer;
67 | }
68 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | import { createStore, applyMiddleware } from 'redux';
8 | import { Provider } from 'react-redux';
9 | import thunk from 'redux-thunk';
10 | import { reducers } from './store/reducers';
11 |
12 | export const store = createStore(reducers, applyMiddleware(thunk));
13 |
14 | ReactDOM.render(
15 |
16 |
17 |
18 |
19 | ,
20 | document.getElementById('root')
21 | );
22 |
23 | // If you want your app to work offline and load faster, you can change
24 | // unregister() to register() below. Note this comes with some pitfalls.
25 | // Learn more about service workers: https://bit.ly/CRA-PWA
26 | serviceWorker.unregister();
27 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/serviceWorker.ts:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | type Config = {
24 | onSuccess?: (registration: ServiceWorkerRegistration) => void;
25 | onUpdate?: (registration: ServiceWorkerRegistration) => void;
26 | };
27 |
28 | export function register(config?: Config) {
29 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
30 | // The URL constructor is available in all browsers that support SW.
31 | const publicUrl = new URL(
32 | process.env.PUBLIC_URL,
33 | window.location.href
34 | );
35 | if (publicUrl.origin !== window.location.origin) {
36 | // Our service worker won't work if PUBLIC_URL is on a different origin
37 | // from what our page is served on. This might happen if a CDN is used to
38 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
39 | return;
40 | }
41 |
42 | window.addEventListener('load', () => {
43 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
44 |
45 | if (isLocalhost) {
46 | // This is running on localhost. Let's check if a service worker still exists or not.
47 | checkValidServiceWorker(swUrl, config);
48 |
49 | // Add some additional logging to localhost, pointing developers to the
50 | // service worker/PWA documentation.
51 | navigator.serviceWorker.ready.then(() => {
52 | console.log(
53 | 'This web app is being served cache-first by a service ' +
54 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
55 | );
56 | });
57 | } else {
58 | // Is not localhost. Just register service worker
59 | registerValidSW(swUrl, config);
60 | }
61 | });
62 | }
63 | }
64 |
65 | function registerValidSW(swUrl: string, config?: Config) {
66 | navigator.serviceWorker
67 | .register(swUrl)
68 | .then(registration => {
69 | registration.onupdatefound = () => {
70 | const installingWorker = registration.installing;
71 | if (installingWorker == null) {
72 | return;
73 | }
74 | installingWorker.onstatechange = () => {
75 | if (installingWorker.state === 'installed') {
76 | if (navigator.serviceWorker.controller) {
77 | // At this point, the updated precached content has been fetched,
78 | // but the previous service worker will still serve the older
79 | // content until all client tabs are closed.
80 | console.log(
81 | 'New content is available and will be used when all ' +
82 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
83 | );
84 |
85 | // Execute callback
86 | if (config && config.onUpdate) {
87 | config.onUpdate(registration);
88 | }
89 | } else {
90 | // At this point, everything has been precached.
91 | // It's the perfect time to display a
92 | // "Content is cached for offline use." message.
93 | console.log('Content is cached for offline use.');
94 |
95 | // Execute callback
96 | if (config && config.onSuccess) {
97 | config.onSuccess(registration);
98 | }
99 | }
100 | }
101 | };
102 | };
103 | })
104 | .catch(error => {
105 | console.error('Error during service worker registration:', error);
106 | });
107 | }
108 |
109 | function checkValidServiceWorker(swUrl: string, config?: Config) {
110 | // Check if the service worker can be found. If it can't reload the page.
111 | fetch(swUrl, {
112 | headers: { 'Service-Worker': 'script' }
113 | })
114 | .then(response => {
115 | // Ensure service worker exists, and that we really are getting a JS file.
116 | const contentType = response.headers.get('content-type');
117 | if (
118 | response.status === 404 ||
119 | (contentType != null && contentType.indexOf('javascript') === -1)
120 | ) {
121 | // No service worker found. Probably a different app. Reload the page.
122 | navigator.serviceWorker.ready.then(registration => {
123 | registration.unregister().then(() => {
124 | window.location.reload();
125 | });
126 | });
127 | } else {
128 | // Service worker found. Proceed as normal.
129 | registerValidSW(swUrl, config);
130 | }
131 | })
132 | .catch(() => {
133 | console.log(
134 | 'No internet connection found. App is running in offline mode.'
135 | );
136 | });
137 | }
138 |
139 | export function unregister() {
140 | if ('serviceWorker' in navigator) {
141 | navigator.serviceWorker.ready
142 | .then(registration => {
143 | registration.unregister();
144 | })
145 | .catch(error => {
146 | console.error(error.message);
147 | });
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/src/store/actions/drag.ts:
--------------------------------------------------------------------------------
1 | import { RefObject, Dispatch } from 'react';
2 | import { ActionTypes } from './types';
3 | import * as actions from '../actions';
4 | import * as Position from '../../utils/position';
5 | import { Vertex } from '../reducers/graph';
6 | import { GraphTheme } from '../../utils/themes';
7 | import { indexToAbsolute } from '../../utils/position';
8 |
9 | export const mouseDown = (
10 | e: any,
11 | isAnimating: boolean,
12 | startVertex: Vertex,
13 | endVertex: Vertex,
14 | theme: GraphTheme
15 | ) => {
16 | return {
17 | type: ActionTypes.MOUSE_DOWN,
18 | e,
19 | isAnimating,
20 | startVertex,
21 | endVertex,
22 | theme,
23 | };
24 | };
25 |
26 | export const onMouseDown = (e: any) => {
27 | return (dispatch: Dispatch, getState: any) => {
28 | let graph = getState().graph;
29 | let isAnimating = graph.isAnimating;
30 | let startVertex = graph.startVertex;
31 | let endVertex = graph.endVertex;
32 | let theme = graph.theme;
33 |
34 | dispatch(mouseDown(e, isAnimating, startVertex, endVertex, theme));
35 | };
36 | };
37 |
38 | export const mouseOver = (
39 | e: any,
40 | verticesRef: RefObject,
41 | startVertex: Vertex,
42 | endVertex: Vertex,
43 | numRows: number,
44 | numCols: number,
45 | startNeighbors: number[],
46 | startNeighborsVertices: Vertex[],
47 | endNeighbors: number[],
48 | endNeighborsVertices: Vertex[],
49 | obstacleNeighbors: number[],
50 | obstacleNeighborsVertices: Vertex[],
51 | theme: GraphTheme
52 | ) => {
53 | return {
54 | type: ActionTypes.MOUSE_OVER,
55 | e,
56 | verticesRef,
57 | startVertex,
58 | endVertex,
59 | numRows,
60 | numCols,
61 | startNeighbors,
62 | startNeighborsVertices,
63 | endNeighbors,
64 | endNeighborsVertices,
65 | obstacleNeighbors,
66 | obstacleNeighborsVertices,
67 | theme,
68 | };
69 | };
70 |
71 | export const onMouseOver = (e: any, verticesRef: RefObject) => {
72 | return (dispatch: Dispatch, getState: any) => {
73 | let graph = getState().graph;
74 | let drag = getState().drag;
75 | let vertex = e.target;
76 | let startVertex = graph.startVertex;
77 | let endVertex = graph.endVertex;
78 | let numRows = graph.numRows;
79 | let numCols = graph.numCols;
80 | let theme = graph.theme;
81 | let wallIndices = drag.wallIndices;
82 | let obstacle1Indices = drag.obstacle1Indices;
83 | let obstacle2Indices = drag.obstacle2Indices;
84 | let obstacle3Indices = drag.obstacle3Indices;
85 | let obstacleIndices = [
86 | ...wallIndices,
87 | ...obstacle1Indices,
88 | ...obstacle2Indices,
89 | obstacle3Indices,
90 | ];
91 | let startNeighbors = getNeighbors(
92 | startVertex.absoluteIndex,
93 | startVertex.row,
94 | numRows,
95 | numCols
96 | );
97 | let endNeighbors = getNeighbors(
98 | endVertex.absoluteIndex,
99 | endVertex.row,
100 | numRows,
101 | numCols
102 | );
103 | let obstacleNeighbors: number[] = [];
104 | for (let obstacleIndex of obstacleIndices) {
105 | let obstacleRow = Position.absoluteToIndex(
106 | obstacleIndex,
107 | numRows,
108 | numCols
109 | ).row;
110 | obstacleNeighbors.push(
111 | ...getNeighbors(obstacleIndex, obstacleRow, numRows, numCols)
112 | );
113 | }
114 | let startNeighborsVertices: Vertex[] = getNeighborVertices(
115 | startNeighbors,
116 | numRows,
117 | numCols,
118 | verticesRef
119 | );
120 | let endNeighborsVertices: Vertex[] = getNeighborVertices(
121 | endNeighbors,
122 | numRows,
123 | numCols,
124 | verticesRef
125 | );
126 | let obstacleNeighborsVertices: Vertex[] = getNeighborVertices(
127 | obstacleNeighbors,
128 | numRows,
129 | numCols,
130 | verticesRef
131 | );
132 |
133 | dispatch(
134 | mouseOver(
135 | e,
136 | verticesRef,
137 | startVertex,
138 | endVertex,
139 | numRows,
140 | numCols,
141 | startNeighbors,
142 | startNeighborsVertices,
143 | endNeighbors,
144 | endNeighborsVertices,
145 | obstacleNeighbors,
146 | obstacleNeighborsVertices,
147 | theme
148 | )
149 | );
150 |
151 | let vertexIndex = parseInt(vertex.getAttribute('absoluteIndex'));
152 | let overEndRow = drag.overEndRow;
153 | let overEndCol = drag.overEndCol;
154 | let overStartRow = drag.overStartRow;
155 | let overStartCol = drag.overStartCol;
156 | let overObstacleRow = drag.overObstacleRow;
157 | let overObstacleCol = drag.overObstacleCol;
158 |
159 | let overEndIndex = indexToAbsolute(
160 | overEndRow,
161 | overEndCol,
162 | numRows,
163 | numCols
164 | );
165 | let overStartIndex = indexToAbsolute(
166 | overStartRow,
167 | overStartCol,
168 | numRows,
169 | numCols
170 | );
171 | let overObstacleIndex = indexToAbsolute(
172 | overObstacleRow,
173 | overObstacleCol,
174 | numRows,
175 | numCols
176 | );
177 |
178 | if (drag.isStartMouseDown && drag.isDoneAnimating) {
179 | dispatch(
180 | actions.onRecalculatePath(
181 | vertexIndex,
182 | endVertex.absoluteIndex,
183 | verticesRef,
184 | overEndIndex,
185 | overStartIndex,
186 | overObstacleIndex
187 | )
188 | );
189 | } else if (drag.isEndMouseDown && drag.isDoneAnimating) {
190 | dispatch(
191 | actions.onRecalculatePath(
192 | startVertex.absoluteIndex,
193 | vertexIndex,
194 | verticesRef,
195 | overEndIndex,
196 | overStartIndex,
197 | overObstacleIndex
198 | )
199 | );
200 | }
201 | };
202 | };
203 |
204 | export const mouseOut = (
205 | e: any,
206 | verticesRef: RefObject,
207 | startVertex: Vertex,
208 | endVertex: Vertex,
209 | numRows: number,
210 | numCols: number,
211 | startNeighbors: number[],
212 | startNeighborsVertices: Vertex[],
213 | endNeighbors: number[],
214 | endNeighborsVertices: Vertex[],
215 | obstacleNeighbors: number[],
216 | obstacleNeighborsVertices: Vertex[],
217 | theme: GraphTheme
218 | ) => {
219 | return {
220 | type: ActionTypes.MOUSE_OUT,
221 | e,
222 | verticesRef,
223 | startVertex,
224 | endVertex,
225 | numRows,
226 | numCols,
227 | startNeighbors,
228 | startNeighborsVertices,
229 | endNeighbors,
230 | endNeighborsVertices,
231 | obstacleNeighbors,
232 | obstacleNeighborsVertices,
233 | theme,
234 | };
235 | };
236 |
237 | export const onMouseOut = (e: any, verticesRef: RefObject) => {
238 | return (dispatch: Dispatch, getState: any) => {
239 | let graph = getState().graph;
240 | let drag = getState().drag;
241 | let startVertex = graph.startVertex;
242 | let endVertex = graph.endVertex;
243 | let numRows = graph.numRows;
244 | let numCols = graph.numCols;
245 | let verticesRef = graph.verticesRef;
246 | let theme = graph.theme;
247 | let wallIndices = drag.wallIndices;
248 | let obstacle1Indices = drag.obstacle1Indices;
249 | let obstacle2Indices = drag.obstacle2Indices;
250 | let obstacle3Indices = drag.obstacle3Indices;
251 | let obstacleIndices = [
252 | ...wallIndices,
253 | ...obstacle1Indices,
254 | ...obstacle2Indices,
255 | ...obstacle3Indices,
256 | ];
257 | let startNeighbors = getNeighbors(
258 | startVertex.absoluteIndex,
259 | startVertex.row,
260 | numRows,
261 | numCols
262 | );
263 | let endNeighbors = getNeighbors(
264 | endVertex.absoluteIndex,
265 | endVertex.row,
266 | numRows,
267 | numCols
268 | );
269 | let obstacleNeighbors: number[] = [];
270 | for (let obstacleIndex of obstacleIndices) {
271 | let obstacleRow = Position.absoluteToIndex(
272 | obstacleIndex,
273 | numRows,
274 | numCols
275 | ).row;
276 | obstacleNeighbors.push(
277 | ...getNeighbors(obstacleIndex, obstacleRow, numRows, numCols)
278 | );
279 | }
280 | let startNeighborsVertices: Vertex[] = getNeighborVertices(
281 | startNeighbors,
282 | numRows,
283 | numCols,
284 | verticesRef
285 | );
286 | let endNeighborsVertices: Vertex[] = getNeighborVertices(
287 | endNeighbors,
288 | numRows,
289 | numCols,
290 | verticesRef
291 | );
292 | let obstacleNeighborsVertices: Vertex[] = getNeighborVertices(
293 | obstacleNeighbors,
294 | numRows,
295 | numCols,
296 | verticesRef
297 | );
298 |
299 | dispatch(
300 | mouseOut(
301 | e,
302 | verticesRef,
303 | startVertex,
304 | endVertex,
305 | numRows,
306 | numCols,
307 | startNeighbors,
308 | startNeighborsVertices,
309 | endNeighbors,
310 | endNeighborsVertices,
311 | obstacleNeighbors,
312 | obstacleNeighborsVertices,
313 | theme
314 | )
315 | );
316 | };
317 | };
318 |
319 | const getNeighbors = (
320 | absoluteIndex: number,
321 | row: number,
322 | numRows: number,
323 | numCols: number
324 | ): number[] => {
325 | return Object.values(
326 | Position.getNeighbors(absoluteIndex, row, numRows, numCols)
327 | );
328 | };
329 |
330 | const getNeighborVertices = (
331 | neighbors: number[],
332 | numRows: number,
333 | numCols: number,
334 | verticesRef: RefObject
335 | ): Vertex[] => {
336 | let neighborVertices: Vertex[] = [];
337 |
338 | for (let neighbor of neighbors) {
339 | if (neighbor >= 0 && neighbor < numRows * numCols)
340 | neighborVertices.push(
341 | Position.getVertexAbsolute(
342 | neighbor,
343 | numRows,
344 | numCols,
345 | verticesRef
346 | )
347 | );
348 | }
349 |
350 | return neighborVertices;
351 | };
352 |
353 | export const mouseUp = (e: any) => {
354 | return {
355 | type: ActionTypes.MOUSE_UP,
356 | e,
357 | };
358 | };
359 |
360 | export const onMouseUp = (e: any, verticesRef: RefObject) => {
361 | return (dispatch: Dispatch, getState: any) => {
362 | let vertex = e.target;
363 | let vertexRow = parseInt(vertex.getAttribute('row'));
364 | let vertexCol = parseInt(vertex.getAttribute('column'));
365 | let drag = getState().drag;
366 |
367 | if (drag.isStartMouseDown) {
368 | if (drag.isOverEnd) {
369 | dispatch(
370 | actions.setStartVertex(
371 | drag.overEndRow,
372 | drag.overEndCol,
373 | verticesRef
374 | )
375 | );
376 | } else if (drag.isOverObstacle) {
377 | dispatch(
378 | actions.setStartVertex(
379 | drag.overObstacleRow,
380 | drag.overObstacleCol,
381 | verticesRef
382 | )
383 | );
384 | } else {
385 | dispatch(
386 | actions.setStartVertex(vertexRow, vertexCol, verticesRef)
387 | );
388 | }
389 | } else if (drag.isEndMouseDown) {
390 | if (drag.isOverStart) {
391 | dispatch(
392 | actions.setEndVertex(
393 | drag.overStartRow,
394 | drag.overStartCol,
395 | verticesRef
396 | )
397 | );
398 | } else if (drag.isOverObstacle) {
399 | dispatch(
400 | actions.setEndVertex(
401 | drag.overObstacleRow,
402 | drag.overObstacleCol,
403 | verticesRef
404 | )
405 | );
406 | } else {
407 | dispatch(
408 | actions.setEndVertex(vertexRow, vertexCol, verticesRef)
409 | );
410 | }
411 | } else if (drag.isMouseDown) {
412 | dispatch(actions.onSetWallIndices(drag.wallIndices, verticesRef));
413 | dispatch(
414 | actions.onSetObstacle1Indices(
415 | drag.obstacle1Indices,
416 | verticesRef
417 | )
418 | );
419 | dispatch(
420 | actions.onSetObstacle2Indices(
421 | drag.obstacle2Indices,
422 | verticesRef
423 | )
424 | );
425 | dispatch(
426 | actions.onSetObstacle3Indices(
427 | drag.obstacle3Indices,
428 | verticesRef
429 | )
430 | );
431 | }
432 |
433 | dispatch(mouseUp(e));
434 | };
435 | };
436 |
437 | export const clearDragWalls = () => {
438 | return {
439 | type: ActionTypes.CLEAR_DRAG_WALLS,
440 | };
441 | };
442 |
443 | export const setIsDoneAnimating = (isDoneAnimating: boolean) => {
444 | return {
445 | type: ActionTypes.SET_IS_DONE_ANIMATING,
446 | isDoneAnimating,
447 | };
448 | };
449 |
450 | export const setIsRecalculating = (isRecalculating: boolean) => {
451 | return {
452 | type: ActionTypes.SET_IS_RECALCULATING,
453 | isRecalculating,
454 | };
455 | };
456 |
457 | export const setObstacleRef = (obstacleRef: number[]) => {
458 | return {
459 | type: ActionTypes.SET_OBSTACLE_REF,
460 | obstacleRef,
461 | };
462 | };
463 |
464 | export const onSetObstacleRef = (obstacleRef: string) => {
465 | return (dispatch: Dispatch, getState: any) => {
466 | const drag = getState().drag;
467 | const wallIndices = drag.wallIndices;
468 | const obstacle1Indices = drag.obstacle1Indices;
469 | const obstacle2Indices = drag.obstacle2Indices;
470 | const obstacle3Indices = drag.obstacle3Indices;
471 |
472 | type Obstacles = { [key: string]: number[] };
473 |
474 | const obstacles: Obstacles = {
475 | wall: wallIndices,
476 | obstacle1: obstacle1Indices,
477 | obstacle2: obstacle2Indices,
478 | obstacle3: obstacle3Indices,
479 | };
480 |
481 | dispatch(setObstacleRef(obstacles[obstacleRef]));
482 | };
483 | };
484 |
--------------------------------------------------------------------------------
/src/store/actions/graph.ts:
--------------------------------------------------------------------------------
1 | import { RefObject, Dispatch } from 'react';
2 | import { ActionTypes } from './types';
3 | import * as actions from '../actions';
4 | import { GraphTheme } from '../../utils/themes';
5 |
6 | export const initGraph = (
7 | verticesRef: RefObject,
8 | isSettingWalls: boolean = false
9 | ) => {
10 | return {
11 | type: ActionTypes.INIT_GRAPH,
12 | verticesRef,
13 | isSettingWalls,
14 | };
15 | };
16 |
17 | export const setVerticesRef = (verticesRef: RefObject) => {
18 | return {
19 | type: ActionTypes.SET_VERTICES_REF,
20 | verticesRef,
21 | };
22 | };
23 |
24 | export const setStartVertex = (
25 | startRow: number,
26 | startCol: number,
27 | verticesRef: RefObject
28 | ) => {
29 | return {
30 | type: ActionTypes.SET_START_VERTEX,
31 | startRow,
32 | startCol,
33 | verticesRef,
34 | };
35 | };
36 |
37 | export const setEndVertex = (
38 | endRow: number,
39 | endCol: number,
40 | verticesRef: RefObject
41 | ) => {
42 | return {
43 | type: ActionTypes.SET_END_VERTEX,
44 | endRow,
45 | endCol,
46 | verticesRef,
47 | };
48 | };
49 |
50 | export const clearTimeouts = () => {
51 | return {
52 | type: ActionTypes.CLEAR_TIMEOUTS,
53 | };
54 | };
55 |
56 | export const clearPath = (
57 | verticesRef: RefObject,
58 | isRecalculating: boolean = false,
59 | start: number = -1,
60 | end: number = -1
61 | ) => {
62 | return {
63 | type: ActionTypes.CLEAR_PATH,
64 | verticesRef,
65 | isRecalculating,
66 | start,
67 | end,
68 | };
69 | };
70 |
71 | export const setWallIndices = (
72 | wallIndices: number[],
73 | verticesRef: RefObject
74 | ) => {
75 | return {
76 | type: ActionTypes.SET_WALL_INDICES,
77 | wallIndices,
78 | verticesRef,
79 | };
80 | };
81 |
82 | export const onSetWallIndices = (
83 | wallIndices: number[],
84 | verticesRef: RefObject
85 | ) => {
86 | return (dispatch: Dispatch, getState: any) => {
87 | dispatch(setWallIndices(wallIndices, verticesRef));
88 | dispatch(initGraph(verticesRef, true));
89 | dispatch(
90 | onRecalculatePath(
91 | getState().graph.start,
92 | getState().graph.end,
93 | verticesRef,
94 | 0,
95 | 0,
96 | 0
97 | )
98 | );
99 | };
100 | };
101 |
102 | export const setObstacle1Indices = (
103 | obstacle1Indices: number[],
104 | verticesRef: RefObject
105 | ) => {
106 | return {
107 | type: ActionTypes.SET_OBSTACLE_1_INDICES,
108 | obstacle1Indices,
109 | verticesRef,
110 | };
111 | };
112 |
113 | export const onSetObstacle1Indices = (
114 | obstacle1Indices: number[],
115 | verticesRef: RefObject
116 | ) => {
117 | return (dispatch: Dispatch, getState: any) => {
118 | dispatch(setObstacle1Indices(obstacle1Indices, verticesRef));
119 | dispatch(initGraph(verticesRef, true));
120 | dispatch(
121 | onRecalculatePath(
122 | getState().graph.start,
123 | getState().graph.end,
124 | verticesRef,
125 | 0,
126 | 0,
127 | 0
128 | )
129 | );
130 | };
131 | };
132 |
133 | export const setObstacle2Indices = (
134 | obstacle2Indices: number[],
135 | verticesRef: RefObject
136 | ) => {
137 | return {
138 | type: ActionTypes.SET_OBSTACLE_2_INDICES,
139 | obstacle2Indices,
140 | verticesRef,
141 | };
142 | };
143 |
144 | export const onSetObstacle2Indices = (
145 | obstacle2Indices: number[],
146 | verticesRef: RefObject
147 | ) => {
148 | return (dispatch: Dispatch, getState: any) => {
149 | dispatch(setObstacle2Indices(obstacle2Indices, verticesRef));
150 | dispatch(initGraph(verticesRef, true));
151 | dispatch(
152 | onRecalculatePath(
153 | getState().graph.start,
154 | getState().graph.end,
155 | verticesRef,
156 | 0,
157 | 0,
158 | 0
159 | )
160 | );
161 | };
162 | };
163 |
164 | export const setObstacle3Indices = (
165 | obstacle3Indices: number[],
166 | verticesRef: RefObject
167 | ) => {
168 | return {
169 | type: ActionTypes.SET_OBSTACLE_3_INDICES,
170 | obstacle3Indices,
171 | verticesRef,
172 | };
173 | };
174 |
175 | export const onSetObstacle3Indices = (
176 | obstacle3Indices: number[],
177 | verticesRef: RefObject
178 | ) => {
179 | return (dispatch: Dispatch, getState: any) => {
180 | dispatch(setObstacle3Indices(obstacle3Indices, verticesRef));
181 | dispatch(initGraph(verticesRef, true));
182 | dispatch(
183 | onRecalculatePath(
184 | getState().graph.start,
185 | getState().graph.end,
186 | verticesRef,
187 | 0,
188 | 0,
189 | 0
190 | )
191 | );
192 | };
193 | };
194 |
195 | export const clearWalls = (verticesRef: RefObject) => {
196 | return {
197 | type: ActionTypes.CLEAR_WALLS,
198 | verticesRef,
199 | };
200 | };
201 |
202 | export const onClearWalls = (verticesRef: RefObject) => {
203 | return (dispatch: Dispatch, getState: any) => {
204 | dispatch(actions.clearDragWalls());
205 | dispatch(clearWalls(verticesRef));
206 | dispatch(initGraph(verticesRef, true));
207 | };
208 | };
209 |
210 | export const setIsAnimating = (isAnimating: boolean) => {
211 | return {
212 | type: ActionTypes.SET_IS_ANIMATING,
213 | isAnimating,
214 | };
215 | };
216 |
217 | export const recalculatePath = (
218 | start: number,
219 | end: number,
220 | verticesRef: RefObject
221 | ) => {
222 | return {
223 | type: ActionTypes.RECALCULATE_PATH,
224 | start,
225 | end,
226 | verticesRef,
227 | };
228 | };
229 |
230 | export const onRecalculatePath = (
231 | start: number,
232 | end: number,
233 | verticesRef: RefObject,
234 | overEndIndex: number,
235 | overStartIndex: number,
236 | overObstacleIndex: number
237 | ) => {
238 | return (dispatch: Dispatch, getState: any) => {
239 | let drag = getState().drag;
240 | let isDoneAnimating = drag.isDoneAnimating;
241 | let isStartMouseDown = drag.isStartMouseDown;
242 | let isEndMouseDown = drag.isEndMouseDown;
243 | // TODO: add when new walls to condition
244 | if (isDoneAnimating && (isStartMouseDown || isEndMouseDown)) {
245 | dispatch(actions.setIsRecalculating(true));
246 | dispatch(clearPath(verticesRef, true, start, end));
247 |
248 | let startIndex = start;
249 | let endIndex = end;
250 | if (drag.isOverEnd) {
251 | startIndex = overEndIndex;
252 | } else if (
253 | drag.isOverObstacle &&
254 | !drag.isOverStart &&
255 | isStartMouseDown
256 | ) {
257 | startIndex = overObstacleIndex;
258 | } else if (drag.isOverStart) {
259 | endIndex = overStartIndex;
260 | } else if (
261 | drag.isOverObstacle &&
262 | !drag.isOverEnd &&
263 | isEndMouseDown
264 | ) {
265 | endIndex = overObstacleIndex;
266 | }
267 |
268 | dispatch(recalculatePath(startIndex, endIndex, verticesRef));
269 | }
270 | };
271 | };
272 |
273 | export const setCurrentAlgorithm = (algorithm: Function) => {
274 | return {
275 | type: ActionTypes.SET_CURRENT_ALGORITHM,
276 | currentAlgorithm: algorithm,
277 | };
278 | };
279 |
280 | export const setNumRows = (numRows: number) => {
281 | return {
282 | type: ActionTypes.SET_NUM_ROWS,
283 | numRows,
284 | };
285 | };
286 |
287 | export const setNumCols = (numCols: number) => {
288 | return {
289 | type: ActionTypes.SET_NUM_COLS,
290 | numCols,
291 | };
292 | };
293 |
294 | export const setTheme = (theme: GraphTheme) => {
295 | return {
296 | type: ActionTypes.SET_THEME,
297 | theme,
298 | };
299 | };
300 |
--------------------------------------------------------------------------------
/src/store/actions/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types';
2 | export * from './graph';
3 | export * from './drag';
4 |
--------------------------------------------------------------------------------
/src/store/actions/types.ts:
--------------------------------------------------------------------------------
1 | export enum ActionTypes {
2 | INIT_GRAPH,
3 | SET_START_ROW,
4 | SET_START_COL,
5 | SET_END_ROW,
6 | SET_END_COL,
7 | MOUSE_DOWN,
8 | MOUSE_UP,
9 | MOUSE_OVER,
10 | MOUSE_OUT,
11 | CLEAR_TIMEOUTS,
12 | CLEAR_PATH,
13 | SET_WALL_INDICES,
14 | CLEAR_WALLS,
15 | CLEAR_DRAG_WALLS,
16 | SET_IS_ANIMATING,
17 | SET_IS_DONE_ANIMATING,
18 | RECALCULATE_PATH,
19 | SET_IS_RECALCULATING,
20 | SET_VERTICES_REF,
21 | SET_START_VERTEX,
22 | SET_END_VERTEX,
23 | SET_CURRENT_ALGORITHM,
24 | SET_NUM_ROWS,
25 | SET_NUM_COLS,
26 | SET_OBSTACLE_REF,
27 | SET_OBSTACLE_1_INDICES,
28 | SET_OBSTACLE_2_INDICES,
29 | SET_OBSTACLE_3_INDICES,
30 | SET_THEME,
31 | }
32 |
--------------------------------------------------------------------------------
/src/store/reducers/index.ts:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { graphReducer, GraphState } from './graph';
3 | import { dragReducer, DragState } from './drag';
4 |
5 | export interface StoreState {
6 | graph: GraphState;
7 | drag: DragState;
8 | }
9 |
10 | export const reducers = combineReducers({
11 | graph: graphReducer,
12 | drag: dragReducer,
13 | });
14 |
--------------------------------------------------------------------------------
/src/utils/colors.ts:
--------------------------------------------------------------------------------
1 | export const COLOR_START = 'blue';
2 | export const COLOR_END = 'red';
3 | export const COLOR_VISITED = '#8d6b94';
4 | export const COLOR_VISITING = '#ff6b6b';
5 | export const COLOR_PATH = '#4ecdc4';
6 | export const COLOR_WALL = '#292f36';
7 |
8 | export const COLOR_GRASS = '#ACE4AA';
9 | export const COLOR_WATER = '#56CBF9';
10 | export const COLOR_TRAFFIC = '#F9C8D8';
11 | export const COLOR_MUD = 'brown';
12 | export const COLOR_CONCRETE = '#57585A';
13 |
14 | export const COLOR_AVENGERS_PURPLE = '#9789FB';
15 | export const COLOR_AVENGERS_GOLD = '#E4B835';
16 | export const COLOR_AVENGERS_BROWN = '#974613';
17 | export const COLOR_AVENGERS_BLACK = '#03011D';
18 |
19 | export const COLOR_ONEPIECE_LIGHTBLUE = '#47BBE8';
20 | export const COLOR_ONEPIECE_DARKBLUE = '#0A2ACE';
21 | export const COLOR_ONEPIECE_WHITE = '#fff';
22 | export const COLOR_ONEPIECE_BLACK = '#000';
23 | export const COLOR_ONEPIECE_RED = '#F80101';
24 | export const COLOR_ONEPIECE_YELLOW = '#F5CC01';
25 |
26 | export const COLOR_LAKERS_PURPLE = '#52247F';
27 | export const COLOR_LAKERS_GOLD = '#F5B326';
28 | export const COLOR_LAKERS_WHITE = '#F8F8F8';
29 |
30 | export const COLOR_LAKERS_BLACK = '#000';
31 |
32 | export const COLOR_POKEMON_EMERALD = '#1BA763';
33 | export const COLOR_POKEMON_BLUE = '#00479E';
34 | export const COLOR_POKEMON_YELLOW = '#F8C406';
35 |
36 | export const COLOR_THEOFFICE_BLACK = '#000';
37 | export const COLOR_THEOFFICE_WHITE = '#fff';
38 | export const COLOR_THEOFFICE_BROWN = '#925B4E';
39 | export const COLOR_THEOFFICE_BLUE = '#36BAEF';
40 |
41 | export const COLOR_HUNTERXHUNTER_BROWN = '#7F5A52';
42 | export const COLOR_HUNTERXHUNTER_DARK_BROWN = '#5E413E';
43 | export const COLOR_HUNTERXHUNTER_GREEN = '#116D35';
44 | export const COLOR_HUNTERXHUNTER_WHITE = '#fff';
45 | export const COLOR_HUNTERXHUNTER_LIGHT_ORANGE = '#F7A74D';
46 | export const COLOR_HUNTERXHUNTER_DARK_ORANGE = '#CE2619';
47 | export const COLOR_HUNTERXHUNTER_MAROON = '#7F252F';
48 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/aStar.ts:
--------------------------------------------------------------------------------
1 | import { Graph } from './graph';
2 | import { PriorityQueueFast } from './priorityQueue';
3 | import * as GraphTypes from './graphTypes';
4 | import * as Position from '../../position';
5 | import { PathfindingStates } from '../pathfindingStates';
6 | import { buildPath } from './buildPath';
7 |
8 | export const aStar = (
9 | graph: Graph,
10 | start: number,
11 | end: number
12 | ): GraphTypes.PathfindingAnimation[] => {
13 | let nodes = new PriorityQueueFast(graph.numRows, graph.numCols);
14 | let previous: GraphTypes.Previous = {};
15 | let distances: GraphTypes.Distances = {};
16 | let pathfindingAnimation: GraphTypes.PathfindingAnimation[] = [];
17 | let path: number[] = [];
18 | let endIndices = Position.absoluteToIndex(
19 | end,
20 | graph.numRows,
21 | graph.numCols
22 | );
23 | let endVertex: GraphTypes.Vertex = {
24 | node: end,
25 | weight: 1,
26 | x: endIndices.row,
27 | y: endIndices.col,
28 | };
29 | let foundEnd = false;
30 |
31 | nodes.enqueue(start, 0);
32 | previous[start] = NaN;
33 | distances[start] = 0;
34 |
35 | pathfindingAnimation.push({
36 | index: start,
37 | state: PathfindingStates.VISITING,
38 | });
39 |
40 | while (nodes.values.length) {
41 | let current = nodes.dequeue();
42 |
43 | // pathfindingAnimation.push({
44 | // index: current.node,
45 | // state: PathfindingStates.VISITING,
46 | // });
47 |
48 | if (current.node === end) {
49 | buildPath(
50 | pathfindingAnimation,
51 | current.node,
52 | previous,
53 | path,
54 | start,
55 | end,
56 | graph
57 | );
58 |
59 | break;
60 | }
61 |
62 | for (const neighbor of graph.adjacencyList[current.node]) {
63 | const newCost =
64 | distances[current.node] +
65 | graph.getEdgeWeight(current.node, neighbor.node);
66 |
67 | const priority = newCost + graph.getHeuristic(neighbor, endVertex);
68 |
69 | if (!(neighbor.node in distances) && !foundEnd) {
70 | pathfindingAnimation.push({
71 | index: neighbor.node,
72 | state: PathfindingStates.VISITING,
73 | });
74 | if (neighbor.node === end) {
75 | foundEnd = true;
76 | }
77 | }
78 |
79 | if (
80 | !(neighbor.node in distances) ||
81 | newCost < distances[neighbor.node]
82 | ) {
83 | if (neighbor.node in distances) {
84 | nodes.updatePriority(neighbor.node, priority);
85 | } else {
86 | nodes.enqueue(neighbor.node, priority);
87 | }
88 | distances[neighbor.node] = newCost;
89 | previous[neighbor.node] = current.node;
90 | }
91 | }
92 | }
93 |
94 | if (path.length === 0) {
95 | pathfindingAnimation.push({
96 | index: 0,
97 | state: PathfindingStates.NO_PATH,
98 | });
99 | }
100 |
101 | return pathfindingAnimation;
102 | };
103 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/breadthFirstSearch.ts:
--------------------------------------------------------------------------------
1 | import { Graph } from './graph';
2 | import * as GraphTypes from './graphTypes';
3 | import { PathfindingStates } from '../pathfindingStates';
4 | import { buildPath } from './buildPath';
5 |
6 | export const breadthFirstSearch = (
7 | graph: Graph,
8 | start: number,
9 | end: number
10 | ): GraphTypes.PathfindingAnimation[] => {
11 | let nodes: number[] = [];
12 | nodes.push(start);
13 | let previous: GraphTypes.Previous = {};
14 | previous[start] = NaN;
15 | let path: number[] = [];
16 | let pathfindingAnimation: GraphTypes.PathfindingAnimation[] = [];
17 |
18 | while (nodes.length) {
19 | let current = nodes.shift();
20 |
21 | pathfindingAnimation.push({
22 | index: current as number,
23 | state: PathfindingStates.VISITING,
24 | });
25 |
26 | if (current === end) {
27 | buildPath(
28 | pathfindingAnimation,
29 | current,
30 | previous,
31 | path,
32 | start,
33 | end,
34 | graph
35 | );
36 |
37 | break;
38 | }
39 |
40 | if (current) {
41 | for (let neighbor of graph.adjacencyList[current]) {
42 | if (!(neighbor.node in previous)) {
43 | nodes.push(neighbor.node);
44 | previous[neighbor.node] = current;
45 | }
46 | }
47 | }
48 | }
49 |
50 | return pathfindingAnimation;
51 | };
52 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/buildPath.ts:
--------------------------------------------------------------------------------
1 | import { PathfindingAnimation, Previous } from './graphTypes';
2 | import { PathfindingStates } from '../pathfindingStates';
3 | import { Neighbors, getNeighbors, absoluteToIndex } from '../../position';
4 | import { Graph } from './graph';
5 |
6 | export const buildPath = (
7 | pathfindingAnimation: PathfindingAnimation[],
8 | currentNode: number,
9 | previous: Previous,
10 | path: number[],
11 | start: number,
12 | end: number,
13 | graph: Graph
14 | ) => {
15 | while (previous[currentNode] || previous[currentNode] === 0) {
16 | path.push(currentNode);
17 | currentNode = previous[currentNode];
18 | }
19 |
20 | path.push(currentNode);
21 | path.reverse();
22 |
23 | for (let i = 0; i < path.length; i++) {
24 | let previousNeighbors: Neighbors = {
25 | left: -1,
26 | right: -1,
27 | top: -1,
28 | bottom: -1,
29 | };
30 | if (i > 0) {
31 | let previousIndices = absoluteToIndex(
32 | path[i - 1],
33 | graph.numRows,
34 | graph.numCols
35 | );
36 | previousNeighbors = getNeighbors(
37 | path[i - 1],
38 | previousIndices.row,
39 | graph.numRows,
40 | graph.numCols
41 | );
42 | }
43 |
44 | let startIndices = absoluteToIndex(
45 | path[i],
46 | graph.numRows,
47 | graph.numCols
48 | );
49 | let startNeighbors = getNeighbors(
50 | path[i],
51 | startIndices.row,
52 | graph.numRows,
53 | graph.numCols
54 | );
55 | let endIndices = absoluteToIndex(path[i], graph.numRows, graph.numCols);
56 | let endNeighbors = getNeighbors(
57 | path[i],
58 | endIndices.row,
59 | graph.numRows,
60 | graph.numCols
61 | );
62 |
63 | if (path[i] === start) {
64 | if (
65 | path[i + 1] === startNeighbors.left ||
66 | path[i + 1] === startNeighbors.right
67 | ) {
68 | pathfindingAnimation.push({
69 | index: path[i],
70 | state: PathfindingStates.PATH_HORIZONTAL_START,
71 | });
72 | } else if (
73 | path[i + 1] === startNeighbors.top ||
74 | path[i + 1] === startNeighbors.bottom
75 | ) {
76 | pathfindingAnimation.push({
77 | index: path[i],
78 | state: PathfindingStates.PATH_VERTICAL_START,
79 | });
80 | }
81 | } else if (path[i] === end) {
82 | if (
83 | path[i - 1] === endNeighbors.left ||
84 | path[i - 1] === endNeighbors.right
85 | ) {
86 | pathfindingAnimation.push({
87 | index: path[i],
88 | state: PathfindingStates.PATH_HORIZONTAL_END,
89 | });
90 | } else if (
91 | path[i - 1] === endNeighbors.top ||
92 | path[i - 1] === endNeighbors.bottom
93 | ) {
94 | pathfindingAnimation.push({
95 | index: path[i],
96 | state: PathfindingStates.PATH_VERTICAL_END,
97 | });
98 | }
99 | } else if (
100 | path[i] === previousNeighbors.top ||
101 | path[i] === previousNeighbors.bottom
102 | ) {
103 | pathfindingAnimation.push({
104 | index: path[i],
105 | state: PathfindingStates.PATH_VERTICAL,
106 | });
107 | } else if (
108 | path[i] === previousNeighbors.left ||
109 | path[i] === previousNeighbors.right
110 | ) {
111 | pathfindingAnimation.push({
112 | index: path[i],
113 | state: PathfindingStates.PATH_HORIZONTAL,
114 | });
115 | } else {
116 | pathfindingAnimation.push({
117 | index: path[i],
118 | state: PathfindingStates.PATH,
119 | });
120 | }
121 | }
122 |
123 | pathfindingAnimation.push({
124 | index: 0,
125 | state: PathfindingStates.DONE,
126 | });
127 | };
128 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/depthFirstSearch.ts:
--------------------------------------------------------------------------------
1 | import { Graph } from './graph';
2 | import * as GraphTypes from './graphTypes';
3 | import { PathfindingStates } from '../pathfindingStates';
4 | import { buildPath } from './buildPath';
5 |
6 | export const depthFirstSearch = (
7 | graph: Graph,
8 | start: number,
9 | end: number
10 | ): GraphTypes.PathfindingAnimation[] => {
11 | let visited: number[] = [];
12 | let pathfindingAnimations: GraphTypes.PathfindingAnimation[] = [];
13 | let previous: GraphTypes.Previous = {};
14 | let path: number[] = [];
15 |
16 | depthFirstSearchUtil(
17 | graph,
18 | start,
19 | end,
20 | visited,
21 | pathfindingAnimations,
22 | previous
23 | );
24 |
25 | buildPath(pathfindingAnimations, end, previous, path, start, end, graph);
26 |
27 | return pathfindingAnimations;
28 | };
29 |
30 | const depthFirstSearchUtil = (
31 | graph: Graph,
32 | start: number,
33 | end: number,
34 | visited: number[],
35 | pathfindingAnimations: GraphTypes.PathfindingAnimation[],
36 | previous: GraphTypes.Previous
37 | ) => {
38 | visited.push(start);
39 |
40 | pathfindingAnimations.push({
41 | index: start,
42 | state: PathfindingStates.VISITING,
43 | });
44 |
45 | for (let neighbor of graph.adjacencyList[start]) {
46 | if (!visited.includes(end)) {
47 | if (!visited.includes(neighbor.node)) {
48 | depthFirstSearchUtil(
49 | graph,
50 | neighbor.node,
51 | end,
52 | visited,
53 | pathfindingAnimations,
54 | previous
55 | );
56 | previous[neighbor.node] = start;
57 | }
58 | }
59 | }
60 | };
61 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/dijkstra.ts:
--------------------------------------------------------------------------------
1 | import { Graph } from './graph';
2 | import { PriorityQueue } from './priorityQueue';
3 | import * as GraphTypes from './graphTypes';
4 | import { PathfindingStates } from '../pathfindingStates';
5 | import { buildPath } from './buildPath';
6 | export const dijkstra = (
7 | graph: Graph,
8 | start: number,
9 | end: number
10 | ): GraphTypes.PathfindingAnimation[] => {
11 | let nodes = new PriorityQueue(graph.numRows, graph.numCols);
12 | let previous: GraphTypes.Previous = {};
13 | let distances: GraphTypes.Distances = {};
14 | let path: number[] = [];
15 | let pathfindingAnimation: GraphTypes.PathfindingAnimation[] = [];
16 |
17 | for (const vertex in graph.adjacencyList) {
18 | const vertexIndex = parseInt(vertex);
19 | distances[vertexIndex] = Infinity;
20 | previous[vertexIndex] = NaN;
21 | nodes.enqueue(vertexIndex, Infinity);
22 | }
23 | distances[start] = 0;
24 | previous[start] = NaN;
25 | nodes.updatePriority(start, 0);
26 |
27 | while (nodes.values.length) {
28 | const current = nodes.dequeue();
29 |
30 | pathfindingAnimation.push({
31 | index: current.node,
32 | state: PathfindingStates.VISITING,
33 | });
34 |
35 | if (current.node === end) {
36 | buildPath(
37 | pathfindingAnimation,
38 | end,
39 | previous,
40 | path,
41 | start,
42 | end,
43 | graph
44 | );
45 |
46 | break;
47 | }
48 |
49 | for (const neighbor of graph.adjacencyList[current.node]) {
50 | const newCost =
51 | distances[current.node] +
52 | graph.getEdgeWeight(current.node, neighbor.node);
53 |
54 | if (newCost < distances[neighbor.node]) {
55 | distances[neighbor.node] = newCost;
56 | previous[neighbor.node] = current.node;
57 | nodes.updatePriority(neighbor.node, newCost);
58 | }
59 | }
60 | }
61 |
62 | if (path.length === 0) {
63 | pathfindingAnimation.push({
64 | index: 0,
65 | state: PathfindingStates.NO_PATH,
66 | });
67 | }
68 |
69 | return pathfindingAnimation;
70 | };
71 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/graph.ts:
--------------------------------------------------------------------------------
1 | import * as Position from '../../position';
2 | import * as GraphTypes from './graphTypes';
3 |
4 | export class Graph {
5 | adjacencyList: GraphTypes.AdjacencyList;
6 |
7 | constructor(public numRows: number, public numCols: number) {
8 | this.adjacencyList = {};
9 | }
10 |
11 | addVertex(vertex: number) {
12 | if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
13 | }
14 |
15 | addEdge(vertex1: number, vertex2: number, weight: number = 1) {
16 | let indices = Position.absoluteToIndex(
17 | vertex2,
18 | this.numRows,
19 | this.numCols
20 | );
21 |
22 | this.adjacencyList[vertex1].push({
23 | node: vertex2,
24 | weight,
25 | x: indices.row,
26 | y: indices.col,
27 | });
28 | }
29 |
30 | getEdgeWeight(vertex1: number, vertex2: number) {
31 | for (const edge of this.adjacencyList[vertex1]) {
32 | if (edge.node === vertex2) {
33 | return edge.weight;
34 | }
35 | }
36 | return Infinity;
37 | }
38 |
39 | getHeuristic(vertex1: GraphTypes.Vertex, vertex2: GraphTypes.Vertex) {
40 | return (
41 | Math.abs(vertex1.x - vertex2.x) + Math.abs(vertex1.y - vertex2.y)
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/graphTypes.ts:
--------------------------------------------------------------------------------
1 | import { PathfindingStates } from '../pathfindingStates';
2 |
3 | export type Vertex = { node: number; weight: number; x: number; y: number };
4 | export type Distances = { [key: string]: number };
5 | export type Previous = { [key: string]: number };
6 | export type AdjacencyList = { [key: string]: Vertex[] };
7 | export type PathfindingAnimation = { index: number; state: PathfindingStates };
8 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/greedyBestFirstSearch.ts:
--------------------------------------------------------------------------------
1 | import { Graph } from './graph';
2 | import { PriorityQueue } from './priorityQueue';
3 | import * as GraphTypes from './graphTypes';
4 | import * as Position from '../../position';
5 | import { PathfindingStates } from '../pathfindingStates';
6 | import { buildPath } from './buildPath';
7 |
8 | export const greedyBestFirstSearch = (
9 | graph: Graph,
10 | start: number,
11 | end: number
12 | ): GraphTypes.PathfindingAnimation[] => {
13 | let nodes = new PriorityQueue(graph.numRows, graph.numCols);
14 | let previous: GraphTypes.Previous = {};
15 | let endIndices = Position.absoluteToIndex(
16 | end,
17 | graph.numRows,
18 | graph.numCols
19 | );
20 | let endVertex: GraphTypes.Vertex = {
21 | node: end,
22 | weight: 1,
23 | x: endIndices.row,
24 | y: endIndices.col,
25 | };
26 | let visited: number[] = [];
27 | let pathfindingAnimation: GraphTypes.PathfindingAnimation[] = [];
28 | let path: number[] = [];
29 | let foundEnd = false;
30 |
31 | nodes.enqueue(start, 0);
32 | previous[start] = NaN;
33 |
34 | visited.push(start);
35 |
36 | pathfindingAnimation.push({
37 | index: start,
38 | state: PathfindingStates.VISITING,
39 | });
40 |
41 | while (nodes.values.length) {
42 | let current = nodes.dequeue();
43 |
44 | // pathfindingAnimation.push({
45 | // index: current.node,
46 | // state: PathfindingStates.VISITING,
47 | // });
48 |
49 | if (current.node === end) {
50 | buildPath(
51 | pathfindingAnimation,
52 | current.node,
53 | previous,
54 | path,
55 | start,
56 | end,
57 | graph
58 | );
59 |
60 | break;
61 | }
62 |
63 | for (const neighbor of graph.adjacencyList[current.node]) {
64 | if (!(neighbor.node in previous)) {
65 | if (!foundEnd) {
66 | pathfindingAnimation.push({
67 | index: neighbor.node,
68 | state: PathfindingStates.VISITING,
69 | });
70 | if (neighbor.node === end) {
71 | foundEnd = true;
72 | }
73 | }
74 |
75 | const priority = graph.getHeuristic(neighbor, endVertex);
76 | previous[neighbor.node] = current.node;
77 | nodes.enqueue(neighbor.node, priority);
78 | }
79 | }
80 | }
81 |
82 | if (path.length === 0) {
83 | pathfindingAnimation.push({
84 | index: 0,
85 | state: PathfindingStates.NO_PATH,
86 | });
87 | }
88 |
89 | return pathfindingAnimation;
90 | };
91 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/index.ts:
--------------------------------------------------------------------------------
1 | export { aStar } from './aStar';
2 | export { dijkstra } from './dijkstra';
3 | export { greedyBestFirstSearch } from './greedyBestFirstSearch';
4 | export { breadthFirstSearch } from './breadthFirstSearch';
5 | export { depthFirstSearch } from './depthFirstSearch';
6 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/algorithms/priorityQueue.ts:
--------------------------------------------------------------------------------
1 | import * as GraphTypes from './graphTypes';
2 | import * as Position from '../../position';
3 |
4 | export class PriorityQueueFast {
5 | values: GraphTypes.Vertex[];
6 |
7 | constructor(public numRows: number, public numCols: number) {
8 | this.values = [];
9 | }
10 |
11 | enqueue(val: number, priority: number) {
12 | let indices = Position.absoluteToIndex(val, this.numRows, this.numCols);
13 | let newNode: GraphTypes.Vertex = {
14 | node: val,
15 | weight: priority,
16 | x: indices.row,
17 | y: indices.col,
18 | };
19 | this.values.push(newNode);
20 |
21 | let index = this.values.length - 1;
22 | let parentIndex = Math.floor((index - 1) / 2);
23 |
24 | while (
25 | parentIndex >= 0 &&
26 | this.values[index].weight <= this.values[parentIndex].weight
27 | ) {
28 | this.swap(this.values, index, parentIndex);
29 | index = parentIndex;
30 | parentIndex = Math.floor((index - 1) / 2);
31 | }
32 | }
33 |
34 | updatePriority(val: number, priority: number) {
35 | for (const value of this.values) {
36 | if (value.node === val) {
37 | this.values.splice(this.values.indexOf(value), 1);
38 | this.enqueue(val, priority);
39 | }
40 | }
41 | }
42 |
43 | dequeue() {
44 | this.swap(this.values, 0, this.values.length - 1);
45 | let max = this.values[this.values.length - 1];
46 | this.values.pop();
47 |
48 | let parentIndex = 0;
49 | while (true) {
50 | let leftChildIndex = 2 * parentIndex + 1;
51 | let rightChildIndex = 2 * parentIndex + 2;
52 | let swapIndex;
53 | if (leftChildIndex < this.values.length) {
54 | if (rightChildIndex < this.values.length) {
55 | swapIndex =
56 | this.values[leftChildIndex].weight <
57 | this.values[rightChildIndex].weight
58 | ? leftChildIndex
59 | : rightChildIndex;
60 | } else {
61 | swapIndex = leftChildIndex;
62 | }
63 | }
64 | if (
65 | swapIndex &&
66 | this.values[parentIndex].weight >= this.values[swapIndex].weight
67 | ) {
68 | this.swap(this.values, parentIndex, swapIndex);
69 | parentIndex = swapIndex;
70 | } else {
71 | break;
72 | }
73 | }
74 |
75 | return max;
76 | }
77 |
78 | swap(arr: GraphTypes.Vertex[], ind1: number, ind2: number) {
79 | [arr[ind1], arr[ind2]] = [arr[ind2], arr[ind1]];
80 | }
81 | }
82 |
83 | export class PriorityQueue {
84 | values: GraphTypes.Vertex[];
85 |
86 | constructor(public numRows: number, public numCols: number) {
87 | this.values = [];
88 | }
89 |
90 | enqueue(val: number, priority: number) {
91 | let indices = Position.absoluteToIndex(val, this.numRows, this.numCols);
92 | let newNode: GraphTypes.Vertex = {
93 | node: val,
94 | weight: priority,
95 | x: indices.row,
96 | y: indices.col,
97 | };
98 | this.values.push(newNode);
99 | this.sort();
100 | }
101 |
102 | dequeue() {
103 | return this.values.shift() as GraphTypes.Vertex;
104 | }
105 |
106 | updatePriority(val: number, priority: number) {
107 | for (const value of this.values) {
108 | if (value.node === val) {
109 | this.values.splice(this.values.indexOf(value), 1);
110 | this.enqueue(val, priority);
111 | }
112 | }
113 | }
114 |
115 | sort() {
116 | this.values.sort((a, b) => a.weight - b.weight);
117 | }
118 |
119 | swap(arr: GraphTypes.Vertex[], ind1: number, ind2: number) {
120 | [arr[ind1], arr[ind2]] = [arr[ind2], arr[ind1]];
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/pathfindingAlgorithms.ts:
--------------------------------------------------------------------------------
1 | import * as algorithms from './algorithms';
2 |
3 | type PathfindingAlgorithms = { [key: string]: Function };
4 |
5 | export const pathfindingAlgorithms: PathfindingAlgorithms = {
6 | BreadthFirstSearch: algorithms.breadthFirstSearch,
7 | Dijkstra: algorithms.dijkstra,
8 | GreedyBestFirstSearch: algorithms.greedyBestFirstSearch,
9 | AStar: algorithms.aStar,
10 | DepthFirstSearch: algorithms.depthFirstSearch,
11 | };
12 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/pathfindingOptions.ts:
--------------------------------------------------------------------------------
1 | export const pathfindingOptions = {
2 | Dijkstra: 'Dijkstra',
3 | AStar: 'A*',
4 | GreedyBestFirstSearch: 'Greedy Best First Search',
5 | BreadthFirstSearch: 'Breadth First Search',
6 | DepthFirstSearch: 'Depth First Search',
7 | };
8 |
--------------------------------------------------------------------------------
/src/utils/pathfinding/pathfindingStates.ts:
--------------------------------------------------------------------------------
1 | export enum PathfindingStates {
2 | VISITING,
3 | PATH,
4 | PATH_HORIZONTAL_START,
5 | PATH_VERTICAL_START,
6 | PATH_HORIZONTAL_END,
7 | PATH_VERTICAL_END,
8 | PATH_VERTICAL,
9 | PATH_HORIZONTAL,
10 | DONE,
11 | NO_PATH,
12 | }
13 |
--------------------------------------------------------------------------------
/src/utils/position.ts:
--------------------------------------------------------------------------------
1 | import { RefObject } from 'react';
2 | import { Vertex } from '../store/reducers/graph';
3 |
4 | export interface Neighbors {
5 | left: number;
6 | right: number;
7 | top: number;
8 | bottom: number;
9 | }
10 |
11 | export function absoluteToIndex(
12 | index: number,
13 | numRows: number,
14 | numCols: number
15 | ) {
16 | let row = 0;
17 | let col = 0;
18 |
19 | for (let i = 0; i < numRows; i++) {
20 | for (let j = 0; j < numCols; j++) {
21 | if (i * numCols + j === index) {
22 | row = i;
23 | col = j;
24 | }
25 | }
26 | }
27 | return { row, col };
28 | }
29 |
30 | export function indexToAbsolute(
31 | row: number,
32 | col: number,
33 | numRows: number,
34 | numCols: number
35 | ) {
36 | let index = 0;
37 |
38 | index += row * numCols;
39 | index += col;
40 |
41 | return index;
42 | }
43 |
44 | export const getVertex = (
45 | row: number,
46 | column: number,
47 | numRows: number,
48 | numCols: number,
49 | verticesRef: RefObject
50 | ): Vertex => {
51 | return {
52 | element: verticesRef.current?.children[row].children[
53 | column
54 | ] as HTMLDivElement,
55 | row,
56 | column,
57 | absoluteIndex: indexToAbsolute(row, column, numRows, numCols),
58 | };
59 | };
60 |
61 | export const getVertexAbsolute = (
62 | absoluteIndex: number,
63 | numRows: number,
64 | numCols: number,
65 | verticesRef: RefObject
66 | ): Vertex => {
67 | let vertexIndices = absoluteToIndex(absoluteIndex, numRows, numCols);
68 |
69 | return {
70 | element: verticesRef.current?.children[vertexIndices.row].children[
71 | vertexIndices.col
72 | ] as HTMLDivElement,
73 | row: vertexIndices.row,
74 | column: vertexIndices.col,
75 | absoluteIndex,
76 | };
77 | };
78 |
79 | export const getNeighbors = (
80 | idx: number,
81 | row: number,
82 | numRows: number,
83 | numCols: number
84 | ): Neighbors => {
85 | let leftNeighbor: number = idx - 1;
86 | let rightNeighbor: number = idx + 1;
87 | let topNeighbor: number = idx - numCols;
88 | let bottomNeighbor: number = idx + numCols;
89 |
90 | if (!validLeftNeighbor(leftNeighbor, row, numCols)) leftNeighbor = -1;
91 | if (!validRightNeighbor(rightNeighbor, row, numCols)) rightNeighbor = -1;
92 | if (!validTopNeighbor(topNeighbor)) topNeighbor = -1;
93 | if (!validBottomNeighbor(bottomNeighbor, numRows, numCols))
94 | bottomNeighbor = -1;
95 |
96 | return {
97 | left: leftNeighbor,
98 | right: rightNeighbor,
99 | top: topNeighbor,
100 | bottom: bottomNeighbor,
101 | };
102 | };
103 |
104 | export const validLeftNeighbor = (
105 | leftNeighbor: number,
106 | row: number,
107 | numCols: number
108 | ): boolean => {
109 | return leftNeighbor !== numCols * row - 1;
110 | };
111 |
112 | export const validRightNeighbor = (
113 | rightNeighbor: number,
114 | row: number,
115 | numCols: number
116 | ): boolean => {
117 | return rightNeighbor !== numCols * (row + 1);
118 | };
119 |
120 | export const validTopNeighbor = (topNeighbor: number): boolean => {
121 | return topNeighbor >= 0;
122 | };
123 |
124 | export const validBottomNeighbor = (
125 | bottomNeighbor: number,
126 | numRows: number,
127 | numCols: number
128 | ): boolean => {
129 | return bottomNeighbor < numRows * numCols;
130 | };
131 |
--------------------------------------------------------------------------------
/src/utils/themes/avengers.ts:
--------------------------------------------------------------------------------
1 | import { GraphTheme, Obstacle, is_touch_device } from './index';
2 | import * as Colors from '../colors';
3 | import thanos from './img/thanos.png';
4 | import gauntlet from './img/gauntlet.png';
5 | import ironMan from './img/ironMan.png';
6 | import ironManCursor from './img/ironMan.png';
7 | import captainAmerica from './img/captainAmerica.png';
8 | import captainAmericaCursor from './img/captainAmerica.png';
9 | import thor from './img/thor.png';
10 | import thorCursor from './img/thor.png';
11 | import captainMarvel from './img/captainMarvel.png';
12 | import captainMarvelCursor from './img/captainMarvel.png';
13 |
14 | export const avengersTheme: GraphTheme = {
15 | start: (vertex: HTMLDivElement) => {
16 | vertex.style.backgroundColor = '';
17 | vertex.style.backgroundImage = `url(${thanos})`;
18 | vertex.style.backgroundRepeat = 'no-repeat';
19 | vertex.style.backgroundPosition = 'center';
20 | vertex.style.backgroundSize = '90%';
21 | vertex.style.cursor = 'grab';
22 | },
23 | end: (vertex: HTMLDivElement) => {
24 | vertex.style.backgroundColor = '';
25 | vertex.style.backgroundImage = `url(${gauntlet})`;
26 | vertex.style.backgroundRepeat = 'no-repeat';
27 | vertex.style.backgroundPosition = 'center';
28 | vertex.style.backgroundSize = '40%';
29 | vertex.style.cursor = 'grab';
30 | },
31 | wall: (vertex: HTMLDivElement) => {
32 | vertex.style.backgroundColor = '';
33 | vertex.style.backgroundImage = `url(${ironMan})`;
34 | vertex.style.backgroundRepeat = 'no-repeat';
35 | vertex.style.backgroundPosition = 'center';
36 | vertex.style.backgroundSize = '40%';
37 | vertex.style.cursor = `url(${ironManCursor}), pointer`;
38 | },
39 | cursorWall: (vertex: HTMLDivElement) => {
40 | vertex.style.cursor = `url(${ironManCursor}), pointer`;
41 | },
42 | obstacle1: (vertex: HTMLDivElement) => {
43 | vertex.style.backgroundColor = '';
44 | vertex.style.backgroundImage = `url(${captainAmerica})`;
45 | vertex.style.backgroundRepeat = 'no-repeat';
46 | vertex.style.backgroundPosition = 'center';
47 | vertex.style.backgroundSize = '50%';
48 | vertex.style.cursor = `url(${captainAmericaCursor}), pointer`;
49 | },
50 | cursorObstacle1: (vertex: HTMLDivElement) => {
51 | vertex.style.cursor = `url(${captainAmericaCursor}), pointer`;
52 | },
53 | obstacle2: (vertex: HTMLDivElement) => {
54 | vertex.style.backgroundColor = '';
55 | vertex.style.backgroundImage = `url(${thor})`;
56 | vertex.style.backgroundRepeat = 'no-repeat';
57 | vertex.style.backgroundPosition = 'center';
58 | vertex.style.backgroundSize = '60%';
59 | vertex.style.cursor = `url(${thorCursor}), pointer`;
60 | },
61 | cursorObstacle2: (vertex: HTMLDivElement) => {
62 | vertex.style.cursor = `url(${thorCursor}), pointer`;
63 | },
64 | obstacle3: (vertex: HTMLDivElement) => {
65 | vertex.style.backgroundColor = '';
66 | vertex.style.backgroundImage = `url(${captainMarvel})`;
67 | vertex.style.backgroundRepeat = 'no-repeat';
68 | vertex.style.backgroundPosition = 'center';
69 | vertex.style.backgroundSize = '70%';
70 | vertex.style.cursor = `url(${captainMarvelCursor}), pointer`;
71 | },
72 | cursorObstacle3: (vertex: HTMLDivElement) => {
73 | vertex.style.cursor = `url(${captainMarvelCursor}), pointer`;
74 | },
75 | unvisited: (vertex: HTMLDivElement) => {
76 | vertex.style.backgroundColor = '';
77 | vertex.style.backgroundImage = '';
78 | vertex.style.color = Colors.COLOR_AVENGERS_GOLD;
79 | vertex.style.cursor = `url(${ironManCursor}), pointer`;
80 | },
81 | visited: (vertex: HTMLDivElement) => {
82 | vertex.style.backgroundColor = Colors.COLOR_AVENGERS_PURPLE;
83 | },
84 | visiting: (vertex: HTMLDivElement) => {
85 | vertex.style.backgroundColor = Colors.COLOR_AVENGERS_GOLD;
86 | },
87 | pathHorizontalStart: (vertex: HTMLDivElement) => {
88 | vertex.style.backgroundImage = `url(${thanos})`;
89 | vertex.style.backgroundColor = Colors.COLOR_AVENGERS_GOLD;
90 | },
91 | pathVerticalStart: (vertex: HTMLDivElement) => {
92 | vertex.style.backgroundImage = `url(${thanos})`;
93 | vertex.style.backgroundColor = Colors.COLOR_AVENGERS_GOLD;
94 | },
95 | pathHorizontalEnd: (vertex: HTMLDivElement) => {
96 | vertex.style.backgroundImage = `url(${gauntlet})`;
97 | vertex.style.backgroundColor = Colors.COLOR_AVENGERS_GOLD;
98 | },
99 | pathVerticalEnd: (vertex: HTMLDivElement) => {
100 | vertex.style.backgroundImage = `url(${gauntlet})`;
101 | vertex.style.backgroundColor = Colors.COLOR_AVENGERS_GOLD;
102 | },
103 | pathHorizontal: (vertex: HTMLDivElement) => {
104 | vertex.style.backgroundColor = Colors.COLOR_AVENGERS_GOLD;
105 | },
106 | pathVertical: (vertex: HTMLDivElement) => {
107 | vertex.style.backgroundColor = Colors.COLOR_AVENGERS_GOLD;
108 | },
109 | revertObstacle: (vertex: HTMLDivElement) => {
110 | vertex.style.backgroundColor = '';
111 | vertex.style.backgroundImage = '';
112 | },
113 | revertPath: (vertex: HTMLDivElement) => {
114 | vertex.style.backgroundColor = '';
115 | },
116 | bodyBackground: (body: HTMLBodyElement) => {
117 | body.style.background = Colors.COLOR_AVENGERS_BLACK;
118 | },
119 | header: (div: HTMLDivElement) => {
120 | div.style.backgroundColor = Colors.COLOR_AVENGERS_BLACK;
121 | div.style.color = Colors.COLOR_AVENGERS_GOLD;
122 | div.style.fontFamily = 'Avengers, sans-serif';
123 | div.style.letterSpacing = '';
124 | div.style.fontSize = '2rem';
125 | },
126 | heading: (heading: HTMLHeadingElement) => {
127 | heading.style.background = `linear-gradient(to top, ${Colors.COLOR_AVENGERS_BROWN}, ${Colors.COLOR_AVENGERS_GOLD})`;
128 | heading.style.backgroundClip = 'text';
129 | heading.style.webkitBackgroundClip = 'text';
130 | heading.style.webkitTextFillColor = 'transparent';
131 | heading.style.webkitTextStroke = `4px ${Colors.COLOR_AVENGERS_BROWN}`;
132 | },
133 | controller: (div: HTMLDivElement) => {
134 | div.style.color = Colors.COLOR_AVENGERS_PURPLE;
135 | },
136 | button: (button: HTMLButtonElement) => {
137 | button.style.backgroundColor = 'transparent';
138 | button.style.color = Colors.COLOR_AVENGERS_PURPLE;
139 |
140 | if (!is_touch_device()) {
141 | button.addEventListener('mouseover', () => {
142 | button.style.backgroundColor = Colors.COLOR_AVENGERS_PURPLE;
143 | button.style.color = Colors.COLOR_AVENGERS_GOLD;
144 | });
145 |
146 | button.addEventListener('mouseout', () => {
147 | button.style.backgroundColor = 'transparent';
148 | button.style.color = Colors.COLOR_AVENGERS_PURPLE;
149 | });
150 | }
151 | },
152 | dropdown: (heading: HTMLHeadingElement) => {
153 | heading.style.backgroundColor = Colors.COLOR_AVENGERS_PURPLE;
154 | heading.style.color = Colors.COLOR_AVENGERS_GOLD;
155 | },
156 | options: (div: HTMLDivElement) => {
157 | div.style.backgroundColor = Colors.COLOR_AVENGERS_PURPLE;
158 | },
159 | option: (li: HTMLLIElement) => {
160 | li.style.backgroundColor = 'transparent';
161 | li.style.color = Colors.COLOR_AVENGERS_GOLD;
162 |
163 | if (!is_touch_device()) {
164 | li.addEventListener('mouseover', () => {
165 | li.style.backgroundColor = Colors.COLOR_AVENGERS_GOLD;
166 | li.style.color = Colors.COLOR_AVENGERS_PURPLE;
167 | });
168 |
169 | li.addEventListener('mouseout', () => {
170 | li.style.backgroundColor = 'transparent';
171 | li.style.color = Colors.COLOR_AVENGERS_GOLD;
172 | });
173 | }
174 | },
175 | };
176 |
177 | export const avengersObstacleOptions: Obstacle = {
178 | wall: 'Iron Man (Barrier)',
179 | obstacle1: 'Captain America (Weight: 2)',
180 | obstacle2: 'Thor (Weight: 3)',
181 | obstacle3: 'Captain Marvel (Weight: 4)',
182 | };
183 |
--------------------------------------------------------------------------------
/src/utils/themes/car.ts:
--------------------------------------------------------------------------------
1 | import { GraphTheme, Obstacle, is_touch_device } from './index';
2 | import * as Colors from '../colors';
3 | import car from './img/car.png';
4 | import location from './img/location.png';
5 | import carObstacle from './img/carObstacle.png';
6 | import carObstacleCursor from './img/carObstacleCursor.png';
7 | import rain from './img/rain.png';
8 | import rainCursor from './img/rainCursor.png';
9 | import cone from './img/cone.png';
10 | import coneCursor from './img/coneCursor.png';
11 | import hill from './img/hill.png';
12 | import hillCursor from './img/hillCursor.png';
13 | import roadHorizontal from './img/road-horizontal.jpg';
14 | import roadVertical from './img/road-vertical.jpg';
15 |
16 | export const carTheme: GraphTheme = {
17 | start: (vertex: HTMLDivElement) => {
18 | vertex.style.backgroundColor = '';
19 | vertex.style.backgroundImage = `url(${car})`;
20 | vertex.style.backgroundRepeat = 'no-repeat';
21 | vertex.style.backgroundPosition = 'center';
22 | vertex.style.backgroundSize = '90%';
23 | vertex.style.cursor = 'grab';
24 | },
25 | end: (vertex: HTMLDivElement) => {
26 | vertex.style.backgroundColor = '';
27 | vertex.style.backgroundImage = `url(${location})`;
28 | vertex.style.backgroundRepeat = 'no-repeat';
29 | vertex.style.backgroundPosition = 'center';
30 | vertex.style.backgroundSize = '90%';
31 | vertex.style.cursor = 'grab';
32 | },
33 | wall: (vertex: HTMLDivElement) => {
34 | vertex.style.backgroundColor = '';
35 | vertex.style.backgroundImage = `url(${carObstacle})`;
36 | vertex.style.backgroundRepeat = 'no-repeat';
37 | vertex.style.backgroundPosition = 'center';
38 | vertex.style.backgroundSize = '90%';
39 | vertex.style.cursor = `url(${carObstacleCursor}), pointer`;
40 | },
41 | cursorWall: (vertex: HTMLDivElement) => {
42 | vertex.style.cursor = `url(${carObstacleCursor}), pointer`;
43 | },
44 | obstacle1: (vertex: HTMLDivElement) => {
45 | vertex.style.backgroundColor = '';
46 | vertex.style.backgroundImage = `url(${rain})`;
47 | vertex.style.backgroundRepeat = 'no-repeat';
48 | vertex.style.backgroundPosition = 'center';
49 | vertex.style.backgroundSize = '90%';
50 | vertex.style.cursor = `url(${rainCursor}), pointer`;
51 | },
52 | cursorObstacle1: (vertex: HTMLDivElement) => {
53 | vertex.style.cursor = `url(${rainCursor}), pointer`;
54 | },
55 | obstacle2: (vertex: HTMLDivElement) => {
56 | vertex.style.backgroundColor = '';
57 | vertex.style.backgroundImage = `url(${cone})`;
58 | vertex.style.backgroundRepeat = 'no-repeat';
59 | vertex.style.backgroundPosition = 'center';
60 | vertex.style.backgroundSize = '80%';
61 | vertex.style.cursor = `url(${coneCursor}), pointer`;
62 | },
63 | cursorObstacle2: (vertex: HTMLDivElement) => {
64 | vertex.style.cursor = `url(${coneCursor}), pointer`;
65 | },
66 | obstacle3: (vertex: HTMLDivElement) => {
67 | vertex.style.backgroundColor = '';
68 | vertex.style.backgroundImage = `url(${hill})`;
69 | vertex.style.backgroundRepeat = 'no-repeat';
70 | vertex.style.backgroundPosition = 'center';
71 | vertex.style.backgroundSize = '70%';
72 | vertex.style.cursor = `url(${hillCursor}), pointer`;
73 | },
74 | cursorObstacle3: (vertex: HTMLDivElement) => {
75 | vertex.style.cursor = `url(${hillCursor}), pointer`;
76 | },
77 | unvisited: (vertex: HTMLDivElement) => {
78 | vertex.style.backgroundColor = '';
79 | vertex.style.backgroundImage = '';
80 | vertex.style.color = Colors.COLOR_WALL;
81 | vertex.style.cursor = `url(${carObstacleCursor}), pointer`;
82 | },
83 | visited: (vertex: HTMLDivElement) => {
84 | vertex.style.backgroundColor = Colors.COLOR_VISITED;
85 | },
86 | visiting: (vertex: HTMLDivElement) => {
87 | vertex.style.backgroundColor = Colors.COLOR_VISITING;
88 | },
89 | pathHorizontalStart: (vertex: HTMLDivElement) => {
90 | vertex.style.backgroundImage = `url(${car}), url(${roadHorizontal})`;
91 | vertex.style.backgroundRepeat = 'no-repeat, no-repeat';
92 | vertex.style.backgroundPosition = 'center, center';
93 | vertex.style.backgroundSize = '90%, cover';
94 | vertex.style.cursor = 'grab';
95 | },
96 | pathVerticalStart: (vertex: HTMLDivElement) => {
97 | vertex.style.backgroundImage = `url(${car}), url(${roadVertical})`;
98 | vertex.style.backgroundRepeat = 'no-repeat, no-repeat';
99 | vertex.style.backgroundPosition = 'center, center';
100 | vertex.style.backgroundSize = '90%, cover';
101 | vertex.style.cursor = 'grab';
102 | },
103 | pathHorizontalEnd: (vertex: HTMLDivElement) => {
104 | vertex.style.backgroundImage = `url(${location}), url(${roadHorizontal})`;
105 | vertex.style.backgroundRepeat = 'no-repeat, no-repeat';
106 | vertex.style.backgroundPosition = 'center, center';
107 | vertex.style.backgroundSize = '90%, cover';
108 | vertex.style.cursor = 'grab';
109 | },
110 | pathVerticalEnd: (vertex: HTMLDivElement) => {
111 | vertex.style.backgroundImage = `url(${location}), url(${roadVertical})`;
112 | vertex.style.backgroundRepeat = 'no-repeat, no-repeat';
113 | vertex.style.backgroundPosition = 'center, center';
114 | vertex.style.backgroundSize = '90%, cover';
115 | vertex.style.cursor = 'grab';
116 | },
117 | pathHorizontal: (vertex: HTMLDivElement) => {
118 | vertex.style.backgroundImage = `url(${roadHorizontal})`;
119 | vertex.style.backgroundRepeat = 'no-repeat';
120 | vertex.style.backgroundPosition = 'center';
121 | vertex.style.backgroundSize = 'cover';
122 | },
123 | pathVertical: (vertex: HTMLDivElement) => {
124 | vertex.style.backgroundImage = `url(${roadVertical})`;
125 | vertex.style.backgroundRepeat = 'no-repeat';
126 | vertex.style.backgroundPosition = 'center';
127 | vertex.style.backgroundSize = 'cover';
128 | },
129 | revertObstacle: (vertex: HTMLDivElement) => {
130 | vertex.style.backgroundColor = '';
131 | vertex.style.backgroundImage = '';
132 | },
133 | revertPath: (vertex: HTMLDivElement) => {
134 | vertex.style.backgroundImage = '';
135 | },
136 | bodyBackground: (body: HTMLBodyElement) => {
137 | body.style.background = '#fff';
138 | },
139 | heading: (heading: HTMLHeadingElement) => {
140 | heading.style.background = Colors.COLOR_VISITING;
141 | heading.style.backgroundClip = 'text';
142 | heading.style.webkitBackgroundClip = 'text';
143 | heading.style.webkitTextFillColor = 'transparent';
144 | heading.style.webkitTextStroke = '';
145 | },
146 | header: (div: HTMLDivElement) => {
147 | div.style.backgroundColor = Colors.COLOR_WALL;
148 | div.style.color = Colors.COLOR_VISITING;
149 | div.style.fontFamily = 'Montserrat, sans-serif';
150 | div.style.letterSpacing = '';
151 | div.style.fontSize = '';
152 | },
153 | controller: (div: HTMLDivElement) => {
154 | div.style.color = Colors.COLOR_VISITING;
155 | },
156 | button: (button: HTMLButtonElement) => {
157 | button.style.backgroundColor = 'transparent';
158 | button.style.color = Colors.COLOR_VISITING;
159 |
160 | if (!is_touch_device()) {
161 | button.addEventListener('mouseover', () => {
162 | button.style.backgroundColor = Colors.COLOR_VISITING;
163 | button.style.color = '#fff';
164 | });
165 |
166 | button.addEventListener('mouseout', () => {
167 | button.style.backgroundColor = 'transparent';
168 | button.style.color = Colors.COLOR_VISITING;
169 | });
170 | }
171 | },
172 | dropdown: (heading: HTMLHeadingElement) => {
173 | heading.style.backgroundColor = Colors.COLOR_VISITING;
174 | heading.style.color = '#fff';
175 | },
176 | options: (div: HTMLDivElement) => {
177 | div.style.backgroundColor = Colors.COLOR_WALL;
178 | },
179 | option: (li: HTMLLIElement) => {
180 | li.style.backgroundColor = 'transparent';
181 | li.style.color = Colors.COLOR_VISITING;
182 |
183 | if (!is_touch_device()) {
184 | li.addEventListener('mouseover', () => {
185 | li.style.backgroundColor = Colors.COLOR_VISITING;
186 | li.style.color = '#fff';
187 | });
188 |
189 | li.addEventListener('mouseout', () => {
190 | li.style.backgroundColor = 'transparent';
191 | li.style.color = Colors.COLOR_VISITING;
192 | });
193 | }
194 | },
195 | };
196 |
197 | export const carObstacleOptions: Obstacle = {
198 | wall: 'Car (Barrier)',
199 | obstacle1: 'Rain (Weight: 2)',
200 | obstacle2: 'Cone (Weight: 3)',
201 | obstacle3: 'Hill (Weight: 4)',
202 | };
203 |
--------------------------------------------------------------------------------
/src/utils/themes/hunterxhunter.ts:
--------------------------------------------------------------------------------
1 | import { GraphTheme, Obstacle, is_touch_device } from './index';
2 | import * as Colors from '../colors';
3 | import gon from './img/gon.png';
4 | import ging from './img/ging.png';
5 | import hisoka from './img/hisoka.png';
6 | import hisokaCursor from './img/hisokaCursor.png';
7 | import troupe from './img/troupe.png';
8 | import troupeCursor from './img/troupeCursor.png';
9 | import razor from './img/razor.png';
10 | import razorCursor from './img/razorCursor.png';
11 | import meruem from './img/meruem.png';
12 | import meruemCursor from './img/meruemCursor.png';
13 |
14 | export const hunterxhunterTheme: GraphTheme = {
15 | start: (vertex: HTMLDivElement) => {
16 | vertex.style.backgroundColor = '';
17 | vertex.style.backgroundImage = `url(${gon})`;
18 | vertex.style.backgroundRepeat = 'no-repeat';
19 | vertex.style.backgroundPosition = 'center';
20 | vertex.style.backgroundSize = '100%;';
21 | vertex.style.cursor = 'grab';
22 | },
23 | end: (vertex: HTMLDivElement) => {
24 | vertex.style.backgroundColor = '';
25 | vertex.style.backgroundImage = `url(${ging})`;
26 | vertex.style.backgroundRepeat = 'no-repeat';
27 | vertex.style.backgroundPosition = 'center';
28 | vertex.style.backgroundSize = '80%';
29 | vertex.style.cursor = 'grab';
30 | },
31 | wall: (vertex: HTMLDivElement) => {
32 | vertex.style.backgroundColor = '';
33 | vertex.style.backgroundImage = `url(${hisoka})`;
34 | vertex.style.backgroundRepeat = 'no-repeat';
35 | vertex.style.backgroundPosition = 'center';
36 | vertex.style.backgroundSize = '80%';
37 | vertex.style.cursor = `url(${hisokaCursor}), pointer`;
38 | },
39 | cursorWall: (vertex: HTMLDivElement) => {
40 | vertex.style.cursor = `url(${hisokaCursor}), pointer`;
41 | },
42 | obstacle1: (vertex: HTMLDivElement) => {
43 | vertex.style.backgroundColor = '';
44 | vertex.style.backgroundImage = `url(${troupe})`;
45 | vertex.style.backgroundRepeat = 'no-repeat';
46 | vertex.style.backgroundPosition = 'center';
47 | vertex.style.backgroundSize = '90%';
48 | vertex.style.cursor = `url(${troupeCursor}), pointer`;
49 | },
50 | cursorObstacle1: (vertex: HTMLDivElement) => {
51 | vertex.style.cursor = `url(${troupeCursor}), pointer`;
52 | },
53 | obstacle2: (vertex: HTMLDivElement) => {
54 | vertex.style.backgroundColor = '';
55 | vertex.style.backgroundImage = `url(${razor})`;
56 | vertex.style.backgroundRepeat = 'no-repeat';
57 | vertex.style.backgroundPosition = 'center';
58 | vertex.style.backgroundSize = '40%';
59 | vertex.style.cursor = `url(${razorCursor}), pointer`;
60 | },
61 | cursorObstacle2: (vertex: HTMLDivElement) => {
62 | vertex.style.cursor = `url(${razorCursor}), pointer`;
63 | },
64 | obstacle3: (vertex: HTMLDivElement) => {
65 | vertex.style.backgroundColor = '';
66 | vertex.style.backgroundImage = `url(${meruem})`;
67 | vertex.style.backgroundRepeat = 'no-repeat';
68 | vertex.style.backgroundPosition = 'center';
69 | vertex.style.backgroundSize = '70%';
70 | vertex.style.cursor = `url(${meruemCursor}), pointer`;
71 | },
72 | cursorObstacle3: (vertex: HTMLDivElement) => {
73 | vertex.style.cursor = `url(${meruemCursor}), pointer`;
74 | },
75 | unvisited: (vertex: HTMLDivElement) => {
76 | vertex.style.backgroundColor = '';
77 | vertex.style.backgroundImage = '';
78 | vertex.style.color = Colors.COLOR_HUNTERXHUNTER_GREEN;
79 | vertex.style.cursor = `url(${hisokaCursor}), pointer`;
80 | },
81 | visited: (vertex: HTMLDivElement) => {
82 | vertex.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_GREEN;
83 | },
84 | visiting: (vertex: HTMLDivElement) => {
85 | vertex.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE;
86 | },
87 | pathHorizontalStart: (vertex: HTMLDivElement) => {
88 | vertex.style.backgroundImage = `url(${gon})`;
89 | vertex.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE;
90 | },
91 | pathVerticalStart: (vertex: HTMLDivElement) => {
92 | vertex.style.backgroundImage = `url(${gon})`;
93 | vertex.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE;
94 | },
95 | pathHorizontalEnd: (vertex: HTMLDivElement) => {
96 | vertex.style.backgroundImage = `url(${ging})`;
97 | vertex.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE;
98 | },
99 | pathVerticalEnd: (vertex: HTMLDivElement) => {
100 | vertex.style.backgroundImage = `url(${ging})`;
101 | vertex.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE;
102 | },
103 | pathHorizontal: (vertex: HTMLDivElement) => {
104 | vertex.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE;
105 | },
106 | pathVertical: (vertex: HTMLDivElement) => {
107 | vertex.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE;
108 | },
109 | revertObstacle: (vertex: HTMLDivElement) => {
110 | vertex.style.backgroundColor = '';
111 | vertex.style.backgroundImage = '';
112 | },
113 | revertPath: (vertex: HTMLDivElement) => {
114 | vertex.style.backgroundColor = '';
115 | },
116 | bodyBackground: (body: HTMLBodyElement) => {
117 | body.style.background = `linear-gradient(to top, ${Colors.COLOR_HUNTERXHUNTER_DARK_BROWN}, ${Colors.COLOR_HUNTERXHUNTER_BROWN})`;
118 | },
119 | header: (div: HTMLDivElement) => {
120 | div.style.backgroundColor = 'transparent';
121 | div.style.color = Colors.COLOR_HUNTERXHUNTER_WHITE;
122 | div.style.fontFamily = 'Arial, sans-serif';
123 | div.style.letterSpacing = '';
124 | div.style.fontSize = '2rem';
125 | },
126 | heading: (heading: HTMLHeadingElement) => {
127 | heading.style.background = `linear-gradient(to top, ${Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE}, ${Colors.COLOR_HUNTERXHUNTER_LIGHT_ORANGE})`;
128 | heading.style.backgroundClip = 'text';
129 | heading.style.webkitBackgroundClip = 'text';
130 | heading.style.webkitTextFillColor = 'transparent';
131 | heading.style.webkitTextStroke = `4px ${Colors.COLOR_HUNTERXHUNTER_MAROON}`;
132 | },
133 | controller: (div: HTMLDivElement) => {
134 | div.style.color = Colors.COLOR_HUNTERXHUNTER_LIGHT_ORANGE;
135 | },
136 | button: (button: HTMLButtonElement) => {
137 | button.style.backgroundColor = 'transparent';
138 | button.style.color = Colors.COLOR_HUNTERXHUNTER_LIGHT_ORANGE;
139 |
140 | if (!is_touch_device()) {
141 | button.addEventListener('mouseover', () => {
142 | button.style.backgroundColor =
143 | Colors.COLOR_HUNTERXHUNTER_LIGHT_ORANGE;
144 | button.style.color = Colors.COLOR_HUNTERXHUNTER_WHITE;
145 | });
146 |
147 | button.addEventListener('mouseout', () => {
148 | button.style.backgroundColor = 'transparent';
149 | button.style.color = Colors.COLOR_HUNTERXHUNTER_LIGHT_ORANGE;
150 | });
151 | }
152 | },
153 | dropdown: (heading: HTMLHeadingElement) => {
154 | heading.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_GREEN;
155 | heading.style.color = Colors.COLOR_HUNTERXHUNTER_WHITE;
156 | },
157 | options: (div: HTMLDivElement) => {
158 | div.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_DARK_ORANGE;
159 | },
160 | option: (li: HTMLLIElement) => {
161 | li.style.backgroundColor = 'transparent';
162 | li.style.color = Colors.COLOR_HUNTERXHUNTER_WHITE;
163 |
164 | if (!is_touch_device()) {
165 | li.addEventListener('mouseover', () => {
166 | li.style.backgroundColor = Colors.COLOR_HUNTERXHUNTER_GREEN;
167 | li.style.color = Colors.COLOR_HUNTERXHUNTER_WHITE;
168 | });
169 |
170 | li.addEventListener('mouseout', () => {
171 | li.style.backgroundColor = 'transparent';
172 | li.style.color = Colors.COLOR_HUNTERXHUNTER_WHITE;
173 | });
174 | }
175 | },
176 | };
177 |
178 | export const hunterxhunterObstacleOptions: Obstacle = {
179 | wall: 'Hisoka (Barrier)',
180 | obstacle1: 'Phantom Troupe (Weight: 2)',
181 | obstacle2: 'Razor (Weight: 3)',
182 | obstacle3: 'Meruem (Weight: 4)',
183 | };
184 |
--------------------------------------------------------------------------------
/src/utils/themes/img/badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/badge.png
--------------------------------------------------------------------------------
/src/utils/themes/img/bigmom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/bigmom.png
--------------------------------------------------------------------------------
/src/utils/themes/img/bigmomCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/bigmomCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/blackbeard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/blackbeard.png
--------------------------------------------------------------------------------
/src/utils/themes/img/blackbeardCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/blackbeardCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/building.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/building.png
--------------------------------------------------------------------------------
/src/utils/themes/img/buildingCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/buildingCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/captainAmerica.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/captainAmerica.png
--------------------------------------------------------------------------------
/src/utils/themes/img/captainMarvel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/captainMarvel.png
--------------------------------------------------------------------------------
/src/utils/themes/img/car.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/car.png
--------------------------------------------------------------------------------
/src/utils/themes/img/carObstacle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/carObstacle.png
--------------------------------------------------------------------------------
/src/utils/themes/img/carObstacleCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/carObstacleCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/celtics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/celtics.png
--------------------------------------------------------------------------------
/src/utils/themes/img/celticsCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/celticsCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/chineseFood.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/chineseFood.png
--------------------------------------------------------------------------------
/src/utils/themes/img/chineseFoodCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/chineseFoodCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/clippers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/clippers.png
--------------------------------------------------------------------------------
/src/utils/themes/img/clippersCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/clippersCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/cone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/cone.png
--------------------------------------------------------------------------------
/src/utils/themes/img/coneCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/coneCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/gasStation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/gasStation.png
--------------------------------------------------------------------------------
/src/utils/themes/img/gasStationCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/gasStationCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/gauntlet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/gauntlet.png
--------------------------------------------------------------------------------
/src/utils/themes/img/ging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/ging.png
--------------------------------------------------------------------------------
/src/utils/themes/img/gon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/gon.png
--------------------------------------------------------------------------------
/src/utils/themes/img/groudon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/groudon.png
--------------------------------------------------------------------------------
/src/utils/themes/img/groudonCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/groudonCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/hill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/hill.png
--------------------------------------------------------------------------------
/src/utils/themes/img/hillCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/hillCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/hisoka.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/hisoka.png
--------------------------------------------------------------------------------
/src/utils/themes/img/hisokaCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/hisokaCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/holly.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/holly.jpg
--------------------------------------------------------------------------------
/src/utils/themes/img/ironMan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/ironMan.png
--------------------------------------------------------------------------------
/src/utils/themes/img/kaido.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/kaido.png
--------------------------------------------------------------------------------
/src/utils/themes/img/kaidoCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/kaidoCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/kyogre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/kyogre.png
--------------------------------------------------------------------------------
/src/utils/themes/img/kyogreCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/kyogreCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/lakers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/lakers.png
--------------------------------------------------------------------------------
/src/utils/themes/img/latias.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/latias.png
--------------------------------------------------------------------------------
/src/utils/themes/img/latiasCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/latiasCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/location.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/location.png
--------------------------------------------------------------------------------
/src/utils/themes/img/meruem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/meruem.png
--------------------------------------------------------------------------------
/src/utils/themes/img/meruemCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/meruemCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/michael.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/michael.png
--------------------------------------------------------------------------------
/src/utils/themes/img/phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/phone.png
--------------------------------------------------------------------------------
/src/utils/themes/img/phoneCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/phoneCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/pistons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/pistons.png
--------------------------------------------------------------------------------
/src/utils/themes/img/pistonsCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/pistonsCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/pokemonPlayer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/pokemonPlayer.png
--------------------------------------------------------------------------------
/src/utils/themes/img/rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/rain.png
--------------------------------------------------------------------------------
/src/utils/themes/img/rainCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/rainCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/rayquaza.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/rayquaza.png
--------------------------------------------------------------------------------
/src/utils/themes/img/rayquazaCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/rayquazaCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/razor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/razor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/razorCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/razorCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/road-horizontal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/road-horizontal.jpg
--------------------------------------------------------------------------------
/src/utils/themes/img/road-vertical.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/road-vertical.jpg
--------------------------------------------------------------------------------
/src/utils/themes/img/shanks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/shanks.png
--------------------------------------------------------------------------------
/src/utils/themes/img/shanksCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/shanksCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/spurs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/spurs.png
--------------------------------------------------------------------------------
/src/utils/themes/img/strawhat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/strawhat.png
--------------------------------------------------------------------------------
/src/utils/themes/img/sunny.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/sunny.png
--------------------------------------------------------------------------------
/src/utils/themes/img/thanos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/thanos.png
--------------------------------------------------------------------------------
/src/utils/themes/img/thor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/thor.png
--------------------------------------------------------------------------------
/src/utils/themes/img/treasure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/treasure.png
--------------------------------------------------------------------------------
/src/utils/themes/img/tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/tree.png
--------------------------------------------------------------------------------
/src/utils/themes/img/trophy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/trophy.png
--------------------------------------------------------------------------------
/src/utils/themes/img/troupe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/troupe.png
--------------------------------------------------------------------------------
/src/utils/themes/img/troupeCursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/princhcanal/pathfinding-visualizer/eaf2556eeb8fdd077ba05dca72245c4994f143fa/src/utils/themes/img/troupeCursor.png
--------------------------------------------------------------------------------
/src/utils/themes/index.ts:
--------------------------------------------------------------------------------
1 | import { carTheme } from './car';
2 | import { avengersTheme } from './avengers';
3 | import { carObstacleOptions } from './car';
4 | import { avengersObstacleOptions } from './avengers';
5 | import { onePieceObstacleOptions, onePieceTheme } from './onePiece';
6 | import { lakersObstacleOptions, lakersTheme } from './lakers';
7 | import { pokemonTheme, pokemonObstacleOptions } from './pokemon';
8 | import { theOfficeTheme, theOfficeObstacleOptions } from './theOffice';
9 | import {
10 | hunterxhunterTheme,
11 | hunterxhunterObstacleOptions,
12 | } from './hunterxhunter';
13 |
14 | type GraphManipulation = (vertex: HTMLDivElement) => void;
15 | type BodyManipulation = (body: HTMLBodyElement) => void;
16 | type DivManipulation = (div: HTMLDivElement) => void;
17 | type ButtonManipulation = (button: HTMLButtonElement) => void;
18 | type HeadingManipulation = (heading: HTMLHeadingElement) => void;
19 | type ListItemManipulation = (li: HTMLLIElement) => void;
20 | type Themes = { [key: string]: GraphTheme };
21 | type Obstacles = { [key: string]: Obstacle };
22 |
23 | export type GraphTheme = {
24 | start: GraphManipulation;
25 | end: GraphManipulation;
26 | wall: GraphManipulation;
27 | obstacle1: GraphManipulation;
28 | obstacle2: GraphManipulation;
29 | obstacle3: GraphManipulation;
30 | unvisited: GraphManipulation;
31 | visited: GraphManipulation;
32 | visiting: GraphManipulation;
33 | pathHorizontalStart: GraphManipulation;
34 | pathVerticalStart: GraphManipulation;
35 | pathHorizontalEnd: GraphManipulation;
36 | pathVerticalEnd: GraphManipulation;
37 | pathHorizontal: GraphManipulation;
38 | pathVertical: GraphManipulation;
39 |
40 | revertObstacle: GraphManipulation;
41 | revertPath: GraphManipulation;
42 |
43 | bodyBackground: BodyManipulation;
44 | header: DivManipulation;
45 | heading: HeadingManipulation;
46 | controller: DivManipulation;
47 | button: ButtonManipulation;
48 | dropdown: HeadingManipulation;
49 | options: DivManipulation;
50 | option: ListItemManipulation;
51 | cursorWall: GraphManipulation;
52 | cursorObstacle1: GraphManipulation;
53 | cursorObstacle2: GraphManipulation;
54 | cursorObstacle3: GraphManipulation;
55 | };
56 |
57 | export type Obstacle = {
58 | wall: string;
59 | obstacle1: string;
60 | obstacle2: string;
61 | obstacle3: string;
62 | };
63 |
64 | export const themes: Themes = {
65 | car: carTheme,
66 | avengers: avengersTheme,
67 | onePiece: onePieceTheme,
68 | lakers: lakersTheme,
69 | pokemon: pokemonTheme,
70 | theOffice: theOfficeTheme,
71 | hunterxhunter: hunterxhunterTheme,
72 | };
73 |
74 | export const themeOptions = {
75 | car: 'Car',
76 | avengers: 'Avengers',
77 | onePiece: 'One Piece',
78 | lakers: 'Lakers',
79 | pokemon: 'Pokemon',
80 | theOffice: 'The Office',
81 | hunterxhunter: 'HunterxHunter',
82 | };
83 |
84 | export const obstacleOptions: Obstacles = {
85 | car: carObstacleOptions,
86 | avengers: avengersObstacleOptions,
87 | onePiece: onePieceObstacleOptions,
88 | lakers: lakersObstacleOptions,
89 | pokemon: pokemonObstacleOptions,
90 | theOffice: theOfficeObstacleOptions,
91 | hunterxhunter: hunterxhunterObstacleOptions,
92 | };
93 |
94 | export const is_touch_device = () => {
95 | try {
96 | document.createEvent('TouchEvent');
97 | return true;
98 | } catch (e) {
99 | return false;
100 | }
101 | };
102 |
--------------------------------------------------------------------------------
/src/utils/themes/lakers.ts:
--------------------------------------------------------------------------------
1 | import { GraphTheme, Obstacle, is_touch_device } from './index';
2 | import * as Colors from '../colors';
3 | import lakers from './img/lakers.png';
4 | import trophy from './img/trophy.png';
5 | import celtics from './img/celtics.png';
6 | import celticsCursor from './img/celticsCursor.png';
7 | import pistons from './img/pistons.png';
8 | import pistonsCursor from './img/pistonsCursor.png';
9 | import clippers from './img/clippers.png';
10 | import clippersCursor from './img/clippersCursor.png';
11 | import spurs from './img/spurs.png';
12 |
13 | export const lakersTheme: GraphTheme = {
14 | start: (vertex: HTMLDivElement) => {
15 | vertex.style.backgroundColor = '';
16 | vertex.style.backgroundImage = `url(${lakers})`;
17 | vertex.style.backgroundRepeat = 'no-repeat';
18 | vertex.style.backgroundPosition = 'center';
19 | vertex.style.backgroundSize = '100%';
20 | vertex.style.cursor = 'grab';
21 | },
22 | end: (vertex: HTMLDivElement) => {
23 | vertex.style.backgroundColor = '';
24 | vertex.style.backgroundImage = `url(${trophy})`;
25 | vertex.style.backgroundRepeat = 'no-repeat';
26 | vertex.style.backgroundPosition = 'center';
27 | vertex.style.backgroundSize = '40%';
28 | vertex.style.cursor = 'grab';
29 | },
30 | wall: (vertex: HTMLDivElement) => {
31 | vertex.style.backgroundColor = '';
32 | vertex.style.backgroundImage = `url(${celtics})`;
33 | vertex.style.backgroundRepeat = 'no-repeat';
34 | vertex.style.backgroundPosition = 'center';
35 | vertex.style.backgroundSize = '90%';
36 | vertex.style.cursor = `url(${celticsCursor}), pointer`;
37 | },
38 | cursorWall: (vertex: HTMLDivElement) => {
39 | vertex.style.cursor = `url(${celticsCursor}), pointer`;
40 | },
41 | obstacle1: (vertex: HTMLDivElement) => {
42 | vertex.style.backgroundColor = '';
43 | vertex.style.backgroundImage = `url(${pistons})`;
44 | vertex.style.backgroundRepeat = 'no-repeat';
45 | vertex.style.backgroundPosition = 'center';
46 | vertex.style.backgroundSize = '140%';
47 | vertex.style.cursor = `url(${pistonsCursor}), pointer`;
48 | },
49 | cursorObstacle1: (vertex: HTMLDivElement) => {
50 | vertex.style.cursor = `url(${pistonsCursor}), pointer`;
51 | },
52 | obstacle2: (vertex: HTMLDivElement) => {
53 | vertex.style.backgroundColor = '';
54 | vertex.style.backgroundImage = `url(${clippers})`;
55 | vertex.style.backgroundRepeat = 'no-repeat';
56 | vertex.style.backgroundPosition = 'center';
57 | vertex.style.backgroundSize = '90%';
58 | vertex.style.cursor = `url(${clippersCursor}), pointer`;
59 | },
60 | cursorObstacle2: (vertex: HTMLDivElement) => {
61 | vertex.style.cursor = `url(${clippersCursor}), pointer`;
62 | },
63 | obstacle3: (vertex: HTMLDivElement) => {
64 | vertex.style.backgroundColor = '';
65 | vertex.style.backgroundImage = `url(${spurs})`;
66 | vertex.style.backgroundRepeat = 'no-repeat';
67 | vertex.style.backgroundPosition = 'center';
68 | vertex.style.backgroundSize = '80%';
69 | vertex.style.cursor = `url(${spurs}), pointer`;
70 | },
71 | cursorObstacle3: (vertex: HTMLDivElement) => {
72 | vertex.style.cursor = `url(${spurs}), pointer`;
73 | },
74 | unvisited: (vertex: HTMLDivElement) => {
75 | vertex.style.backgroundColor = '';
76 | vertex.style.backgroundImage = '';
77 | vertex.style.color = Colors.COLOR_LAKERS_GOLD;
78 | vertex.style.cursor = `url(${celticsCursor}), pointer`;
79 | },
80 | visited: (vertex: HTMLDivElement) => {
81 | vertex.style.backgroundColor = Colors.COLOR_LAKERS_GOLD;
82 | },
83 | visiting: (vertex: HTMLDivElement) => {
84 | vertex.style.backgroundColor = Colors.COLOR_LAKERS_BLACK;
85 | },
86 | pathHorizontalStart: (vertex: HTMLDivElement) => {
87 | vertex.style.backgroundImage = `url(${lakers})`;
88 | vertex.style.backgroundColor = Colors.COLOR_LAKERS_WHITE;
89 | vertex.style.cursor = 'grab';
90 | },
91 | pathVerticalStart: (vertex: HTMLDivElement) => {
92 | vertex.style.backgroundImage = `url(${lakers})`;
93 | vertex.style.backgroundColor = Colors.COLOR_LAKERS_WHITE;
94 | vertex.style.cursor = 'grab';
95 | },
96 | pathHorizontalEnd: (vertex: HTMLDivElement) => {
97 | vertex.style.backgroundImage = `url(${trophy})`;
98 | vertex.style.backgroundColor = Colors.COLOR_LAKERS_WHITE;
99 | vertex.style.cursor = 'grab';
100 | },
101 | pathVerticalEnd: (vertex: HTMLDivElement) => {
102 | vertex.style.backgroundImage = `url(${trophy})`;
103 | vertex.style.backgroundColor = Colors.COLOR_LAKERS_WHITE;
104 | vertex.style.cursor = 'grab';
105 | },
106 | pathHorizontal: (vertex: HTMLDivElement) => {
107 | vertex.style.backgroundColor = Colors.COLOR_LAKERS_WHITE;
108 | },
109 | pathVertical: (vertex: HTMLDivElement) => {
110 | vertex.style.backgroundColor = Colors.COLOR_LAKERS_WHITE;
111 | },
112 | revertObstacle: (vertex: HTMLDivElement) => {
113 | vertex.style.backgroundColor = '';
114 | vertex.style.backgroundImage = '';
115 | },
116 | revertPath: (vertex: HTMLDivElement) => {
117 | vertex.style.backgroundColor = '';
118 | },
119 | bodyBackground: (body: HTMLBodyElement) => {
120 | body.style.background = Colors.COLOR_LAKERS_PURPLE;
121 | },
122 | header: (div: HTMLDivElement) => {
123 | div.style.backgroundColor = Colors.COLOR_LAKERS_PURPLE;
124 | div.style.color = Colors.COLOR_LAKERS_GOLD;
125 | div.style.fontFamily = 'Lakers, sans-serif';
126 | div.style.letterSpacing = '';
127 | div.style.fontSize = '2rem';
128 | },
129 | heading: (heading: HTMLHeadingElement) => {
130 | heading.style.background = Colors.COLOR_LAKERS_GOLD;
131 | heading.style.backgroundClip = 'text';
132 | heading.style.webkitBackgroundClip = 'text';
133 | heading.style.webkitTextFillColor = 'transparent';
134 | heading.style.webkitTextStroke = `3px ${Colors.COLOR_LAKERS_BLACK}`;
135 | },
136 | controller: (div: HTMLDivElement) => {
137 | div.style.color = Colors.COLOR_LAKERS_GOLD;
138 | },
139 | button: (button: HTMLButtonElement) => {
140 | button.style.backgroundColor = 'transparent';
141 | button.style.color = Colors.COLOR_LAKERS_GOLD;
142 |
143 | if (!is_touch_device()) {
144 | button.addEventListener('mouseover', () => {
145 | button.style.backgroundColor = Colors.COLOR_LAKERS_GOLD;
146 | button.style.color = Colors.COLOR_LAKERS_PURPLE;
147 | });
148 |
149 | button.addEventListener('mouseout', () => {
150 | button.style.backgroundColor = 'transparent';
151 | button.style.color = Colors.COLOR_LAKERS_GOLD;
152 | });
153 | }
154 | },
155 | dropdown: (heading: HTMLHeadingElement) => {
156 | heading.style.backgroundColor = Colors.COLOR_LAKERS_GOLD;
157 | heading.style.color = Colors.COLOR_LAKERS_PURPLE;
158 | },
159 | options: (div: HTMLDivElement) => {
160 | div.style.backgroundColor = Colors.COLOR_LAKERS_BLACK;
161 | },
162 | option: (li: HTMLLIElement) => {
163 | li.style.backgroundColor = 'transparent';
164 | li.style.color = Colors.COLOR_LAKERS_PURPLE;
165 |
166 | if (!is_touch_device()) {
167 | li.addEventListener('mouseover', () => {
168 | li.style.backgroundColor = Colors.COLOR_LAKERS_GOLD;
169 | li.style.color = Colors.COLOR_LAKERS_PURPLE;
170 | });
171 |
172 | li.addEventListener('mouseout', () => {
173 | li.style.backgroundColor = 'transparent';
174 | li.style.color = Colors.COLOR_LAKERS_PURPLE;
175 | });
176 | }
177 | },
178 | };
179 |
180 | export const lakersObstacleOptions: Obstacle = {
181 | wall: 'Celtics (Barrier)',
182 | obstacle1: 'Pistons (Weight: 2)',
183 | obstacle2: 'Clippers (Weight: 3)',
184 | obstacle3: 'Spurs (Weight: 4)',
185 | };
186 |
--------------------------------------------------------------------------------
/src/utils/themes/onePiece.ts:
--------------------------------------------------------------------------------
1 | import { GraphTheme, Obstacle, is_touch_device } from './index';
2 | import * as Colors from '../colors';
3 | import strawhat from './img/strawhat.png';
4 | import treasure from './img/treasure.png';
5 | import kaido from './img/kaido.png';
6 | import kaidoCursor from './img/kaidoCursor.png';
7 | import bigmom from './img/bigmom.png';
8 | import bigmomCursor from './img/bigmomCursor.png';
9 | import shanks from './img/shanks.png';
10 | import shanksCursor from './img/shanksCursor.png';
11 | import blackbeard from './img/blackbeard.png';
12 | import blackbeardCursor from './img/blackbeardCursor.png';
13 | import sunny from './img/sunny.png';
14 |
15 | export const onePieceTheme: GraphTheme = {
16 | start: (vertex: HTMLDivElement) => {
17 | vertex.style.backgroundColor = '';
18 | vertex.style.backgroundImage = `url(${strawhat})`;
19 | vertex.style.backgroundRepeat = 'no-repeat';
20 | vertex.style.backgroundPosition = 'center';
21 | vertex.style.backgroundSize = '100%';
22 | vertex.style.cursor = 'grab';
23 | },
24 | end: (vertex: HTMLDivElement) => {
25 | vertex.style.backgroundColor = '';
26 | vertex.style.backgroundImage = `url(${treasure})`;
27 | vertex.style.backgroundRepeat = 'no-repeat';
28 | vertex.style.backgroundPosition = 'center';
29 | vertex.style.backgroundSize = '100%';
30 | vertex.style.cursor = 'grab';
31 | },
32 | wall: (vertex: HTMLDivElement) => {
33 | vertex.style.backgroundColor = '';
34 | vertex.style.backgroundImage = `url(${kaido})`;
35 | vertex.style.backgroundRepeat = 'no-repeat';
36 | vertex.style.backgroundPosition = 'center';
37 | vertex.style.backgroundSize = '100%';
38 | vertex.style.cursor = `url(${kaidoCursor}), pointer`;
39 | },
40 | cursorWall: (vertex: HTMLDivElement) => {
41 | vertex.style.cursor = `url(${kaidoCursor}), pointer`;
42 | },
43 | obstacle1: (vertex: HTMLDivElement) => {
44 | vertex.style.backgroundColor = '';
45 | vertex.style.backgroundImage = `url(${bigmom})`;
46 | vertex.style.backgroundRepeat = 'no-repeat';
47 | vertex.style.backgroundPosition = 'center';
48 | vertex.style.backgroundSize = '100%';
49 | vertex.style.cursor = `url(${bigmomCursor}), pointer`;
50 | },
51 | cursorObstacle1: (vertex: HTMLDivElement) => {
52 | vertex.style.cursor = `url(${bigmomCursor}), pointer`;
53 | },
54 | obstacle2: (vertex: HTMLDivElement) => {
55 | vertex.style.backgroundColor = '';
56 | vertex.style.backgroundImage = `url(${shanks})`;
57 | vertex.style.backgroundRepeat = 'no-repeat';
58 | vertex.style.backgroundPosition = 'center';
59 | vertex.style.backgroundSize = '100%';
60 | vertex.style.cursor = `url(${shanksCursor}), pointer`;
61 | },
62 | cursorObstacle2: (vertex: HTMLDivElement) => {
63 | vertex.style.cursor = `url(${shanksCursor}), pointer`;
64 | },
65 | obstacle3: (vertex: HTMLDivElement) => {
66 | vertex.style.backgroundColor = '';
67 | vertex.style.backgroundImage = `url(${blackbeard})`;
68 | vertex.style.backgroundRepeat = 'no-repeat';
69 | vertex.style.backgroundPosition = 'center';
70 | vertex.style.backgroundSize = '80%';
71 | vertex.style.cursor = `url(${blackbeardCursor}), pointer`;
72 | },
73 | cursorObstacle3: (vertex: HTMLDivElement) => {
74 | vertex.style.cursor = `url(${blackbeardCursor}), pointer`;
75 | },
76 | unvisited: (vertex: HTMLDivElement) => {
77 | vertex.style.backgroundColor = '';
78 | vertex.style.backgroundImage = '';
79 | vertex.style.color = Colors.COLOR_ONEPIECE_RED;
80 | vertex.style.cursor = `url(${kaidoCursor}), pointer`;
81 | },
82 | visited: (vertex: HTMLDivElement) => {
83 | vertex.style.backgroundColor = Colors.COLOR_ONEPIECE_YELLOW;
84 | },
85 | visiting: (vertex: HTMLDivElement) => {
86 | vertex.style.backgroundColor = Colors.COLOR_ONEPIECE_RED;
87 | },
88 | pathHorizontalStart: (vertex: HTMLDivElement) => {
89 | vertex.style.backgroundImage = `url(${strawhat})`;
90 | vertex.style.backgroundColor = Colors.COLOR_ONEPIECE_RED;
91 | },
92 | pathVerticalStart: (vertex: HTMLDivElement) => {
93 | vertex.style.backgroundImage = `url(${strawhat})`;
94 | vertex.style.backgroundColor = Colors.COLOR_ONEPIECE_RED;
95 | },
96 | pathHorizontalEnd: (vertex: HTMLDivElement) => {
97 | vertex.style.backgroundImage = `url(${treasure})`;
98 | vertex.style.backgroundColor = Colors.COLOR_ONEPIECE_RED;
99 | },
100 | pathVerticalEnd: (vertex: HTMLDivElement) => {
101 | vertex.style.backgroundImage = `url(${treasure})`;
102 | vertex.style.backgroundColor = Colors.COLOR_ONEPIECE_RED;
103 | },
104 | pathHorizontal: (vertex: HTMLDivElement) => {
105 | vertex.style.backgroundImage = `url(${sunny})`;
106 | vertex.style.backgroundColor = Colors.COLOR_ONEPIECE_RED;
107 | vertex.style.backgroundRepeat = 'no-repeat';
108 | vertex.style.backgroundPosition = 'center';
109 | vertex.style.backgroundSize = 'cover';
110 | },
111 | pathVertical: (vertex: HTMLDivElement) => {
112 | vertex.style.backgroundImage = `url(${sunny})`;
113 | vertex.style.backgroundColor = Colors.COLOR_ONEPIECE_RED;
114 | vertex.style.backgroundRepeat = 'no-repeat';
115 | vertex.style.backgroundPosition = 'center';
116 | vertex.style.backgroundSize = 'cover';
117 | },
118 | revertObstacle: (vertex: HTMLDivElement) => {
119 | vertex.style.backgroundColor = '';
120 | vertex.style.backgroundImage = '';
121 | },
122 | revertPath: (vertex: HTMLDivElement) => {
123 | vertex.style.backgroundImage = '';
124 | },
125 | bodyBackground: (body: HTMLBodyElement) => {
126 | body.style.background = `linear-gradient(to top right, ${Colors.COLOR_ONEPIECE_DARKBLUE}, ${Colors.COLOR_ONEPIECE_LIGHTBLUE})`;
127 | },
128 | header: (div: HTMLDivElement) => {
129 | div.style.backgroundColor = 'transparent';
130 | div.style.color = Colors.COLOR_ONEPIECE_DARKBLUE;
131 | div.style.fontFamily = 'One Piece, sans-serif';
132 | div.style.letterSpacing = '1rem';
133 | div.style.fontSize = '2rem';
134 | },
135 | heading: (heading: HTMLHeadingElement) => {
136 | heading.style.background = `linear-gradient(to top, ${Colors.COLOR_ONEPIECE_DARKBLUE}, ${Colors.COLOR_ONEPIECE_LIGHTBLUE})`;
137 | heading.style.backgroundClip = 'text';
138 | heading.style.webkitBackgroundClip = 'text';
139 | heading.style.webkitTextFillColor = 'transparent';
140 | heading.style.webkitTextStroke = `2px ${Colors.COLOR_ONEPIECE_BLACK}`;
141 | },
142 | controller: (div: HTMLDivElement) => {
143 | div.style.color = Colors.COLOR_ONEPIECE_DARKBLUE;
144 | },
145 | button: (button: HTMLButtonElement) => {
146 | button.style.backgroundColor = 'transparent';
147 | button.style.color = Colors.COLOR_ONEPIECE_DARKBLUE;
148 |
149 | if (!is_touch_device()) {
150 | button.addEventListener('mouseover', () => {
151 | button.style.backgroundColor = Colors.COLOR_ONEPIECE_DARKBLUE;
152 | button.style.color = Colors.COLOR_ONEPIECE_YELLOW;
153 | });
154 |
155 | button.addEventListener('mouseout', () => {
156 | button.style.backgroundColor = 'transparent';
157 | button.style.color = Colors.COLOR_ONEPIECE_DARKBLUE;
158 | });
159 | }
160 | },
161 | dropdown: (heading: HTMLHeadingElement) => {
162 | heading.style.backgroundColor = Colors.COLOR_ONEPIECE_DARKBLUE;
163 | heading.style.color = Colors.COLOR_ONEPIECE_YELLOW;
164 | },
165 | options: (div: HTMLDivElement) => {
166 | div.style.backgroundColor = Colors.COLOR_ONEPIECE_DARKBLUE;
167 | },
168 | option: (li: HTMLLIElement) => {
169 | li.style.backgroundColor = 'transparent';
170 | li.style.color = Colors.COLOR_ONEPIECE_YELLOW;
171 |
172 | if (!is_touch_device()) {
173 | li.addEventListener('mouseover', () => {
174 | li.style.backgroundColor = Colors.COLOR_ONEPIECE_YELLOW;
175 | li.style.color = Colors.COLOR_ONEPIECE_DARKBLUE;
176 | });
177 |
178 | li.addEventListener('mouseout', () => {
179 | li.style.backgroundColor = 'transparent';
180 | li.style.color = Colors.COLOR_ONEPIECE_YELLOW;
181 | });
182 | }
183 | },
184 | };
185 |
186 | export const onePieceObstacleOptions: Obstacle = {
187 | wall: 'Kaido (Barrier)',
188 | obstacle1: 'Big Mom (Weight: 2)',
189 | obstacle2: 'Shanks (Weight: 3)',
190 | obstacle3: 'Blackbeard (Weight: 4)',
191 | };
192 |
--------------------------------------------------------------------------------
/src/utils/themes/pokemon.ts:
--------------------------------------------------------------------------------
1 | import { GraphTheme, Obstacle, is_touch_device } from './index';
2 | import * as Colors from '../colors';
3 | import pokemonPlayer from './img/pokemonPlayer.png';
4 | import badge from './img/badge.png';
5 | import rayquaza from './img/rayquaza.png';
6 | import rayquazaCursor from './img/rayquazaCursor.png';
7 | import groudon from './img/groudon.png';
8 | import groudonCursor from './img/groudonCursor.png';
9 | import kyogre from './img/kyogre.png';
10 | import kyogreCursor from './img/kyogreCursor.png';
11 | import latias from './img/latias.png';
12 | import latiasCursor from './img/latiasCursor.png';
13 |
14 | export const pokemonTheme: GraphTheme = {
15 | start: (vertex: HTMLDivElement) => {
16 | vertex.style.backgroundColor = '';
17 | vertex.style.backgroundImage = `url(${pokemonPlayer})`;
18 | vertex.style.backgroundRepeat = 'no-repeat';
19 | vertex.style.backgroundPosition = 'center';
20 | vertex.style.backgroundSize = '50%';
21 | vertex.style.cursor = 'grab';
22 | },
23 | end: (vertex: HTMLDivElement) => {
24 | vertex.style.backgroundColor = '';
25 | vertex.style.backgroundImage = `url(${badge})`;
26 | vertex.style.backgroundRepeat = 'no-repeat';
27 | vertex.style.backgroundPosition = 'center';
28 | vertex.style.backgroundSize = '120%';
29 | vertex.style.cursor = 'grab';
30 | },
31 | wall: (vertex: HTMLDivElement) => {
32 | vertex.style.backgroundColor = '';
33 | vertex.style.backgroundImage = `url(${rayquaza})`;
34 | vertex.style.backgroundRepeat = 'no-repeat';
35 | vertex.style.backgroundPosition = 'center';
36 | vertex.style.backgroundSize = '80%';
37 | vertex.style.cursor = `url(${rayquazaCursor}), pointer`;
38 | },
39 | cursorWall: (vertex: HTMLDivElement) => {
40 | vertex.style.cursor = `url(${rayquazaCursor}), pointer`;
41 | },
42 | obstacle1: (vertex: HTMLDivElement) => {
43 | vertex.style.backgroundColor = '';
44 | vertex.style.backgroundImage = `url(${groudon})`;
45 | vertex.style.backgroundRepeat = 'no-repeat';
46 | vertex.style.backgroundPosition = 'center';
47 | vertex.style.backgroundSize = '100%';
48 | vertex.style.cursor = `url(${groudonCursor}), pointer`;
49 | },
50 | cursorObstacle1: (vertex: HTMLDivElement) => {
51 | vertex.style.cursor = `url(${groudonCursor}), pointer`;
52 | },
53 | obstacle2: (vertex: HTMLDivElement) => {
54 | vertex.style.backgroundColor = '';
55 | vertex.style.backgroundImage = `url(${kyogre})`;
56 | vertex.style.backgroundRepeat = 'no-repeat';
57 | vertex.style.backgroundPosition = 'center';
58 | vertex.style.backgroundSize = '100%';
59 | vertex.style.cursor = `url(${kyogreCursor}), pointer`;
60 | },
61 | cursorObstacle2: (vertex: HTMLDivElement) => {
62 | vertex.style.cursor = `url(${kyogreCursor}), pointer`;
63 | },
64 | obstacle3: (vertex: HTMLDivElement) => {
65 | vertex.style.backgroundColor = '';
66 | vertex.style.backgroundImage = `url(${latias})`;
67 | vertex.style.backgroundRepeat = 'no-repeat';
68 | vertex.style.backgroundPosition = 'center';
69 | vertex.style.backgroundSize = '100%';
70 | vertex.style.cursor = `url(${latiasCursor}), pointer`;
71 | },
72 | cursorObstacle3: (vertex: HTMLDivElement) => {
73 | vertex.style.cursor = `url(${latiasCursor}), pointer`;
74 | },
75 | unvisited: (vertex: HTMLDivElement) => {
76 | vertex.style.backgroundColor = '';
77 | vertex.style.backgroundImage = '';
78 | vertex.style.color = Colors.COLOR_POKEMON_BLUE;
79 | vertex.style.cursor = `url(${rayquazaCursor}), pointer`;
80 | },
81 | visited: (vertex: HTMLDivElement) => {
82 | vertex.style.backgroundColor = Colors.COLOR_POKEMON_BLUE;
83 | },
84 | visiting: (vertex: HTMLDivElement) => {
85 | vertex.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
86 | },
87 | pathHorizontalStart: (vertex: HTMLDivElement) => {
88 | vertex.style.backgroundImage = `url(${pokemonPlayer})`;
89 | vertex.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
90 | },
91 | pathVerticalStart: (vertex: HTMLDivElement) => {
92 | vertex.style.backgroundImage = `url(${pokemonPlayer})`;
93 | vertex.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
94 | },
95 | pathHorizontalEnd: (vertex: HTMLDivElement) => {
96 | vertex.style.backgroundImage = `url(${badge})`;
97 | vertex.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
98 | },
99 | pathVerticalEnd: (vertex: HTMLDivElement) => {
100 | vertex.style.backgroundImage = `url(${badge})`;
101 | vertex.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
102 | },
103 | pathHorizontal: (vertex: HTMLDivElement) => {
104 | vertex.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
105 | },
106 | pathVertical: (vertex: HTMLDivElement) => {
107 | vertex.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
108 | },
109 | revertObstacle: (vertex: HTMLDivElement) => {
110 | vertex.style.backgroundColor = '';
111 | vertex.style.backgroundImage = '';
112 | },
113 | revertPath: (vertex: HTMLDivElement) => {
114 | vertex.style.backgroundColor = '';
115 | },
116 | bodyBackground: (body: HTMLBodyElement) => {
117 | body.style.background = `linear-gradient(to top, ${Colors.COLOR_POKEMON_EMERALD}, ${Colors.COLOR_POKEMON_BLUE})`;
118 | // body.style.background = `linear-gradient(to top, ${Colors.COLOR_POKEMON_BLUE}, ${Colors.COLOR_POKEMON_EMERALD})`;
119 | },
120 | header: (div: HTMLDivElement) => {
121 | div.style.backgroundColor = 'transparent';
122 | div.style.color = Colors.COLOR_POKEMON_YELLOW;
123 | div.style.fontFamily = 'Pokemon, sans-serif';
124 | div.style.letterSpacing = '0.5rem';
125 | div.style.fontSize = '2rem';
126 | },
127 | heading: (heading: HTMLHeadingElement) => {
128 | heading.style.background = Colors.COLOR_POKEMON_YELLOW;
129 | heading.style.backgroundClip = 'text';
130 | heading.style.webkitBackgroundClip = 'text';
131 | heading.style.webkitTextFillColor = 'transparent';
132 | heading.style.webkitTextStroke = `6px ${Colors.COLOR_POKEMON_BLUE}`;
133 | },
134 | controller: (div: HTMLDivElement) => {
135 | div.style.color = Colors.COLOR_POKEMON_YELLOW;
136 | },
137 | button: (button: HTMLButtonElement) => {
138 | button.style.backgroundColor = 'transparent';
139 | button.style.color = Colors.COLOR_POKEMON_YELLOW;
140 |
141 | if (!is_touch_device()) {
142 | button.addEventListener('mouseover', () => {
143 | button.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
144 | button.style.color = Colors.COLOR_POKEMON_EMERALD;
145 | });
146 |
147 | button.addEventListener('mouseout', () => {
148 | button.style.backgroundColor = 'transparent';
149 | button.style.color = Colors.COLOR_POKEMON_YELLOW;
150 | });
151 | }
152 | },
153 | dropdown: (heading: HTMLHeadingElement) => {
154 | heading.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
155 | heading.style.color = Colors.COLOR_POKEMON_EMERALD;
156 | },
157 | options: (div: HTMLDivElement) => {
158 | div.style.backgroundColor = Colors.COLOR_POKEMON_EMERALD;
159 | },
160 | option: (li: HTMLLIElement) => {
161 | li.style.backgroundColor = 'transparent';
162 | li.style.color = Colors.COLOR_POKEMON_YELLOW;
163 |
164 | if (!is_touch_device()) {
165 | li.addEventListener('mouseover', () => {
166 | li.style.backgroundColor = Colors.COLOR_POKEMON_YELLOW;
167 | li.style.color = Colors.COLOR_POKEMON_EMERALD;
168 | });
169 |
170 | li.addEventListener('mouseout', () => {
171 | li.style.backgroundColor = 'transparent';
172 | li.style.color = Colors.COLOR_POKEMON_YELLOW;
173 | });
174 | }
175 | },
176 | };
177 |
178 | export const pokemonObstacleOptions: Obstacle = {
179 | wall: 'Rayquaza (Barrier)',
180 | obstacle1: 'Groudon (Weight: 2)',
181 | obstacle2: 'Kyogre (Weight: 3)',
182 | obstacle3: 'Latias (Weight: 4)',
183 | };
184 |
--------------------------------------------------------------------------------
/src/utils/themes/theOffice.ts:
--------------------------------------------------------------------------------
1 | import { GraphTheme, Obstacle, is_touch_device } from './index';
2 | import * as Colors from '../colors';
3 | import holly from './img/holly.jpg';
4 | import michael from './img/michael.png';
5 | import gasStation from './img/gasStation.png';
6 | import gasStationCursor from './img/gasStationCursor.png';
7 | import phone from './img/phone.png';
8 | import phoneCursor from './img/phoneCursor.png';
9 | import chineseFood from './img/chineseFood.png';
10 | import chineseFoodCursor from './img/chineseFoodCursor.png';
11 | import building from './img/building.png';
12 | import buildingCursor from './img/buildingCursor.png';
13 |
14 | export const theOfficeTheme: GraphTheme = {
15 | start: (vertex: HTMLDivElement) => {
16 | vertex.style.backgroundColor = '';
17 | vertex.style.backgroundImage = `url(${holly})`;
18 | vertex.style.backgroundRepeat = 'no-repeat';
19 | vertex.style.backgroundPosition = 'center';
20 | vertex.style.backgroundSize = '80%';
21 | vertex.style.cursor = 'grab';
22 | },
23 | end: (vertex: HTMLDivElement) => {
24 | vertex.style.backgroundColor = '';
25 | vertex.style.backgroundImage = `url(${michael})`;
26 | vertex.style.backgroundRepeat = 'no-repeat';
27 | vertex.style.backgroundPosition = 'center';
28 | vertex.style.backgroundSize = '100%';
29 | vertex.style.cursor = 'grab';
30 | },
31 | wall: (vertex: HTMLDivElement) => {
32 | vertex.style.backgroundColor = '';
33 | vertex.style.backgroundImage = `url(${gasStation})`;
34 | vertex.style.backgroundRepeat = 'no-repeat';
35 | vertex.style.backgroundPosition = 'center';
36 | vertex.style.backgroundSize = 'cover';
37 | vertex.style.cursor = `url(${gasStationCursor}), pointer`;
38 | },
39 | cursorWall: (vertex: HTMLDivElement) => {
40 | vertex.style.cursor = `url(${gasStationCursor}), pointer`;
41 | },
42 | obstacle1: (vertex: HTMLDivElement) => {
43 | vertex.style.backgroundColor = '';
44 | vertex.style.backgroundImage = `url(${phone})`;
45 | vertex.style.backgroundRepeat = 'no-repeat';
46 | vertex.style.backgroundPosition = 'center';
47 | vertex.style.backgroundSize = '80%';
48 | vertex.style.cursor = `url(${phoneCursor}), pointer`;
49 | },
50 | cursorObstacle1: (vertex: HTMLDivElement) => {
51 | vertex.style.cursor = `url(${phoneCursor}), pointer`;
52 | },
53 | obstacle2: (vertex: HTMLDivElement) => {
54 | vertex.style.backgroundColor = '';
55 | vertex.style.backgroundImage = `url(${chineseFood})`;
56 | vertex.style.backgroundRepeat = 'no-repeat';
57 | vertex.style.backgroundPosition = 'center';
58 | vertex.style.backgroundSize = '80%';
59 | vertex.style.cursor = `url(${chineseFoodCursor}), pointer`;
60 | },
61 | cursorObstacle2: (vertex: HTMLDivElement) => {
62 | vertex.style.cursor = `url(${chineseFoodCursor}), pointer`;
63 | },
64 | obstacle3: (vertex: HTMLDivElement) => {
65 | vertex.style.backgroundColor = '';
66 | vertex.style.backgroundImage = `url(${building})`;
67 | vertex.style.backgroundRepeat = 'no-repeat';
68 | vertex.style.backgroundPosition = 'center';
69 | vertex.style.backgroundSize = '60%';
70 | vertex.style.cursor = `url(${buildingCursor}), pointer`;
71 | },
72 | cursorObstacle3: (vertex: HTMLDivElement) => {
73 | vertex.style.cursor = `url(${buildingCursor}), pointer`;
74 | },
75 | unvisited: (vertex: HTMLDivElement) => {
76 | vertex.style.backgroundColor = '';
77 | vertex.style.backgroundImage = '';
78 | vertex.style.color = Colors.COLOR_THEOFFICE_WHITE;
79 | vertex.style.cursor = `url(${gasStationCursor}), pointer`;
80 | },
81 | visited: (vertex: HTMLDivElement) => {
82 | vertex.style.backgroundColor = Colors.COLOR_THEOFFICE_WHITE;
83 | },
84 | visiting: (vertex: HTMLDivElement) => {
85 | vertex.style.backgroundColor = Colors.COLOR_THEOFFICE_BLUE;
86 | },
87 | pathHorizontalStart: (vertex: HTMLDivElement) => {
88 | vertex.style.backgroundImage = `url(${holly})`;
89 | vertex.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
90 | },
91 | pathVerticalStart: (vertex: HTMLDivElement) => {
92 | vertex.style.backgroundImage = `url(${holly})`;
93 | vertex.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
94 | },
95 | pathHorizontalEnd: (vertex: HTMLDivElement) => {
96 | vertex.style.backgroundImage = `url(${michael})`;
97 | vertex.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
98 | },
99 | pathVerticalEnd: (vertex: HTMLDivElement) => {
100 | vertex.style.backgroundImage = `url(${michael})`;
101 | vertex.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
102 | },
103 | pathHorizontal: (vertex: HTMLDivElement) => {
104 | vertex.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
105 | },
106 | pathVertical: (vertex: HTMLDivElement) => {
107 | vertex.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
108 | },
109 | revertObstacle: (vertex: HTMLDivElement) => {
110 | vertex.style.backgroundColor = '';
111 | vertex.style.backgroundImage = '';
112 | },
113 | revertPath: (vertex: HTMLDivElement) => {
114 | vertex.style.backgroundImage = '';
115 | },
116 | bodyBackground: (body: HTMLBodyElement) => {
117 | body.style.background = Colors.COLOR_THEOFFICE_BLACK;
118 | },
119 | header: (div: HTMLDivElement) => {
120 | div.style.backgroundColor = 'transparent';
121 | div.style.color = Colors.COLOR_THEOFFICE_WHITE;
122 | div.style.fontFamily = 'The Office, sans-serif';
123 | div.style.letterSpacing = '';
124 | div.style.fontSize = '2rem';
125 | },
126 | heading: (heading: HTMLHeadingElement) => {
127 | heading.style.background = Colors.COLOR_THEOFFICE_WHITE;
128 | heading.style.backgroundClip = 'text';
129 | heading.style.webkitBackgroundClip = 'text';
130 | heading.style.webkitTextFillColor = 'transparent';
131 | heading.style.webkitTextStroke = '';
132 | },
133 | controller: (div: HTMLDivElement) => {
134 | div.style.color = Colors.COLOR_THEOFFICE_WHITE;
135 | },
136 | button: (button: HTMLButtonElement) => {
137 | button.style.backgroundColor = 'transparent';
138 | button.style.color = Colors.COLOR_THEOFFICE_WHITE;
139 |
140 | if (!is_touch_device()) {
141 | button.addEventListener('mouseover', () => {
142 | button.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
143 | button.style.color = Colors.COLOR_THEOFFICE_WHITE;
144 | });
145 |
146 | button.addEventListener('mouseout', () => {
147 | button.style.backgroundColor = 'transparent';
148 | button.style.color = Colors.COLOR_THEOFFICE_WHITE;
149 | });
150 | }
151 | },
152 | dropdown: (heading: HTMLHeadingElement) => {
153 | heading.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
154 | heading.style.color = Colors.COLOR_THEOFFICE_WHITE;
155 | },
156 | options: (div: HTMLDivElement) => {
157 | div.style.backgroundColor = Colors.COLOR_THEOFFICE_WHITE;
158 | },
159 | option: (li: HTMLLIElement) => {
160 | li.style.backgroundColor = 'transparent';
161 | li.style.color = Colors.COLOR_THEOFFICE_BLACK;
162 |
163 | if (!is_touch_device()) {
164 | li.addEventListener('mouseover', () => {
165 | li.style.backgroundColor = Colors.COLOR_THEOFFICE_BROWN;
166 | li.style.color = Colors.COLOR_THEOFFICE_WHITE;
167 | });
168 |
169 | li.addEventListener('mouseout', () => {
170 | li.style.backgroundColor = 'transparent';
171 | li.style.color = Colors.COLOR_THEOFFICE_BLACK;
172 | });
173 | }
174 | },
175 | };
176 |
177 | export const theOfficeObstacleOptions: Obstacle = {
178 | wall: 'Gas Station (Barrier)',
179 | obstacle1: 'Cellphone Store (Weight: 2)',
180 | obstacle2: "Mr. Choo's Chinese Food (Weight: 3)",
181 | obstacle3: 'Top of Building (Weight: 4)',
182 | };
183 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 | "jsx": "react"
17 | },
18 | "include": ["src"]
19 | }
20 |
--------------------------------------------------------------------------------