diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 0f756bf..798c659 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -23,6 +23,8 @@ module.exports = { "warn", { allowConstantExport: true }, ], + "no-throw-literal": "off", + "@typescript-eslint/no-throw-literal": "error", }, settings: { react: { diff --git a/package-lock.json b/package-lock.json index a373c13..8ad77ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,16 +14,21 @@ "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.5", "@react-three/fiber": "^8.13.7", + "@reduxjs/toolkit": "^1.9.5", "d3-scale": "^4.0.2", "framer-motion": "^10.15.2", "normalize.css": "^8.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-redux": "^8.1.2", + "redux": "^4.2.1", "three": "^0.155.0" }, "devDependencies": { "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", + "@types/react-redux": "^7.1.26", + "@types/redux": "^3.6.0", "@types/three": "^0.155.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", @@ -1279,6 +1284,29 @@ } } }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1539,6 +1567,15 @@ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz", "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==" }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -1600,6 +1637,18 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.26", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.26.tgz", + "integrity": "sha512-UKPo7Cm7rswYU6PH6CmTNCRv5NYF3HrgKuHEYTK8g/3czYLrUux50gQ2pkxc9c7ZpQZi+PNhgmI8oNIRoiVIxg==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "node_modules/@types/react-transition-group": { "version": "4.4.6", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", @@ -1608,6 +1657,16 @@ "@types/react": "*" } }, + "node_modules/@types/redux": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@types/redux/-/redux-3.6.0.tgz", + "integrity": "sha512-ic+60DXHW5seNyqFvfr7Sk5cnXs+HsF9tIeIaxjOuSP5kzgDXC+AzKTYmjAfuLx4Sccm/0vjwBQj3OOkUkwOqg==", + "deprecated": "This is a stub types definition for Redux (https://github.com/reactjs/redux). Redux provides its own type definitions, so you don't need @types/redux installed!", + "dev": true, + "dependencies": { + "redux": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -1639,6 +1698,11 @@ "meshoptimizer": "~0.18.1" } }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/webxr": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.3.tgz", @@ -3569,6 +3633,15 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -4649,6 +4722,49 @@ "loose-envify": "^1.1.0" } }, + "node_modules/react-redux": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.2.tgz", + "integrity": "sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-slider": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.4.tgz", @@ -4726,6 +4842,22 @@ "balanced-match": "^1.0.0" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", @@ -4748,6 +4880,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", @@ -6367,6 +6504,17 @@ } } }, + "@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", + "requires": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + } + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -6521,6 +6669,15 @@ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz", "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==" }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -6582,6 +6739,18 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.26", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.26.tgz", + "integrity": "sha512-UKPo7Cm7rswYU6PH6CmTNCRv5NYF3HrgKuHEYTK8g/3czYLrUux50gQ2pkxc9c7ZpQZi+PNhgmI8oNIRoiVIxg==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-transition-group": { "version": "4.4.6", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", @@ -6590,6 +6759,15 @@ "@types/react": "*" } }, + "@types/redux": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@types/redux/-/redux-3.6.0.tgz", + "integrity": "sha512-ic+60DXHW5seNyqFvfr7Sk5cnXs+HsF9tIeIaxjOuSP5kzgDXC+AzKTYmjAfuLx4Sccm/0vjwBQj3OOkUkwOqg==", + "dev": true, + "requires": { + "redux": "*" + } + }, "@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -6621,6 +6799,11 @@ "meshoptimizer": "~0.18.1" } }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "@types/webxr": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.3.tgz", @@ -8050,6 +8233,11 @@ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, + "immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -8841,6 +9029,26 @@ } } }, + "react-redux": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.2.tgz", + "integrity": "sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + } + } + }, "react-slider": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.4.tgz", @@ -8902,6 +9110,20 @@ "balanced-match": "^1.0.0" } }, + "redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "requires": {} + }, "regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", @@ -8918,6 +9140,11 @@ "functions-have-names": "^1.2.3" } }, + "reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", diff --git a/package.json b/package.json index 75e5bb0..f813464 100644 --- a/package.json +++ b/package.json @@ -17,16 +17,21 @@ "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.5", "@react-three/fiber": "^8.13.7", + "@reduxjs/toolkit": "^1.9.5", "d3-scale": "^4.0.2", "framer-motion": "^10.15.2", "normalize.css": "^8.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-redux": "^8.1.2", + "redux": "^4.2.1", "three": "^0.155.0" }, "devDependencies": { "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", + "@types/react-redux": "^7.1.26", + "@types/redux": "^3.6.0", "@types/three": "^0.155.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", diff --git a/src/app.tsx b/src/app.tsx index 0df7c49..7eb89a1 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,14 +1,16 @@ import { Box, Stack } from "@mui/material"; -import DataSideBar from "./components/dataSideBar"; -import ResultsBar from "./components/resultsBar"; -import CentrePlot from "./components/centrePlot"; -import LegendBar from "./components/legendBar"; +import DataSideBar from "./data-entry/dataSideBar"; +import ResultsBar from "./results/resultsBar"; +import CentrePlot from "./plot/centrePlot"; +import LegendBar from "./legend/legendBar"; +import BasicAppBar from "./basicAppBar"; export default function App(): JSX.Element { return ( - - - + + + + @@ -20,7 +22,7 @@ export default function App(): JSX.Element { - - + + ); } diff --git a/src/components/basicAppBar.tsx b/src/basicAppBar.tsx similarity index 99% rename from src/components/basicAppBar.tsx rename to src/basicAppBar.tsx index 129722f..bfa71ce 100644 --- a/src/components/basicAppBar.tsx +++ b/src/basicAppBar.tsx @@ -8,7 +8,6 @@ import MenuIcon from "@mui/icons-material/Menu"; import { Drawer } from "@mui/material"; import SideMenu from "./sideMenu"; - export default function BasicAppBar(): JSX.Element { const [state, setState] = React.useState({ menuOpen: false }); const toggleDrawer = (open: boolean) => () => { diff --git a/src/calculations/ray.test.ts b/src/calculations/ray.test.ts index c086a32..22f6806 100644 --- a/src/calculations/ray.test.ts +++ b/src/calculations/ray.test.ts @@ -6,5 +6,11 @@ test("Getting a point from a ray", () => { const ray1 = new Ray(new Vector2(1, 1), new Vector2(1, 1)); const vector1 = ray1.getPoint(5); const vector2 = new Vector2(6, 6); - expect(vector1?.equals(vector2)); + expect(vector1.equals(vector2)); +}); + +test("Getting point at distance", () => { + const ray1 = new Ray(new Vector2(1, 1), new Vector2(0, 0)); + const point = ray1.getPointAtDistance(4); + console.log(point); }); diff --git a/src/calculations/ray.ts b/src/calculations/ray.ts index 8811b40..586987f 100644 --- a/src/calculations/ray.ts +++ b/src/calculations/ray.ts @@ -18,21 +18,18 @@ export class Ray { * @param scalar * @returns */ - getPoint(scalar: number): Vector2 | null { - if (scalar < 0) return null; + getPoint(scalar: number): Vector2 { const result = new Vector2(this.direction.x, this.direction.y); result.multiplyScalar(scalar); result.add(this.initial_point); return result; } - getPointAtDistance(distance: number): Vector2 | null { + getPointAtDistance(distance: number): Vector2 { return this.getPoint(distance / this.direction.length()); } - getParameterRange(t1: number, t2: number): NumericRange | null { - if (t1 < 0 && t2 < 0) return null; - + getParameterRange(t1: number, t2: number): NumericRange { let tMin = Math.min(t1, t2); const tMax = Math.max(t1, t2); diff --git a/src/components/dataSideBar.tsx b/src/components/dataSideBar.tsx deleted file mode 100644 index f88e137..0000000 --- a/src/components/dataSideBar.tsx +++ /dev/null @@ -1,300 +0,0 @@ -import { - Card, - CardContent, - Stack, - Typography, - Divider, - Autocomplete, - TextField, - Button, - FormControl, - Select, - MenuItem, - InputLabel, - SelectChangeEvent, - ButtonGroup, - Input, -} from "@mui/material"; -import { Detector, BeamlineConfig } from "../utils/types"; -import React from "react"; -import RemoveIcon from "@mui/icons-material/Remove"; -import AddIcon from "@mui/icons-material/Add"; -import { DistanceUnits, EnergyUnits, WavelengthUnits, AngleUnits } from "../utils/units"; - - -export default function DataSideBar(): JSX.Element { - const [cameraDiameterUnits, setCameraDiameterUnits] = - React.useState(DistanceUnits.millimetre); - - const handleCameraDiameterUnits = (event: SelectChangeEvent) => { - setCameraDiameterUnits(event.target.value as DistanceUnits); - }; - - const [clearanceDiameterUnits, setClearnaceDiameterUnits] = - React.useState(DistanceUnits.millimetre); - - const handleClearanceDiameterUnits = (event: SelectChangeEvent) => { - setClearnaceDiameterUnits(event.target.value as DistanceUnits); - }; - - const [beamstopDiameterUnits, setBeamstopDiameterUnits] = - React.useState(DistanceUnits.millimetre); - - const handleBeamstopDiameterUnits = (event: SelectChangeEvent) => { - setBeamstopDiameterUnits(event.target.value as DistanceUnits); - }; - const [pixelSizeUnits, setPixelSizeUnits] = React.useState( - DistanceUnits.millimetre, - ); - - const handlePixelSizeUnits = (event: SelectChangeEvent) => { - setPixelSizeUnits(event.target.value as DistanceUnits); - }; - const [beamEnergyUnits, setBeamEnergyUnits] = React.useState( - EnergyUnits.electronVolts, - ); - - const handleBeamEnergyUnits = (event: SelectChangeEvent) => { - setBeamEnergyUnits(event.target.value as EnergyUnits); - }; - - const [wavelengthUnits, setWavelengthUnits] = React.useState( - WavelengthUnits.nanmometres, - ); - - const handleWavelengthUnits = (event: SelectChangeEvent) => { - setWavelengthUnits(event.target.value as WavelengthUnits); - }; - - const [cameraLength, setCameraLength] = React.useState(1.9); - - const handleCameraLength = (event: React.ChangeEvent) => { - setCameraLength(parseFloat(event.target.value)); - }; - const [angleUnits, setAngleUnits] = React.useState( - AngleUnits.radians, - ); - - const handleAngleUnits = (event: SelectChangeEvent) => { - setAngleUnits(event.target.value as AngleUnits); - }; - - const [angle, setAngle] = React.useState(90); - - const handleAngleChange = (event: React.ChangeEvent) => { - setAngle(parseFloat(event.target.value)); - }; - - return ( - - - - Configuration - - Predefined Configuration Templates - - ( - - )} - /> - - - Detector - ( - - )} - /> - Resolution: 150x160 - - Pixel size - - units - - - - - - Beamstop - - Diameter: {4} - - units - - - - - - Position - - x: - px - - - - y: - px - - - - Clearance - - Diameter: {4} - - units - - - - - Camera Tube - - Diameter: {4} - - units - - - - Position - - x: - px - - - y: - px - - - Beam properties - - Energy - - units - - - - - WaveLength - - units - - - - Minimum allowed wavelength: {4} - Maximum allowed wavelength: {4} - - - Camera Length: - - - - - - m - - - - Angle: - - - units - - - - - - - ); -} diff --git a/src/components/legendBar.tsx b/src/components/legendBar.tsx deleted file mode 100644 index c8f3618..0000000 --- a/src/components/legendBar.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { - Card, - CardContent, - Checkbox, - FormControlLabel, - FormGroup, - Stack, - Typography, -} from "@mui/material"; - -export default function LegendBar(): JSX.Element { - return ( - - - - Legend - Add something to do with colors here - - } label="Detector" /> - } label="Beamstop" /> - } label="Camera tube" /> - } label="Q range" /> - } label="Mask" /> - } label="Calibrant" /> - - Currently selected Calibrant is: {5} - - - - ); -} diff --git a/src/components/resultsBar.tsx b/src/components/resultsBar.tsx deleted file mode 100644 index 600ef48..0000000 --- a/src/components/resultsBar.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Box, Card, CardContent, Divider, FormControl, InputLabel, MenuItem, Select, SelectChangeEvent, Stack, Typography } from "@mui/material"; -import { AngleUnits } from "../utils/units"; -import React from "react"; -const theta = "\u03B8" - -enum ScatteringQuantity { - q = "q", - d = "d", - s = "s", - twoTheta = "2" + theta, -} - - - -export default function ResultsBar(): JSX.Element { - const [angleUnits, setAngleUnits] = React.useState( - AngleUnits.radians, - ); - - const handleAngleUnits = (event: SelectChangeEvent) => { - setAngleUnits(event.target.value as AngleUnits); - }; - const [quantity, setQuantity] = React.useState( - ScatteringQuantity.q - ); - - const handleQuantity = (event: SelectChangeEvent) => { - setQuantity(event.target.value as ScatteringQuantity); - }; - - return ( - - - - - Results bottom bar - - - Scattering quantity: - - quantity - - - - units - - - - Min {quantity} value: - Max {quantity} value: - Requested min {quantity} value: - Requested max {quantity} value: - - - - - ); -} diff --git a/src/components/sideMenu.tsx b/src/components/sideMenu.tsx deleted file mode 100644 index 36a2f69..0000000 --- a/src/components/sideMenu.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Box from "@mui/material/Box"; -import List from "@mui/material/List"; -import Divider from "@mui/material/Divider"; -import ListItem from "@mui/material/ListItem"; -import { Button, Stack, Typography } from "@mui/material"; - -export default function SideMenu() { - return ( - - - - - Reset to default - - - - - - - ); -} diff --git a/src/data-entry/configSlice.ts b/src/data-entry/configSlice.ts new file mode 100644 index 0000000..1e0024e --- /dev/null +++ b/src/data-entry/configSlice.ts @@ -0,0 +1,19 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import testBeamlineConfig from "../presets/presetConfigs.json"; +import { BeamlineConfig } from "../utils/types"; +import { RootState } from "../store"; + +const beamlineConfigSlice = createSlice({ + name: "config", + initialState: (testBeamlineConfig as Record).test, + reducers: { + editConfig: (state, action: PayloadAction>) => { + return { ...state, ...action.payload }; + }, + }, +}); + +export const beamlineConfigReducer = beamlineConfigSlice.reducer; + +export const { editConfig } = beamlineConfigSlice.actions; +export const configSelector = (state: RootState) => state.config; diff --git a/src/data-entry/dataSideBar.tsx b/src/data-entry/dataSideBar.tsx new file mode 100644 index 0000000..b017672 --- /dev/null +++ b/src/data-entry/dataSideBar.tsx @@ -0,0 +1,345 @@ +import { + Card, + CardContent, + Stack, + Typography, + Divider, + Autocomplete, + TextField, + Button, + FormControl, + Select, + MenuItem, + InputLabel, + SelectChangeEvent, + ButtonGroup, + Input, +} from "@mui/material"; +import RemoveIcon from "@mui/icons-material/Remove"; +import AddIcon from "@mui/icons-material/Add"; +import { + DistanceUnits, + EnergyUnits, + WavelengthUnits, + AngleUnits, +} from "../utils/units"; +import { useDispatch, useSelector } from "react-redux"; +import { configSelector, editConfig } from "./configSlice"; +import { editUnits, unitSelector } from "./unitSlice"; +import detectorData from "../presets/detectors.json"; +import presetData from "../presets/presetConfigs.json"; +import { BeamlineConfig, Detector } from "../utils/types"; +import { useState } from "react"; + + +const detectorList = detectorData as Record; +const presetList = presetData as Record; + +export default function DataSideBar(): JSX.Element { + const config = useSelector(configSelector); + const units = useSelector(unitSelector); + const dispatch = useDispatch(); + const [preset, setPreset] = useState("test"); + + const handlePresetChange = (preset: string): void => { + setPreset(preset); + dispatch(editConfig(presetList[preset])); + }; + + const handleDetectorChange = (detector: string): void => { + dispatch(editConfig({ detector: detector })); + }; + + return ( + + + + Configuration + + Predefined Configuration Templates + + ( + + )} + onChange={(_, value) => { + value ? handlePresetChange(value) : {}; + }} + /> + + + Detector + ( + + )} + onChange={(_, value) => { + value ? handleDetectorChange(value) : {}; + }} + /> + + Resolution: {detectorList[config.detector].resolution.height} x{" "} + {detectorList[config.detector].resolution.width} + + + + Pixel size: {detectorList[config.detector].pixel_size} x{" "} + {detectorList[config.detector].pixel_size}{" "} + + + units + + + + + + Beamstop + + + Diameter: {config.beamstop.diameter} + + + units + + + + + + Position + + + x: {config.beamstop.centre.x} px + + + + + + y: {config.beamstop.centre.y} px + + + + + Clearance + + Diameter: {config.clearance} + + units + + + + + Camera Tube + + + Diameter: {config.cameraTube.diameter} + + + units + + + + Position + + + x: {config.cameraTube.centre.x} px + + + + + y: {config.cameraTube.centre.y} px + + + + Beam properties + + Energy: + + units + + + + + WaveLength + + units + + + + + Minimum allowed wavelength: {config.minWavelength}{" "} + + + Maximum allowed wavelength: {config.maxWavelength} + + + + Camera Length: + + + + + + m + + + + Angle: + + dispatch(editConfig({ angle: parseFloat(event.target.value) })) + } + /> + + units + + + + + + + ); +} diff --git a/src/data-entry/unitSlice.ts b/src/data-entry/unitSlice.ts new file mode 100644 index 0000000..9c0efff --- /dev/null +++ b/src/data-entry/unitSlice.ts @@ -0,0 +1,33 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { RootState } from "../store"; +import { + AngleUnits, + DistanceUnits, + EnergyUnits, + UnitConfig, + WavelengthUnits, +} from "../utils/units"; + +const defaultUnits: UnitConfig = { + cameraDiameterUnits: DistanceUnits.millimetre, + clearanceDiameterUnits: DistanceUnits.micrometre, + beamEnergyUnits: EnergyUnits.electronVolts, + pixelSizeUnits: DistanceUnits.millimetre, + angleUnits: AngleUnits.radians, + wavelengthUnits: WavelengthUnits.angstroms, + beamstopDiameterUnits: DistanceUnits.millimetre, +}; + +const unitConfigSlice = createSlice({ + name: "unit-config", + initialState: defaultUnits, + reducers: { + editUnits: (state, action: PayloadAction>) => { + return { ...state, ...action.payload }; + }, + }, +}); + +export const { editUnits } = unitConfigSlice.actions; +export const unitConfigReducer = unitConfigSlice.reducer; +export const unitSelector = (state: RootState) => state.units; diff --git a/src/index.css b/src/index.css index 929290a..399400d 100644 --- a/src/index.css +++ b/src/index.css @@ -2,17 +2,17 @@ @import "@h5web/lib/styles.css"; body { - margin: 0; - height: 100vh; - width: 100vw; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + margin: 0; + height: 100vh; + width: 100vw; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; -} \ No newline at end of file + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} diff --git a/src/legend/legendBar.tsx b/src/legend/legendBar.tsx new file mode 100644 index 0000000..6679638 --- /dev/null +++ b/src/legend/legendBar.tsx @@ -0,0 +1,31 @@ +import { + Card, + CardContent, + Checkbox, + FormControlLabel, + FormGroup, + Stack, + Typography, +} from "@mui/material"; + +export default function LegendBar(): JSX.Element { + return ( + + + + Legend + Add something to do with colors here + + } label="Detector" /> + } label="Beamstop" /> + } label="Camera tube" /> + } label="Q range" /> + } label="Mask" /> + } label="Calibrant" /> + + Currently selected Calibrant is: {5} + + + + ); +} diff --git a/src/main.tsx b/src/main.tsx index 5ce9948..3569e45 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,17 +1,14 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./app.tsx"; -import BasicAppBar from "./components/basicAppBar.tsx"; import "./index.css"; -import { Box } from "@mui/material"; - - +import { Provider } from "react-redux"; +import store from "./store.ts"; ReactDOM.createRoot(document.getElementById("root")!).render( - - + - + , ); diff --git a/src/components/centrePlot.tsx b/src/plot/centrePlot.tsx similarity index 100% rename from src/components/centrePlot.tsx rename to src/plot/centrePlot.tsx diff --git a/src/presets/detectors.json b/src/presets/detectors.json index 95cefe2..e0f08f2 100644 --- a/src/presets/detectors.json +++ b/src/presets/detectors.json @@ -1,8 +1,16 @@ { - "name": "Pilatus P3-2M", + "Pilatus P3-2M": { "resolution": { - "width": "1475", - "height": "1679" + "width": 1475, + "height": 1679 }, "pixel_size": 0.172 -} \ No newline at end of file + }, + "test": { + "resolution": { + "width": 1, + "height": 1 + }, + "pixel_size": 1 + } +} diff --git a/src/presets/presetConfigs.json b/src/presets/presetConfigs.json index 9e26dfe..8050da2 100644 --- a/src/presets/presetConfigs.json +++ b/src/presets/presetConfigs.json @@ -1 +1,50 @@ -{} \ No newline at end of file +{ + "test": { + "detector": "test", + "beamstop": { + "centre": { + "x": 1, + "y": 2 + }, + "diameter": 1 + }, + "cameraTube": { + "centre": { + "x": 1, + "y": 2 + }, + "diameter": 1 + }, + "angle": 1, + "cameraLength": 1, + "clearance": 1, + "minWavelength": 1, + "maxWavelength": 3, + "minCameraLength": 2, + "maxCameraLength": 4 + }, + "second": { + "detector": "Pilatus P3-2M", + "beamstop": { + "centre": { + "x": 1, + "y": 2 + }, + "diameter": 4 + }, + "cameraTube": { + "centre": { + "x": 1, + "y": 2 + }, + "diameter": 1 + }, + "angle": 1, + "cameraLength": 1, + "clearance": 1, + "minWavelength": 1, + "maxWavelength": 1, + "minCameraLength": 1, + "maxCameraLength": 1 + } +} \ No newline at end of file diff --git a/src/results/resultsBar.tsx b/src/results/resultsBar.tsx new file mode 100644 index 0000000..2929e3b --- /dev/null +++ b/src/results/resultsBar.tsx @@ -0,0 +1,94 @@ +import { + Box, + Card, + CardContent, + Divider, + FormControl, + InputLabel, + MenuItem, + Select, + SelectChangeEvent, + Stack, + Typography, +} from "@mui/material"; +import { AngleUnits } from "../utils/units"; +import React from "react"; +const theta = "\u03B8"; + +enum ScatteringQuantity { + q = "q", + d = "d", + s = "s", + twoTheta = "2" + theta, +} + +export default function ResultsBar(): JSX.Element { + const [angleUnits, setAngleUnits] = React.useState( + AngleUnits.radians, + ); + + const handleAngleUnits = (event: SelectChangeEvent) => { + setAngleUnits(event.target.value as AngleUnits); + }; + const [quantity, setQuantity] = React.useState( + ScatteringQuantity.q, + ); + + const handleQuantity = (event: SelectChangeEvent) => { + setQuantity(event.target.value as ScatteringQuantity); + }; + + return ( + + + + + Results bottom bar + + + Scattering quantity: + + quantity + + + + units + + + + Min {quantity} value: + Max {quantity} value: + Requested min {quantity} value: + Requested max {quantity} value: + + + + + ); +} diff --git a/src/sideMenu.tsx b/src/sideMenu.tsx new file mode 100644 index 0000000..846b534 --- /dev/null +++ b/src/sideMenu.tsx @@ -0,0 +1,21 @@ +import Box from "@mui/material/Box"; +import List from "@mui/material/List"; +import Divider from "@mui/material/Divider"; +import ListItem from "@mui/material/ListItem"; +import { Button, Stack, Typography } from "@mui/material"; + +export default function SideMenu() { + return ( + + + + + Reset to default + + + + + + + ); +} diff --git a/src/store.ts b/src/store.ts new file mode 100644 index 0000000..c42d4a5 --- /dev/null +++ b/src/store.ts @@ -0,0 +1,15 @@ +import { configureStore } from "@reduxjs/toolkit"; +import { beamlineConfigReducer } from "./data-entry/configSlice"; +import { unitConfigReducer } from "./data-entry/unitSlice"; + +const store = configureStore({ + reducer: { + config: beamlineConfigReducer, + units: unitConfigReducer, + }, +}); + +export default store; + +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; diff --git a/src/utils/types.ts b/src/utils/types.ts index b90f0f8..f20dce0 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -1,9 +1,8 @@ import { Vector2 } from "three"; export interface Detector { - name: string; resolution: { height: number; width: number }; - pixel_size: { height: number; width: number }; + pixel_size: number; } export interface CircularDevice { @@ -12,11 +11,10 @@ export interface CircularDevice { } export interface BeamlineConfig { - name: string | null - detector: Detector; - Beamstop: CircularDevice; - CameraTube: CircularDevice; - angle: number; + detector: string; + beamstop: CircularDevice; + cameraTube: CircularDevice; + angle: number | null; cameraLength: number; clearance: number; // remember to do int checks on this value minWavelength: number; diff --git a/src/utils/units.ts b/src/utils/units.ts index 50aabfd..defbd61 100644 --- a/src/utils/units.ts +++ b/src/utils/units.ts @@ -1,22 +1,32 @@ -const mu = "\u03bc" -const angstrum = "\u212B" +const mu = "\u03bc"; +const angstrum = "\u212B"; export enum DistanceUnits { - millimetre = "mm", - micrometre = mu + "m", + millimetre = "mm", + micrometre = mu + "m", } export enum EnergyUnits { - electronVolts = "eV", - kiloElectronVolts = "keV", + electronVolts = "eV", + kiloElectronVolts = "keV", } export enum WavelengthUnits { - nanmometres = "nm", - angstroms = angstrum + nanmometres = "nm", + angstroms = angstrum, } export enum AngleUnits { - radians = "rad", - degrees = "deg", -} \ No newline at end of file + radians = "rad", + degrees = "deg", +} + +export interface UnitConfig { + pixelSizeUnits: DistanceUnits; + beamEnergyUnits: EnergyUnits; + beamstopDiameterUnits: DistanceUnits; + clearanceDiameterUnits: DistanceUnits; + cameraDiameterUnits: DistanceUnits; + wavelengthUnits: WavelengthUnits; + angleUnits: AngleUnits; +}