From 78832749792cefbe188cfc9ea486849fed6f9d5b Mon Sep 17 00:00:00 2001 From: Szymon Kania <26521377+ostatni5@users.noreply.github.com> Date: Sun, 16 Apr 2023 18:03:23 +0200 Subject: [PATCH] 822 geometry of patient body (#847) * Add CT body * Add resultCT * lint * Rename CT to 3D_datafile_CT * Change dimension to 3 * Rename CT to 3D * Update src/JsRoot/GraphData.tsx Co-authored-by: Jakub Niechaj * Apply suggestions from code review Co-authored-by: Jakub Niechaj --------- Co-authored-by: Jakub Niechaj --- package-lock.json | 67 +++++++++++++++++++ package.json | 2 + src/JsRoot/GraphData.tsx | 14 +++- .../components/Sidebar/EditorSidebar.tsx | 7 +- .../Sidebar/propeteries/PropeteriesPanel.tsx | 2 + .../propeteries/category/CTConfiguration.tsx | 38 +++++++++++ .../propeteries/category/ObjectDimensions.tsx | 5 +- src/ThreeEditor/util/BasicMeshes.ts | 25 ++++++- src/ThreeEditor/util/ObjectLoader.ts | 7 +- .../components/Results/Results3D.tsx | 34 ++++++++++ .../components/Simulation/SimulationPanel.tsx | 2 +- .../Simulation/SimulationProgressBar.tsx | 2 +- 12 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 src/ThreeEditor/components/Sidebar/propeteries/category/CTConfiguration.tsx create mode 100644 src/WrapperApp/components/Results/Results3D.tsx diff --git a/package-lock.json b/package-lock.json index e26f895f4..cedac848a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "pyodide": "^0.22.1", "react": "^17.0.2", "react-async-script": "^1.2.0", + "react-copy-to-clipboard": "^5.1.0", "react-countdown": "^2.3.2", "react-dnd": "^15.1.2", "react-dom": "^17.0.2", @@ -54,6 +55,7 @@ "devDependencies": { "@types/react": "^18.0.9", "@types/react-async-script": "^1.2.1", + "@types/react-copy-to-clipboard": "^5.0.4", "@types/three": "^0.149.0", "cross-env": "^7.0.3", "gh-pages": "^5.0.0", @@ -4642,6 +4644,15 @@ "hoist-non-react-statics": "^3.3.1" } }, + "node_modules/@types/react-copy-to-clipboard": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz", + "integrity": "sha512-otTJsJpofYAeaIeOwV5xBUGpo6exXG2HX7X4nseToCB2VgPEBxGBHCm/FecZ676doNR7HCSTVtmohxfG2b3/yQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "17.0.19", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.19.tgz", @@ -6735,6 +6746,14 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js": { "version": "3.29.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", @@ -15773,6 +15792,18 @@ "react": ">=16.4.1" } }, + "node_modules/react-copy-to-clipboard": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", + "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "dependencies": { + "copy-to-clipboard": "^3.3.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^15.3.0 || 16 || 17 || 18" + } + }, "node_modules/react-countdown": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/react-countdown/-/react-countdown-2.3.5.tgz", @@ -17929,6 +17960,11 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -22682,6 +22718,15 @@ "hoist-non-react-statics": "^3.3.1" } }, + "@types/react-copy-to-clipboard": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz", + "integrity": "sha512-otTJsJpofYAeaIeOwV5xBUGpo6exXG2HX7X4nseToCB2VgPEBxGBHCm/FecZ676doNR7HCSTVtmohxfG2b3/yQ==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "17.0.19", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.19.tgz", @@ -24265,6 +24310,14 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, "core-js": { "version": "3.29.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", @@ -30755,6 +30808,15 @@ "prop-types": "^15.5.0" } }, + "react-copy-to-clipboard": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", + "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "requires": { + "copy-to-clipboard": "^3.3.1", + "prop-types": "^15.8.1" + } + }, "react-countdown": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/react-countdown/-/react-countdown-2.3.5.tgz", @@ -32361,6 +32423,11 @@ "is-number": "^7.0.0" } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/package.json b/package.json index ab142cc67..7c5823369 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "pyodide": "^0.22.1", "react": "^17.0.2", "react-async-script": "^1.2.0", + "react-copy-to-clipboard": "^5.1.0", "react-countdown": "^2.3.2", "react-dnd": "^15.1.2", "react-dom": "^17.0.2", @@ -96,6 +97,7 @@ "devDependencies": { "@types/react": "^18.0.9", "@types/react-async-script": "^1.2.1", + "@types/react-copy-to-clipboard": "^5.0.4", "@types/three": "^0.149.0", "cross-env": "^7.0.3", "gh-pages": "^5.0.0", diff --git a/src/JsRoot/GraphData.tsx b/src/JsRoot/GraphData.tsx index d69e61b9a..ad4c46db3 100644 --- a/src/JsRoot/GraphData.tsx +++ b/src/JsRoot/GraphData.tsx @@ -8,6 +8,7 @@ import { estimatorPage1DToCsv } from '../util/csv/Csv'; import { ScoringOutputJSON } from '../ThreeEditor/util/Scoring/ScoringOutput'; import { FilterJSON } from '../ThreeEditor/util/Detect/DetectFilter'; import { EstimatorResults } from '../WrapperApp/components/Results/ResultsPanel'; +import Result3D from '../WrapperApp/components/Results/Results3D'; export type pageData = { name: string; @@ -38,6 +39,11 @@ export interface Page0D extends IPage { dimensions: 0; } +export interface Page3D extends IPage { + dimensions: 3; + resultsUrl: string; +} + export type Estimator = { name: string; metadata?: unknown; @@ -45,7 +51,7 @@ export type Estimator = { scoringOutputJsonRef?: ScoringOutputJSON; }; -export type Page = Page2D | Page1D | Page0D; +export type Page = Page3D | Page2D | Page1D | Page0D; export const isPage2d = (page: Page): page is Page2D => { return (page as Page2D).dimensions === 2; @@ -59,6 +65,10 @@ export const isPage0d = (page: Page): page is Page0D => { return (page as Page0D).dimensions === 0; }; +export const isPage3D = (page: Page): page is Page3D => { + return (page as Page3D).dimensions === 3; +}; + const getGraphFromPage = (page: Page, title?: string) => { if (isPage2d(page)) { return ; @@ -66,6 +76,8 @@ const getGraphFromPage = (page: Page, title?: string) => { return ; } else if (isPage0d(page)) { return ; + } else if (isPage3D(page)) { + return ; } else { return
Error
; } diff --git a/src/ThreeEditor/components/Sidebar/EditorSidebar.tsx b/src/ThreeEditor/components/Sidebar/EditorSidebar.tsx index 5f6ffb73b..55418077f 100644 --- a/src/ThreeEditor/components/Sidebar/EditorSidebar.tsx +++ b/src/ThreeEditor/components/Sidebar/EditorSidebar.tsx @@ -4,7 +4,7 @@ import { Box, Tabs, Tab } from '@mui/material'; import { TabPanel } from '../../../WrapperApp/components/TabPanel'; import { PropertiesPanel } from './propeteries/PropeteriesPanel'; import { EditorSidebarTabTree } from './tabs/EditorSidebarTabTree'; -import { BoxMesh, CylinderMesh, SphereMesh } from '../../util/BasicMeshes'; +import { BoxMesh, CTMesh, CylinderMesh, SphereMesh } from '../../util/BasicMeshes'; import { AddObjectCommand } from '../../js/commands/AddObjectCommand'; import { AddZoneCommand } from '../../js/commands/AddZoneCommand'; import { AddDetectGeometryCommand } from '../../js/commands/AddDetectGeometryCommand'; @@ -85,6 +85,11 @@ export function EditorSidebar(props: { editor: Editor }) { title: 'Sphere', onClick: () => editor.execute(new AddObjectCommand(editor, new SphereMesh(editor))) + }, + { + title: 'CT', + onClick: () => + editor.execute(new AddObjectCommand(editor, new CTMesh(editor))) } ], tree: diff --git a/src/ThreeEditor/components/Sidebar/propeteries/PropeteriesPanel.tsx b/src/ThreeEditor/components/Sidebar/propeteries/PropeteriesPanel.tsx index 8d381a2fc..90caaedaf 100644 --- a/src/ThreeEditor/components/Sidebar/propeteries/PropeteriesPanel.tsx +++ b/src/ThreeEditor/components/Sidebar/propeteries/PropeteriesPanel.tsx @@ -16,6 +16,7 @@ import { QuantityDifferentialScoring } from './category/QuantityDifferentialScor import { BeamConfiguration } from './category/BeamConfiguration'; import { ObjectMaterial } from './category/ObjectMaterial'; import { FilterConfiguration } from './category/FilterConfiguration'; +import { CTConfiguration } from './category/CTConfiguration'; export function PropertiesPanel(props: { boxProps: BoxProps; editor: Editor }) { const { boxProps, editor } = props; @@ -40,6 +41,7 @@ export function PropertiesPanel(props: { boxProps: BoxProps; editor: Editor }) { + diff --git a/src/ThreeEditor/components/Sidebar/propeteries/category/CTConfiguration.tsx b/src/ThreeEditor/components/Sidebar/propeteries/category/CTConfiguration.tsx new file mode 100644 index 000000000..bff95c2a9 --- /dev/null +++ b/src/ThreeEditor/components/Sidebar/propeteries/category/CTConfiguration.tsx @@ -0,0 +1,38 @@ +import { Object3D } from 'three'; +import { SetValueCommand } from '../../../../js/commands/SetValueCommand'; +import { Editor } from '../../../../js/Editor'; +import { TextPropertyField } from '../fields/PropertyField'; +import { useSmartWatchEditorState } from '../../../../util/hooks/signals'; +import { PropertiesCategory } from './PropertiesCategory'; +import { isCTMesh } from '../../../../util/BasicMeshes'; + +export function CTConfiguration(props: { editor: Editor; object: Object3D }) { + const { object, editor } = props; + + const { state: watchedObject } = useSmartWatchEditorState(editor, object); + + const visibleFlag = isCTMesh(watchedObject); + + return ( + + {visibleFlag && ( + <> + { + editor.execute( + new SetValueCommand( + editor, + watchedObject.object, + 'pathOnServer', + value + ) + ); + }} + /> + + )} + + ); +} diff --git a/src/ThreeEditor/components/Sidebar/propeteries/category/ObjectDimensions.tsx b/src/ThreeEditor/components/Sidebar/propeteries/category/ObjectDimensions.tsx index eb8924cd5..ebc63f982 100644 --- a/src/ThreeEditor/components/Sidebar/propeteries/category/ObjectDimensions.tsx +++ b/src/ThreeEditor/components/Sidebar/propeteries/category/ObjectDimensions.tsx @@ -11,6 +11,7 @@ import { BASIC_GEOMETRY_OPTIONS, isBasicMesh, isBoxMesh, + isCTMesh, isCylinderMesh, isSphereMesh } from '../../../../util/BasicMeshes'; @@ -460,7 +461,9 @@ export function ObjectDimensions(props: { }) { const { object, editor } = props; - const visibleFlag = isBasicMesh(object) || isDetectGeometry(object) || isWorldZone(object); + const visibleFlag = + !isCTMesh(object) && + (isBasicMesh(object) || isDetectGeometry(object) || isWorldZone(object)); return ( diff --git a/src/ThreeEditor/util/BasicMeshes.ts b/src/ThreeEditor/util/BasicMeshes.ts index a480d48d0..2c1896155 100644 --- a/src/ThreeEditor/util/BasicMeshes.ts +++ b/src/ThreeEditor/util/BasicMeshes.ts @@ -13,7 +13,8 @@ const defaultMaterial = new THREE.MeshBasicMaterial({ export const BASIC_GEOMETRY_OPTIONS = { Box: 'Box', Cylinder: 'Cylinder', - Sphere: 'Sphere' + Sphere: 'Sphere', + CT: 'CT', } as const; export type BasicGeometry = typeof BASIC_GEOMETRY_OPTIONS[keyof typeof BASIC_GEOMETRY_OPTIONS]; @@ -78,6 +79,26 @@ export class SphereMesh extends BasicMesh { } } +const ctGeometry = new THREE.BoxGeometry(2, .5, 1, 1, 1, 1); +const ctMaterial = defaultMaterial.clone() +ctMaterial.color.setHex(0x00ff00); +export class CTMesh extends BasicMesh { + readonly notScalable = true; + + pathOnServer: string = ''; + + constructor(editor: Editor, geometry?: THREE.BoxGeometry, material?: THREE.MeshBasicMaterial) { + super(editor, 'CT', 'CTMesh', 'CT', geometry ?? ctGeometry, material ?? ctMaterial.clone()); + } + + toJSON(meta: { geometries: unknown; materials: unknown; textures: unknown; images: unknown; } | undefined) { + const json = super.toJSON(meta); + json.object.pathOnServer = this.pathOnServer; + return json; + } +} + + export const isBasicMesh = (x: unknown): x is BasicMesh => x instanceof BasicMesh; export const isBoxMesh = (x: unknown): x is BoxMesh => x instanceof BoxMesh; @@ -85,3 +106,5 @@ export const isBoxMesh = (x: unknown): x is BoxMesh => x instanceof BoxMesh; export const isCylinderMesh = (x: unknown): x is CylinderMesh => x instanceof CylinderMesh; export const isSphereMesh = (x: unknown): x is SphereMesh => x instanceof SphereMesh; + +export const isCTMesh = (x: unknown): x is CTMesh => x instanceof CTMesh; diff --git a/src/ThreeEditor/util/ObjectLoader.ts b/src/ThreeEditor/util/ObjectLoader.ts index 9a9b714c5..9934a1cc4 100644 --- a/src/ThreeEditor/util/ObjectLoader.ts +++ b/src/ThreeEditor/util/ObjectLoader.ts @@ -1,6 +1,6 @@ import { Object3D, ObjectLoader } from 'three'; import { Editor } from '../js/Editor'; -import { BoxMesh, CylinderMesh, SphereMesh } from './BasicMeshes'; +import { BoxMesh, CTMesh, CylinderMesh, SphereMesh } from './BasicMeshes'; export class EditorObjectLoader extends ObjectLoader { private editor: Editor; @@ -17,6 +17,7 @@ export class EditorObjectLoader extends ObjectLoader { const geometry = geometries[data.geometry]; const material = materials[data.material]; + let object = super.parseObject(data, geometries, materials, animations); let editorObject; switch (data.type) { @@ -30,6 +31,10 @@ export class EditorObjectLoader extends ObjectLoader { editorObject = new CylinderMesh(this.editor); geometry.rotateX(Math.PI / 2); break; + case 'CTMesh': + editorObject = new CTMesh(this.editor); + editorObject.pathOnServer = data.pathOnServer; + break; default: // not custom object type } diff --git a/src/WrapperApp/components/Results/Results3D.tsx b/src/WrapperApp/components/Results/Results3D.tsx new file mode 100644 index 000000000..d63d31b74 --- /dev/null +++ b/src/WrapperApp/components/Results/Results3D.tsx @@ -0,0 +1,34 @@ +import { Box, Button, Stack, Typography } from '@mui/material'; +import { Page3D } from '../../../JsRoot/GraphData'; +import { CopyToClipboard } from 'react-copy-to-clipboard'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; + +export function Result3D(props: { page: Page3D; title?: string }) { + const { page, title } = props; + + return ( + spacing(1, 0) }}> + {title ?? 'Quantity'} + + + Url on server: + + {page.resultsUrl} + + + + + + + + ); +} + +export default Result3D; diff --git a/src/WrapperApp/components/Simulation/SimulationPanel.tsx b/src/WrapperApp/components/Simulation/SimulationPanel.tsx index 7ab4cbdd2..eb7bb2c2b 100644 --- a/src/WrapperApp/components/Simulation/SimulationPanel.tsx +++ b/src/WrapperApp/components/Simulation/SimulationPanel.tsx @@ -65,7 +65,7 @@ export default function SimulationPanel(props: SimulationPanelProps) { const [simName, setSimName] = useState(editorRef.current!.toJSON().project.title); const [nTasks, setNTasks] = useState(1); - const [simulator, setSimulator] = useState('shieldhit'); + const [simulator] = useState('shieldhit'); const [inputFiles, setInputFiles] = useState(); const [directRun, setDirectRun] = useState(true); diff --git a/src/WrapperApp/components/Simulation/SimulationProgressBar.tsx b/src/WrapperApp/components/Simulation/SimulationProgressBar.tsx index ec8c59ac2..bc956baef 100644 --- a/src/WrapperApp/components/Simulation/SimulationProgressBar.tsx +++ b/src/WrapperApp/components/Simulation/SimulationProgressBar.tsx @@ -1,4 +1,4 @@ -import { Box, LinearProgress, Divider, Tooltip } from '@mui/material'; +import { Box, LinearProgress, Tooltip } from '@mui/material'; import { TaskStatusData, TaskTime, StatusState } from '../../../services/ResponseTypes'; import { useRef, useCallback, useEffect } from 'react'; import Countdown from 'react-countdown';