From cd1c62718304245ce7e70dce8d141518608f9461 Mon Sep 17 00:00:00 2001 From: Oleksandr Zhynzher Date: Thu, 16 Nov 2023 09:33:49 +0200 Subject: [PATCH 01/12] EPMUII-8317 Add errors handling for load from URL --- src/engine/lib/core/loaders/MRIFileLoader.ts | 22 ++++++++++------ src/engine/lib/core/readers/MRIReader.ts | 7 ++++-- .../AbstractFileReader.ts | 2 +- src/engine/lib/services/StoreService.ts | 5 ++-- src/ui/Main.jsx | 25 ++++++++++++++++--- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/engine/lib/core/loaders/MRIFileLoader.ts b/src/engine/lib/core/loaders/MRIFileLoader.ts index 7738988e..7ac99a77 100644 --- a/src/engine/lib/core/loaders/MRIFileLoader.ts +++ b/src/engine/lib/core/loaders/MRIFileLoader.ts @@ -24,7 +24,7 @@ export class MRIFileLoader { * @param {string} url - The URL of the file to load. * @returns {Promise} A promise that resolves to an array of Files or rejects with an error. */ - async load(url: string): Promise { + async load(url: string): Promise { this.filesLoaded = 0; this.filesProgressByLength = false; this.store.setLoadingProgress(0); @@ -52,15 +52,16 @@ export class MRIFileLoader { * @param {string} url - The URL from which to fetch the file. * @returns {Promise} A promise that resolves to an array containing the fetched file as a File object. */ - async fetchSingleFile(url: string): Promise { + async fetchSingleFile(url: string): Promise { const response = await this.fetchWithProgress(url, this.callbackLoadProgress); if (!response.ok) { this.handleVolumeLoadFailed(`Failed to fetch file from URL: ${url}`); - return []; + return null; } const blob = await response.blob(); + const fileName = getFileNameFromUrl(url); const file = new File([blob], fileName, { type: blob.type, @@ -91,14 +92,15 @@ export class MRIFileLoader { * @param {string} txtUrl - The URL of the .txt file containing the list of file URLs. * @returns {Promise} A promise that resolves to an array of File objects. */ - async fetchTxtFile(txtUrl: string): Promise { + async fetchTxtFile(txtUrl: string): Promise { this.filesProgressByLength = true; const base = txtUrl.substring(0, txtUrl.lastIndexOf('/') + 1); const fileNames = await this.fetchTxtContent(txtUrl); this.filesLength = fileNames.length; const filePromises = fileNames.map((filename: string) => this.fetchSingleFile(base + filename)); const files = await Promise.all(filePromises); - return files.flat(); + const validFalies = files.filter(Boolean) as Array; + return validFalies.flat(); } /** @@ -131,7 +133,9 @@ export class MRIFileLoader { const files = await Promise.all(filePromises); - return files.flat(); + const validFalies = files.filter(Boolean) as Array; + + return validFalies.flat(); } /** @@ -154,6 +158,10 @@ export class MRIFileLoader { }; xhr.onload = () => { + if (xhr.status === 403) { + return this.handleVolumeLoadFailed(`Error 403 Forbiden, failed to fetch file from URL: ${url}`); + } + if (this.filesProgressByLength) { this.filesLoaded = this.filesLoaded + 1; const percentComplete = this.filesLoaded / this.filesLength; @@ -189,6 +197,6 @@ export class MRIFileLoader { */ handleVolumeLoadFailed(error: string) { this.events.emit(MriEvents.FILE_READ_ERROR, { error }); - this.store.setVolumeLoadFailed(this.fileName, [error]); + this.store.setVolumeLoadFailed(this.fileName); } } diff --git a/src/engine/lib/core/readers/MRIReader.ts b/src/engine/lib/core/readers/MRIReader.ts index f4e12778..7a3edae0 100644 --- a/src/engine/lib/core/readers/MRIReader.ts +++ b/src/engine/lib/core/readers/MRIReader.ts @@ -10,8 +10,11 @@ export class MRIReader { if (data && data.length && data[0] instanceof File) { this.fileReader.read(data as File[]); } else if (isValidUrl(data as string)) { - const files: File[] = await this.fileLoader.load(data as string); - this.fileReader.read(files); + const files: File[] | null = await this.fileLoader.load(data as string); + + if (files) { + this.fileReader.read(files); + } } else { throw new Error('Invalid input. Expected a File or URL.'); } diff --git a/src/engine/lib/core/readers/abstract-file-reader/AbstractFileReader.ts b/src/engine/lib/core/readers/abstract-file-reader/AbstractFileReader.ts index bbeb9a6a..ce12de81 100644 --- a/src/engine/lib/core/readers/abstract-file-reader/AbstractFileReader.ts +++ b/src/engine/lib/core/readers/abstract-file-reader/AbstractFileReader.ts @@ -75,7 +75,7 @@ export abstract class AbstractFileReader { */ public handleVolumeReadFailed(error: string) { this.events.emit(MriEvents.FILE_READ_ERROR, { error }); - this.store.setVolumeLoadFailed(this.fileName, [error]); + this.store.setVolumeLoadFailed(this.fileName); } /** diff --git a/src/engine/lib/services/StoreService.ts b/src/engine/lib/services/StoreService.ts index e0b1a876..fda90804 100644 --- a/src/engine/lib/services/StoreService.ts +++ b/src/engine/lib/services/StoreService.ts @@ -80,11 +80,10 @@ export class MRIStoreService { this.dispatchActions(actions); } - public setVolumeLoadFailed(fileName: string, errors: string[]): void { + public setVolumeLoadFailed(fileName: string): void { const actions = [ - { type: StoreActionType.SET_ERR_ARRAY, errors }, { type: StoreActionType.SET_VOLUME_SET, volume: null }, - { type: StoreActionType.SET_FILENAME, fileName: fileName }, + { type: StoreActionType.SET_FILENAME, fileName }, { type: StoreActionType.SET_PROGRESS, progress: 0 }, { type: StoreActionType.SET_SPINNER, spinner: false }, { type: StoreActionType.SET_IS_LOADED, isLoaded: false }, diff --git a/src/ui/Main.jsx b/src/ui/Main.jsx index 1c2c21ac..0568eda7 100644 --- a/src/ui/Main.jsx +++ b/src/ui/Main.jsx @@ -8,7 +8,6 @@ import StoreActionType from '../store/ActionTypes'; import FullScreenToggle from './Toolbars/FullScreen'; import UiModalText from './Modals/UiModalText'; import UiModalAlert from './Modals/ModalAlert'; -import UiErrConsole from './UiErrConsole'; import ModeView from '../store/ViewMode'; import Graphics2d from '../engine/Graphics2d'; import BrowserDetector from '../engine/utils/BrowserDetector'; @@ -28,13 +27,16 @@ import { TopToolbar } from './TopToolbar/TopToolbar'; import { UiAbout } from './Header/UiAbout'; import { MobileSettings } from './MobileSettings/MobileSettings'; import StartScreen from './StartScreen/StartScreen'; +import MriViwer from '../engine/lib/MRIViewer'; +import { MriEvents } from '../engine/lib/enums'; + import css from './Main.module.css'; import cx from 'classnames'; import '../nouislider-custom.css'; export const Main = () => { const dispatch = useDispatch(); - const { arrErrors, isLoaded, progress, spinner, viewMode, showModalText, showModalAlert } = useSelector((state) => state); + const { isLoaded, progress, spinner, viewMode, showModalText, showModalAlert } = useSelector((state) => state); const [m_fileNameOnLoad, setM_fileNameOnLoad] = useState(false); const [isWebGl20supported, setIsWebGl20supported] = useState(true); @@ -43,6 +45,7 @@ export const Main = () => { const [isFullMode, setIsFullMode] = useState(false); const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); const appRef = useRef(); + const mriViwer = useRef(MriViwer).current; useEffect(() => { function handleResize() { @@ -55,6 +58,23 @@ export const Main = () => { window.removeEventListener('resize', handleResize); }; }, []); + + useEffect(() => { + const handleFileReadError = (eventData) => { + setStrAlertTitle('File Read Error'); + setStrAlertText(eventData.error); + onShowModalAlert(); + }; + + // Subscribe to the FILE_READ_ERROR event + mriViwer.events.on(MriEvents.FILE_READ_ERROR, handleFileReadError); + + // Clean up + return () => { + mriViwer.events.off(MriEvents.FILE_READ_ERROR, handleFileReadError); + }; + }, []); + const [, drop] = useDrop( () => ({ accept: DnDItemTypes.SETTINGS, @@ -204,7 +224,6 @@ export const Main = () => { )} - {arrErrors.length > 0 && } {showModalText && ( )} From 5bf503d2080e036940750d5dc53a03a4a6f2e07f Mon Sep 17 00:00:00 2001 From: Oleksandr Iefymchuk Date: Tue, 21 Nov 2023 18:04:02 +0200 Subject: [PATCH 02/12] EPMUII-3204-Center-2D-view --- src/engine/Graphics2d.js | 17 +++--- src/engine/Graphics2d.module.css | 98 +----------------------------- src/engine/tools2d/ToolDistance.js | 17 ++++-- src/engine/tools2d/ToolPick.js | 17 ++++-- src/ui/Main.module.css | 8 +-- 5 files changed, 41 insertions(+), 116 deletions(-) diff --git a/src/engine/Graphics2d.js b/src/engine/Graphics2d.js index 94668922..1104c73a 100644 --- a/src/engine/Graphics2d.js +++ b/src/engine/Graphics2d.js @@ -470,8 +470,8 @@ class Graphics2d extends React.Component { } // centering: setting canvas image size, to match its HTML element's size - objCanvas.width = wScreen; - objCanvas.height = hScreen; + objCanvas.width = this.m_mount.current.clientWidth; + objCanvas.height = this.m_mount.current.clientHeight; // check is segmentation 2d mode is active // const isSegm = store.graphics2dModeSegmentation; // console.log("Segm2d mode = " + isSegm); @@ -481,9 +481,10 @@ class Graphics2d extends React.Component { } // prepareImageForRender fillBackground(ctx) { - const { hRender, wRender } = this.state; + const w = this.m_mount.current.clientWidth; + const h = this.m_mount.current.clientHeight; ctx.fillStyle = '#000000'; - ctx.fillRect(0, 0, wRender, hRender); + ctx.fillRect(0, 0, w, h); } renderReadyImage() { @@ -491,8 +492,6 @@ class Graphics2d extends React.Component { const ctx = objCanvas.getContext('2d'); const store = this.props; const zoom = store.render2dZoom; - const xPos = store.render2dxPos; - const yPos = store.render2dyPos; const canvasWidth = objCanvas.width; const canvasHeight = objCanvas.height; const newImgWidth = canvasWidth / zoom; @@ -532,6 +531,10 @@ class Graphics2d extends React.Component { } else { createImageBitmap(this.imgData) .then((imageBitmap) => { + const centerX = (canvasWidth - this.imgData.width) / 2; + const centerY = (canvasHeight - this.imgData.height) / 2; + const xPos = store.render2dxPos - centerX; + const yPos = store.render2dyPos - centerY; ctx.drawImage(imageBitmap, xPos, yPos, canvasWidth, canvasHeight, 0, 0, newImgWidth, newImgHeight); }) .then(() => { @@ -554,7 +557,7 @@ class Graphics2d extends React.Component { let yPosNew; const store = this.props; const zoom = store.render2dZoom; - const step = evt.deltaY * 2 ** -10; + const step = (evt.deltaY * 2 ** -10) / 2; let newZoom = zoom + step; if (step < 0) { diff --git a/src/engine/Graphics2d.module.css b/src/engine/Graphics2d.module.css index 2f40942b..5f77c3d4 100644 --- a/src/engine/Graphics2d.module.css +++ b/src/engine/Graphics2d.module.css @@ -5,103 +5,11 @@ .wrapperStyles { width: 100%; - height: 100vh; - justify-content: center; - overflow: scroll; + height: 100%; } .canvasStyles { display: block; - width: 150vw; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -@media screen and (min-width: 768px) and (max-width: 1023px) { - .wrapperStyles { - height: calc(100% - 2.5rem); - width: calc(100% - 3rem); - border: 2px solid #dc5e47; - border-radius: 10px; - box-sizing: border-box; - } - - .canvasStyles { - aspect-ratio: 1 / 1; - width: 100vh; - } -} - -@media screen and (orientation: landscape) and (min-width: 768px) and (max-width: 1023px) { - .wrapperStyles { - height: calc(100% - 2.5rem); - width: calc(100%- 3rem); - } - - .canvasStyles { - aspect-ratio: 1 / 1; - } -} - -@media screen and (min-width: 1024px) and (orientation: landscape) { - .wrapperStyles { - height: calc(100% - 0.5rem); - width: 80%; - border: 2px solid #dc5e47; - border-radius: 10px; - } - - .canvasStyles { - margin: 0 auto; - box-sizing: border-box; - } -} - -@media screen and (min-width: 1024px) and (orientation: landscape) and (max-width: 2559px) { - .wrapperStyles { - display: block; - height: calc(100% - 0.5rem); - width: 43%; - overflow: hidden; - border: 2px solid #dc5e47; - border-radius: 10px; - } - - .canvasStyles { - width: auto; - margin: 0 auto; - box-sizing: border-box; - } -} - -@media screen and (min-width: 1024px) and (max-width: 2559px) and (orientation: portrait) { - .wrapperStyles { - width: 90%; - height: calc(100% - 0.5rem); - border: 2px solid #dc5e47; - border-radius: 10px; - display: block; - } - - .canvasStyles { - aspect-ratio: 1/1; - width: 150vw; - } -} - -@media screen and (min-width: 2560px) { - .wrapperStyles { - width: 80%; - border: 2px solid #dc5e47; - border-radius: 10px; - display: flex; - align-items: center; - } - - .canvasStyles { - aspect-ratio: 1/1; - width: auto; - } + width: 100%; + height: 100%; } diff --git a/src/engine/tools2d/ToolDistance.js b/src/engine/tools2d/ToolDistance.js index a77bc873..8a172aa0 100644 --- a/src/engine/tools2d/ToolDistance.js +++ b/src/engine/tools2d/ToolDistance.js @@ -113,9 +113,13 @@ class ToolDistance { const zDim = vol.m_zDim; const objCanvas = store.graphics2d.m_mount.current; const canvasRect = objCanvas.getBoundingClientRect(); + const canvasWidth = canvasRect.width; + const canvasHeight = canvasRect.height; + const centerX = (canvasWidth - wScr) / 2; + const centerY = (canvasHeight - hScr) / 2; + const xPos = (store.render2dxPos - centerX) / wScr; + const yPos = (store.render2dyPos - centerY) / hScr; const zoom = store.render2dZoom; - const xPos = store.render2dxPos / canvasRect.width; - const yPos = store.render2dyPos / canvasRect.height; const vTex = { x: 0.0, @@ -152,9 +156,14 @@ class ToolDistance { const zDim = vol.m_zDim; const objCanvas = store.graphics2d.m_mount.current; const canvasRect = objCanvas.getBoundingClientRect(); + const canvasWidth = canvasRect.width; + const canvasHeight = canvasRect.height; + const centerX = (canvasWidth - wScr) / 2; + const centerY = (canvasHeight - hScr) / 2; + const xPos = (store.render2dxPos - centerX) / wScr; + const yPos = (store.render2dyPos - centerY) / hScr; const zoom = store.render2dZoom; - const xPos = store.render2dxPos / canvasRect.width; - const yPos = store.render2dyPos / canvasRect.height; + if (mode2d === Modes2d.TRANSVERSE) { // z const vScr.x = (xTex / xDim - xPos) / zoom; diff --git a/src/engine/tools2d/ToolPick.js b/src/engine/tools2d/ToolPick.js index 1de1152d..d93b51a3 100644 --- a/src/engine/tools2d/ToolPick.js +++ b/src/engine/tools2d/ToolPick.js @@ -60,9 +60,14 @@ class ToolPick { const zDim = vol.m_zDim; const objCanvas = store.graphics2d.m_mount.current; const canvasRect = objCanvas.getBoundingClientRect(); + const canvasWidth = canvasRect.width; + const canvasHeight = canvasRect.height; + const centerX = (canvasWidth - store.graphics2d.imgData.width) / 2; + const centerY = (canvasHeight - store.graphics2d.imgData.height) / 2; + const xPos = (store.render2dxPos - centerX) / store.graphics2d.imgData.width; + const yPos = (store.render2dyPos - centerY) / store.graphics2d.imgData.height; const zoom = store.render2dZoom; - const xPos = store.render2dxPos / canvasRect.width; - const yPos = store.render2dyPos / canvasRect.height; + if (mode2d === Modes2d.TRANSVERSE) { // z: const vTex.x = Math.floor((xPos + xScr * zoom) * xDim); @@ -92,16 +97,16 @@ class ToolPick { const xRatioImage = xScr / this.m_wScreen; const yRatioImage = yScr / this.m_hScreen; - if (xRatioImage > 1.0 || yRatioImage > 1.0) { - // out if rendered image - return; - } const vTex = this.screenToTexture(xRatioImage, yRatioImage, store); const volSet = store.volumeSet; const vol = volSet.getVolume(store.volumeIndex); const xDim = vol.m_xDim; const yDim = vol.m_yDim; + + if (vTex.x < 0 || vTex.y < 0 || vTex.z < 0 || vTex.x >= vol.m_xDim || vTex.y >= vol.m_yDim) { + return; + } /* if (mode2d === Modes2d.SAGGITAL) { // x diff --git a/src/ui/Main.module.css b/src/ui/Main.module.css index b5f8a3fe..c34629c0 100644 --- a/src/ui/Main.module.css +++ b/src/ui/Main.module.css @@ -68,7 +68,7 @@ position: absolute; right: 0; top: 50%; - z-index: 10; + z-index: 2; } .left { display: none; @@ -81,6 +81,7 @@ right: 0; bottom: 0; z-index: 1; + width: 100%; user-select: none; } @@ -127,8 +128,7 @@ display: block; position: absolute; left: 25px; - top: 12%; - z-index: 1010; + top: 15%; } .bottleft { @@ -216,7 +216,7 @@ } .left { - top: 10%; + top: 15%; } } From a197d27565952abb8af1434f84e718b9a47189a0 Mon Sep 17 00:00:00 2001 From: Dmytro Ilnytskyy Date: Thu, 30 Nov 2023 23:30:49 +0200 Subject: [PATCH 03/12] EPMUII-8819: have fixed recently opened files section for mobile devices --- .../RecentlyFiles/RecentlyFiles.module.css | 20 +++++++++++++++++-- src/ui/StartScreen/StartScreen.module.css | 4 ++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/ui/StartScreen/RecentlyFiles/RecentlyFiles.module.css b/src/ui/StartScreen/RecentlyFiles/RecentlyFiles.module.css index 227e3ddd..3b9ce34a 100644 --- a/src/ui/StartScreen/RecentlyFiles/RecentlyFiles.module.css +++ b/src/ui/StartScreen/RecentlyFiles/RecentlyFiles.module.css @@ -23,8 +23,24 @@ .left { color: var(--dark-gray4); - margin-right: 20px; - flex-basis: 50%; + margin-right: 2%; + flex-basis: 40%; + overflow: hidden; +} +.right { + flex-basis: 58%; + overflow: hidden; +} + +.element { + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.element:not(:first-child) { + margin-top: 10px; } @media screen and (min-width: 768px) { diff --git a/src/ui/StartScreen/StartScreen.module.css b/src/ui/StartScreen/StartScreen.module.css index 88ccdffb..a60e9f7a 100644 --- a/src/ui/StartScreen/StartScreen.module.css +++ b/src/ui/StartScreen/StartScreen.module.css @@ -21,6 +21,10 @@ display: none; } +.container { + width: 400px; +} + @media screen and (min-width: 768px) { .subheader { margin: 0; From 37e48291fb8a188c5d6885d7f03dda9dc0250afb Mon Sep 17 00:00:00 2001 From: Vika Abysova Date: Sat, 9 Dec 2023 23:35:02 +0200 Subject: [PATCH 04/12] fixing drag box --- package-lock.json | 104 ++++++++++---------- package.json | 4 +- src/ui/DragAndDrop/DragAndDropContainer.jsx | 82 ++++++--------- src/ui/Main.module.css | 5 +- 4 files changed, 84 insertions(+), 111 deletions(-) diff --git a/package-lock.json b/package-lock.json index 23cafe6b..4a7fc38a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,8 @@ "daikon": "^1.2.42", "jszip": "^3.10.1", "react": "^17.0.2", - "react-dnd": "^14.0.2", - "react-dnd-html5-backend": "^14.0.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", "react-dom": "^17.0.2", "react-modal": "^3.14.2", "react-popper": "^2.2.5", @@ -4099,19 +4099,19 @@ } }, "node_modules/@react-dnd/asap": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", - "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" }, "node_modules/@react-dnd/invariant": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", - "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" }, "node_modules/@react-dnd/shallowequal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", - "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, "node_modules/@rollup/plugin-node-resolve": { "version": "7.1.3", @@ -9199,13 +9199,13 @@ } }, "node_modules/dnd-core": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-14.0.1.tgz", - "integrity": "sha512-+PVS2VPTgKFPYWo3vAFEA8WPbTf7/xo43TifH9G8S1KqnrQu0o77A3unrF5yOugy4mIz7K5wAVFHUcha7wsz6A==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", "dependencies": { - "@react-dnd/asap": "^4.0.0", - "@react-dnd/invariant": "^2.0.0", - "redux": "^4.1.1" + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" } }, "node_modules/dnd-core/node_modules/redux": { @@ -20638,13 +20638,13 @@ } }, "node_modules/react-dnd": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-14.0.5.tgz", - "integrity": "sha512-9i1jSgbyVw0ELlEVt/NkCUkxy1hmhJOkePoCH713u75vzHGyXhPDm28oLfc2NMSBjZRM1Y+wRjHXJT3sPrTy+A==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", "dependencies": { - "@react-dnd/invariant": "^2.0.0", - "@react-dnd/shallowequal": "^2.0.0", - "dnd-core": "14.0.1", + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" }, @@ -20667,11 +20667,11 @@ } }, "node_modules/react-dnd-html5-backend": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-14.1.0.tgz", - "integrity": "sha512-6ONeqEC3XKVf4eVmMTe0oPds+c5B9Foyj8p/ZKLb7kL2qh9COYxiBHv3szd6gztqi/efkmriywLUVlPotqoJyw==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", "dependencies": { - "dnd-core": "14.0.1" + "dnd-core": "^16.0.1" } }, "node_modules/react-dom": { @@ -29621,19 +29621,19 @@ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, "@react-dnd/asap": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", - "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" }, "@react-dnd/invariant": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", - "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" }, "@react-dnd/shallowequal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", - "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, "@rollup/plugin-node-resolve": { "version": "7.1.3", @@ -33550,13 +33550,13 @@ } }, "dnd-core": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-14.0.1.tgz", - "integrity": "sha512-+PVS2VPTgKFPYWo3vAFEA8WPbTf7/xo43TifH9G8S1KqnrQu0o77A3unrF5yOugy4mIz7K5wAVFHUcha7wsz6A==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", "requires": { - "@react-dnd/asap": "^4.0.0", - "@react-dnd/invariant": "^2.0.0", - "redux": "^4.1.1" + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" }, "dependencies": { "redux": { @@ -42328,23 +42328,23 @@ } }, "react-dnd": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-14.0.5.tgz", - "integrity": "sha512-9i1jSgbyVw0ELlEVt/NkCUkxy1hmhJOkePoCH713u75vzHGyXhPDm28oLfc2NMSBjZRM1Y+wRjHXJT3sPrTy+A==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", "requires": { - "@react-dnd/invariant": "^2.0.0", - "@react-dnd/shallowequal": "^2.0.0", - "dnd-core": "14.0.1", + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" } }, "react-dnd-html5-backend": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-14.1.0.tgz", - "integrity": "sha512-6ONeqEC3XKVf4eVmMTe0oPds+c5B9Foyj8p/ZKLb7kL2qh9COYxiBHv3szd6gztqi/efkmriywLUVlPotqoJyw==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", "requires": { - "dnd-core": "14.0.1" + "dnd-core": "^16.0.1" } }, "react-dom": { diff --git a/package.json b/package.json index 446e0330..12e0cf86 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,8 @@ "daikon": "^1.2.42", "jszip": "^3.10.1", "react": "^17.0.2", - "react-dnd": "^14.0.2", - "react-dnd-html5-backend": "^14.0.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", "react-dom": "^17.0.2", "react-modal": "^3.14.2", "react-popper": "^2.2.5", diff --git a/src/ui/DragAndDrop/DragAndDropContainer.jsx b/src/ui/DragAndDrop/DragAndDropContainer.jsx index ec1697be..fc63ce12 100644 --- a/src/ui/DragAndDrop/DragAndDropContainer.jsx +++ b/src/ui/DragAndDrop/DragAndDropContainer.jsx @@ -1,65 +1,41 @@ -import React, { useCallback, useState } from 'react'; +import React, { useState } from 'react'; import css from '../Main.module.css'; -import { DnDItemTypes } from '../Constants/DnDItemTypes'; -import { useDrag } from 'react-dnd'; export const DragAndDropContainer = ({ children }) => { - const [position, setPosition] = useState({ top: 100, left: null }); - const [isCanDrag, setIsCanDrag] = useState(false); + const [position, setPosition] = useState({ top: 100, left: 900 }); + const [isDragging, setIsDragging] = useState(false); + const [offset, setOffset] = useState({ x: 0, y: 0 }); - const checkEventTagName = useCallback( - (e) => { - e.target.tagName === 'DIV' ? setIsCanDrag(true) : setIsCanDrag(false); - }, - [isCanDrag, setIsCanDrag] - ); + const startDrag = (e) => { + if (e.target.tagName.toLowerCase() !== 'span') { + setIsDragging(true); + setOffset({ + x: e.clientX - position.left, + y: e.clientY - position.top, + }); + } + }; - const getLeftPositionSettings = useCallback( - (e) => { - if (position.left === null) { - setPosition({ - top: position.top, - left: window.innerWidth - e.currentTarget.offsetWidth - 25, - }); - } - }, - [position] - ); + const stopDrag = () => { + setIsDragging(false); + }; - const [{ isDragging }, drag, dragPreview] = useDrag( - () => ({ - type: DnDItemTypes.SETTINGS, - item: { left: position.left, top: position.top, isCanDrag }, - options: { - dropEffect: 'move', - }, - end: (item, monitor) => { - const { x, y } = monitor.getDropResult(); - const top = Math.round(item.top + y); - const left = Math.round(item.left + x); - setPosition({ top, left }); - }, - canDrag: () => { - return isCanDrag; - }, - collect: (monitor) => ({ - isDragging: monitor.isDragging(), - }), - }), - [position.left, position.top, isCanDrag] - ); + const handleDrag = (e) => { + if (isDragging) { + const x = e.clientX - offset.x; + const y = e.clientY - offset.y; + setPosition({ left: x, top: y }); + } + }; - return isDragging ? ( -
- {children} -
- ) : ( + return (
Date: Tue, 12 Dec 2023 20:05:55 -0800 Subject: [PATCH 05/12] Enable beta AI segmentation --- src/engine/PATH_MODEL.json | 1870 ----------------- src/engine/Segm2d.js | 52 +- .../Properties2d/SegmentationProperty.jsx | 4 +- 3 files changed, 30 insertions(+), 1896 deletions(-) delete mode 100644 src/engine/PATH_MODEL.json diff --git a/src/engine/PATH_MODEL.json b/src/engine/PATH_MODEL.json deleted file mode 100644 index 035a59bf..00000000 --- a/src/engine/PATH_MODEL.json +++ /dev/null @@ -1,1870 +0,0 @@ -{ - "format": "layers-model", - "generatedBy": "keras v2.3.1", - "convertedBy": "TensorFlow.js Converter v1.5.2", - "modelTopology": { - "keras_version": "2.3.1", - "backend": "tensorflow", - "model_config": { - "class_name": "Model", - "config": { - "name": "model_4", - "layers": [ - { - "name": "input_1", - "class_name": "InputLayer", - "config": { - "batch_input_shape": [ - null, - 320, - 480, - 3 - ], - "dtype": "float32", - "sparse": false, - "name": "input_1" - }, - "inbound_nodes": [] - }, - { - "name": "block1_conv1", - "class_name": "Conv2D", - "config": { - "name": "block1_conv1", - "trainable": true, - "dtype": "float32", - "filters": 64, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "input_1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block1_conv2", - "class_name": "Conv2D", - "config": { - "name": "block1_conv2", - "trainable": true, - "dtype": "float32", - "filters": 64, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block1_conv1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block1_pool", - "class_name": "MaxPooling2D", - "config": { - "name": "block1_pool", - "trainable": true, - "dtype": "float32", - "pool_size": [ - 2, - 2 - ], - "padding": "valid", - "strides": [ - 2, - 2 - ], - "data_format": "channels_last" - }, - "inbound_nodes": [ - [ - [ - "block1_conv2", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block2_conv1", - "class_name": "Conv2D", - "config": { - "name": "block2_conv1", - "trainable": true, - "dtype": "float32", - "filters": 128, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block1_pool", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block2_conv2", - "class_name": "Conv2D", - "config": { - "name": "block2_conv2", - "trainable": true, - "dtype": "float32", - "filters": 128, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block2_conv1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block2_pool", - "class_name": "MaxPooling2D", - "config": { - "name": "block2_pool", - "trainable": true, - "dtype": "float32", - "pool_size": [ - 2, - 2 - ], - "padding": "valid", - "strides": [ - 2, - 2 - ], - "data_format": "channels_last" - }, - "inbound_nodes": [ - [ - [ - "block2_conv2", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block3_conv1", - "class_name": "Conv2D", - "config": { - "name": "block3_conv1", - "trainable": true, - "dtype": "float32", - "filters": 256, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block2_pool", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block3_conv2", - "class_name": "Conv2D", - "config": { - "name": "block3_conv2", - "trainable": true, - "dtype": "float32", - "filters": 256, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block3_conv1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block3_conv3", - "class_name": "Conv2D", - "config": { - "name": "block3_conv3", - "trainable": true, - "dtype": "float32", - "filters": 256, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block3_conv2", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block3_pool", - "class_name": "MaxPooling2D", - "config": { - "name": "block3_pool", - "trainable": true, - "dtype": "float32", - "pool_size": [ - 2, - 2 - ], - "padding": "valid", - "strides": [ - 2, - 2 - ], - "data_format": "channels_last" - }, - "inbound_nodes": [ - [ - [ - "block3_conv3", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block4_conv1", - "class_name": "Conv2D", - "config": { - "name": "block4_conv1", - "trainable": true, - "dtype": "float32", - "filters": 512, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block3_pool", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block4_conv2", - "class_name": "Conv2D", - "config": { - "name": "block4_conv2", - "trainable": true, - "dtype": "float32", - "filters": 512, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block4_conv1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block4_conv3", - "class_name": "Conv2D", - "config": { - "name": "block4_conv3", - "trainable": true, - "dtype": "float32", - "filters": 512, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "relu", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "block4_conv2", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "block4_pool", - "class_name": "MaxPooling2D", - "config": { - "name": "block4_pool", - "trainable": true, - "dtype": "float32", - "pool_size": [ - 2, - 2 - ], - "padding": "valid", - "strides": [ - 2, - 2 - ], - "data_format": "channels_last" - }, - "inbound_nodes": [ - [ - [ - "block4_conv3", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "zero_padding2d_1", - "class_name": "ZeroPadding2D", - "config": { - "name": "zero_padding2d_1", - "trainable": true, - "dtype": "float32", - "padding": [ - [ - 1, - 1 - ], - [ - 1, - 1 - ] - ], - "data_format": "channels_last" - }, - "inbound_nodes": [ - [ - [ - "block4_pool", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "conv2d_1", - "class_name": "Conv2D", - "config": { - "name": "conv2d_1", - "trainable": true, - "dtype": "float32", - "filters": 512, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "valid", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "zero_padding2d_1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "batch_normalization_1", - "class_name": "BatchNormalization", - "config": { - "name": "batch_normalization_1", - "trainable": true, - "dtype": "float32", - "axis": -1, - "momentum": 0.99, - "epsilon": 0.001, - "center": true, - "scale": true, - "beta_initializer": { - "class_name": "Zeros", - "config": {} - }, - "gamma_initializer": { - "class_name": "Ones", - "config": {} - }, - "moving_mean_initializer": { - "class_name": "Zeros", - "config": {} - }, - "moving_variance_initializer": { - "class_name": "Ones", - "config": {} - }, - "beta_regularizer": null, - "gamma_regularizer": null, - "beta_constraint": null, - "gamma_constraint": null - }, - "inbound_nodes": [ - [ - [ - "conv2d_1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "up_sampling2d_1", - "class_name": "UpSampling2D", - "config": { - "name": "up_sampling2d_1", - "trainable": true, - "dtype": "float32", - "size": [ - 2, - 2 - ], - "data_format": "channels_last", - "interpolation": "nearest" - }, - "inbound_nodes": [ - [ - [ - "batch_normalization_1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "concatenate_1", - "class_name": "Concatenate", - "config": { - "name": "concatenate_1", - "trainable": true, - "dtype": "float32", - "axis": -1 - }, - "inbound_nodes": [ - [ - [ - "up_sampling2d_1", - 0, - 0, - {} - ], - [ - "block3_pool", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "zero_padding2d_2", - "class_name": "ZeroPadding2D", - "config": { - "name": "zero_padding2d_2", - "trainable": true, - "dtype": "float32", - "padding": [ - [ - 1, - 1 - ], - [ - 1, - 1 - ] - ], - "data_format": "channels_last" - }, - "inbound_nodes": [ - [ - [ - "concatenate_1", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "conv2d_2", - "class_name": "Conv2D", - "config": { - "name": "conv2d_2", - "trainable": true, - "dtype": "float32", - "filters": 256, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "valid", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "zero_padding2d_2", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "batch_normalization_2", - "class_name": "BatchNormalization", - "config": { - "name": "batch_normalization_2", - "trainable": true, - "dtype": "float32", - "axis": -1, - "momentum": 0.99, - "epsilon": 0.001, - "center": true, - "scale": true, - "beta_initializer": { - "class_name": "Zeros", - "config": {} - }, - "gamma_initializer": { - "class_name": "Ones", - "config": {} - }, - "moving_mean_initializer": { - "class_name": "Zeros", - "config": {} - }, - "moving_variance_initializer": { - "class_name": "Ones", - "config": {} - }, - "beta_regularizer": null, - "gamma_regularizer": null, - "beta_constraint": null, - "gamma_constraint": null - }, - "inbound_nodes": [ - [ - [ - "conv2d_2", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "up_sampling2d_2", - "class_name": "UpSampling2D", - "config": { - "name": "up_sampling2d_2", - "trainable": true, - "dtype": "float32", - "size": [ - 2, - 2 - ], - "data_format": "channels_last", - "interpolation": "nearest" - }, - "inbound_nodes": [ - [ - [ - "batch_normalization_2", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "concatenate_2", - "class_name": "Concatenate", - "config": { - "name": "concatenate_2", - "trainable": true, - "dtype": "float32", - "axis": -1 - }, - "inbound_nodes": [ - [ - [ - "up_sampling2d_2", - 0, - 0, - {} - ], - [ - "block2_pool", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "zero_padding2d_3", - "class_name": "ZeroPadding2D", - "config": { - "name": "zero_padding2d_3", - "trainable": true, - "dtype": "float32", - "padding": [ - [ - 1, - 1 - ], - [ - 1, - 1 - ] - ], - "data_format": "channels_last" - }, - "inbound_nodes": [ - [ - [ - "concatenate_2", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "conv2d_3", - "class_name": "Conv2D", - "config": { - "name": "conv2d_3", - "trainable": true, - "dtype": "float32", - "filters": 128, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "valid", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "zero_padding2d_3", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "batch_normalization_3", - "class_name": "BatchNormalization", - "config": { - "name": "batch_normalization_3", - "trainable": true, - "dtype": "float32", - "axis": -1, - "momentum": 0.99, - "epsilon": 0.001, - "center": true, - "scale": true, - "beta_initializer": { - "class_name": "Zeros", - "config": {} - }, - "gamma_initializer": { - "class_name": "Ones", - "config": {} - }, - "moving_mean_initializer": { - "class_name": "Zeros", - "config": {} - }, - "moving_variance_initializer": { - "class_name": "Ones", - "config": {} - }, - "beta_regularizer": null, - "gamma_regularizer": null, - "beta_constraint": null, - "gamma_constraint": null - }, - "inbound_nodes": [ - [ - [ - "conv2d_3", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "up_sampling2d_3", - "class_name": "UpSampling2D", - "config": { - "name": "up_sampling2d_3", - "trainable": true, - "dtype": "float32", - "size": [ - 2, - 2 - ], - "data_format": "channels_last", - "interpolation": "nearest" - }, - "inbound_nodes": [ - [ - [ - "batch_normalization_3", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "concatenate_3", - "class_name": "Concatenate", - "config": { - "name": "concatenate_3", - "trainable": true, - "dtype": "float32", - "axis": -1 - }, - "inbound_nodes": [ - [ - [ - "up_sampling2d_3", - 0, - 0, - {} - ], - [ - "block1_pool", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "zero_padding2d_4", - "class_name": "ZeroPadding2D", - "config": { - "name": "zero_padding2d_4", - "trainable": true, - "dtype": "float32", - "padding": [ - [ - 1, - 1 - ], - [ - 1, - 1 - ] - ], - "data_format": "channels_last" - }, - "inbound_nodes": [ - [ - [ - "concatenate_3", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "conv2d_4", - "class_name": "Conv2D", - "config": { - "name": "conv2d_4", - "trainable": true, - "dtype": "float32", - "filters": 64, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "valid", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "zero_padding2d_4", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "batch_normalization_4", - "class_name": "BatchNormalization", - "config": { - "name": "batch_normalization_4", - "trainable": true, - "dtype": "float32", - "axis": -1, - "momentum": 0.99, - "epsilon": 0.001, - "center": true, - "scale": true, - "beta_initializer": { - "class_name": "Zeros", - "config": {} - }, - "gamma_initializer": { - "class_name": "Ones", - "config": {} - }, - "moving_mean_initializer": { - "class_name": "Zeros", - "config": {} - }, - "moving_variance_initializer": { - "class_name": "Ones", - "config": {} - }, - "beta_regularizer": null, - "gamma_regularizer": null, - "beta_constraint": null, - "gamma_constraint": null - }, - "inbound_nodes": [ - [ - [ - "conv2d_4", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "conv2d_5", - "class_name": "Conv2D", - "config": { - "name": "conv2d_5", - "trainable": true, - "dtype": "float32", - "filters": 96, - "kernel_size": [ - 3, - 3 - ], - "strides": [ - 1, - 1 - ], - "padding": "same", - "data_format": "channels_last", - "dilation_rate": [ - 1, - 1 - ], - "activation": "linear", - "use_bias": true, - "kernel_initializer": { - "class_name": "VarianceScaling", - "config": { - "scale": 1.0, - "mode": "fan_avg", - "distribution": "uniform", - "seed": null - } - }, - "bias_initializer": { - "class_name": "Zeros", - "config": {} - }, - "kernel_regularizer": null, - "bias_regularizer": null, - "activity_regularizer": null, - "kernel_constraint": null, - "bias_constraint": null - }, - "inbound_nodes": [ - [ - [ - "batch_normalization_4", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "reshape_1", - "class_name": "Reshape", - "config": { - "name": "reshape_1", - "trainable": true, - "dtype": "float32", - "target_shape": [ - 38400, - -1 - ] - }, - "inbound_nodes": [ - [ - [ - "conv2d_5", - 0, - 0, - {} - ] - ] - ] - }, - { - "name": "activation_1", - "class_name": "Activation", - "config": { - "name": "activation_1", - "trainable": true, - "dtype": "float32", - "activation": "softmax" - }, - "inbound_nodes": [ - [ - [ - "reshape_1", - 0, - 0, - {} - ] - ] - ] - } - ], - "input_layers": [ - [ - "input_1", - 0, - 0 - ] - ], - "output_layers": [ - [ - "activation_1", - 0, - 0 - ] - ] - } - }, - "training_config": { - "optimizer_config": { - "class_name": "Adadelta", - "config": { - "learning_rate": 1.0, - "rho": 0.95, - "decay": 0.0, - "epsilon": 1e-07 - } - }, - "loss": "categorical_crossentropy", - "metrics": [ - "accuracy" - ], - "weighted_metrics": null, - "sample_weight_mode": null, - "loss_weights": null - } - }, - "weightsManifest": [ - { - "paths": [ - "group1-shard1of12.bin", - "group1-shard2of12.bin", - "group1-shard3of12.bin", - "group1-shard4of12.bin", - "group1-shard5of12.bin", - "group1-shard6of12.bin", - "group1-shard7of12.bin", - "group1-shard8of12.bin", - "group1-shard9of12.bin", - "group1-shard10of12.bin", - "group1-shard11of12.bin", - "group1-shard12of12.bin" - ], - "weights": [ - { - "name": "batch_normalization_1/gamma", - "shape": [ - 512 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_1/beta", - "shape": [ - 512 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_1/moving_mean", - "shape": [ - 512 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_1/moving_variance", - "shape": [ - 512 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_2/gamma", - "shape": [ - 256 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_2/beta", - "shape": [ - 256 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_2/moving_mean", - "shape": [ - 256 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_2/moving_variance", - "shape": [ - 256 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_3/gamma", - "shape": [ - 128 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_3/beta", - "shape": [ - 128 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_3/moving_mean", - "shape": [ - 128 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_3/moving_variance", - "shape": [ - 128 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_4/gamma", - "shape": [ - 64 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_4/beta", - "shape": [ - 64 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_4/moving_mean", - "shape": [ - 64 - ], - "dtype": "float32" - }, - { - "name": "batch_normalization_4/moving_variance", - "shape": [ - 64 - ], - "dtype": "float32" - }, - { - "name": "block1_conv1/kernel", - "shape": [ - 3, - 3, - 3, - 64 - ], - "dtype": "float32" - }, - { - "name": "block1_conv1/bias", - "shape": [ - 64 - ], - "dtype": "float32" - }, - { - "name": "block1_conv2/kernel", - "shape": [ - 3, - 3, - 64, - 64 - ], - "dtype": "float32" - }, - { - "name": "block1_conv2/bias", - "shape": [ - 64 - ], - "dtype": "float32" - }, - { - "name": "block2_conv1/kernel", - "shape": [ - 3, - 3, - 64, - 128 - ], - "dtype": "float32" - }, - { - "name": "block2_conv1/bias", - "shape": [ - 128 - ], - "dtype": "float32" - }, - { - "name": "block2_conv2/kernel", - "shape": [ - 3, - 3, - 128, - 128 - ], - "dtype": "float32" - }, - { - "name": "block2_conv2/bias", - "shape": [ - 128 - ], - "dtype": "float32" - }, - { - "name": "block3_conv1/kernel", - "shape": [ - 3, - 3, - 128, - 256 - ], - "dtype": "float32" - }, - { - "name": "block3_conv1/bias", - "shape": [ - 256 - ], - "dtype": "float32" - }, - { - "name": "block3_conv2/kernel", - "shape": [ - 3, - 3, - 256, - 256 - ], - "dtype": "float32" - }, - { - "name": "block3_conv2/bias", - "shape": [ - 256 - ], - "dtype": "float32" - }, - { - "name": "block3_conv3/kernel", - "shape": [ - 3, - 3, - 256, - 256 - ], - "dtype": "float32" - }, - { - "name": "block3_conv3/bias", - "shape": [ - 256 - ], - "dtype": "float32" - }, - { - "name": "block4_conv1/kernel", - "shape": [ - 3, - 3, - 256, - 512 - ], - "dtype": "float32" - }, - { - "name": "block4_conv1/bias", - "shape": [ - 512 - ], - "dtype": "float32" - }, - { - "name": "block4_conv2/kernel", - "shape": [ - 3, - 3, - 512, - 512 - ], - "dtype": "float32" - }, - { - "name": "block4_conv2/bias", - "shape": [ - 512 - ], - "dtype": "float32" - }, - { - "name": "block4_conv3/kernel", - "shape": [ - 3, - 3, - 512, - 512 - ], - "dtype": "float32" - }, - { - "name": "block4_conv3/bias", - "shape": [ - 512 - ], - "dtype": "float32" - }, - { - "name": "conv2d_1/kernel", - "shape": [ - 3, - 3, - 512, - 512 - ], - "dtype": "float32" - }, - { - "name": "conv2d_1/bias", - "shape": [ - 512 - ], - "dtype": "float32" - }, - { - "name": "conv2d_2/kernel", - "shape": [ - 3, - 3, - 768, - 256 - ], - "dtype": "float32" - }, - { - "name": "conv2d_2/bias", - "shape": [ - 256 - ], - "dtype": "float32" - }, - { - "name": "conv2d_3/kernel", - "shape": [ - 3, - 3, - 384, - 128 - ], - "dtype": "float32" - }, - { - "name": "conv2d_3/bias", - "shape": [ - 128 - ], - "dtype": "float32" - }, - { - "name": "conv2d_4/kernel", - "shape": [ - 3, - 3, - 192, - 64 - ], - "dtype": "float32" - }, - { - "name": "conv2d_4/bias", - "shape": [ - 64 - ], - "dtype": "float32" - }, - { - "name": "conv2d_5/kernel", - "shape": [ - 3, - 3, - 64, - 96 - ], - "dtype": "float32" - }, - { - "name": "conv2d_5/bias", - "shape": [ - 96 - ], - "dtype": "float32" - } - ] - } - ] -} \ No newline at end of file diff --git a/src/engine/Segm2d.js b/src/engine/Segm2d.js index 80c1ee22..6e1fe80b 100644 --- a/src/engine/Segm2d.js +++ b/src/engine/Segm2d.js @@ -14,7 +14,7 @@ // ******************************************************** import * as tf from '@tensorflow/tfjs'; -import PATH_MODEL from './PATH_MODEL.json'; +const PATH_MODEL = 'http://lugachai.ru/med3web/tfjs/model.json'; // ******************************************************** // Const @@ -35,7 +35,6 @@ const STAGE_MODEL_IS_LOADING = 1; const STAGE_MODEL_READY = 2; const STAGE_IMAGE_PROCESSED = 3; const STAGE_SEGMENTATION_READY = 4; -// const STAGE_READY_NEXT_IMAGE = 5; const OUT_W = 240; const OUT_H = 160; @@ -136,6 +135,9 @@ class Segm2d { } async startApplyImage() { + if (this.stage === STAGE_SEGMENTATION_READY) { + return; + } this.stage = STAGE_IMAGE_PROCESSED; console.log('Start apply segm to image ...'); @@ -267,9 +269,9 @@ class Segm2d { getStageString() { return [ 'Wait. Model is not loaded', // const STAGE_MODEL_NOT_LOADED = 0; - 'Wait. Model is loading ...', // const STAGE_MODEL_IS_LOADING = 1; + 'Loading TensorFlow model from server...', // const STAGE_MODEL_IS_LOADING = 1; 'Model is ready', // const STAGE_MODEL_READY = 2; - 'Image is processed ...', // const STAGE_IMAGE_PROCESSED = 3; + 'Image is processed...', // const STAGE_IMAGE_PROCESSED = 3; 'Segmentation is ready', // const STAGE_SEGMENTATION_READY = 4; ][this.stage]; } @@ -288,7 +290,6 @@ class Segm2d { const strMessage = this.getStageString(); console.log('Segm2d render. stage = ' + strMessage); - /* // load model if (this.model === null) { this.onLoadModel(); @@ -299,7 +300,6 @@ class Segm2d { return; } } // if model non null - */ if (this.stage === STAGE_SEGMENTATION_READY && this.pixels !== null) { // draw pixels array on screen @@ -315,26 +315,32 @@ class Segm2d { } // clear screen - ctx.fillStyle = 'rgb(64, 64, 64)'; - ctx.fillRect(0, 0, w, h); - // draw cross - ctx.strokeStyle = '#FF0000'; - - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.lineTo(w - 1, h - 1); - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(w - 1, 0); - ctx.lineTo(0, h - 1); - ctx.stroke(); + ctx.fillStyle = '#242424'; + drawRoundedRect(ctx, 30, 0, w, h, 20); + // draw wait message const strMsgPrint = this.getStageString(); - ctx.font = '24px serif'; - ctx.fillStyle = 'rgb(64, 255, 64)'; - ctx.fillText(strMsgPrint, w / 2, h / 2); + ctx.font = '24px sans-serif'; + ctx.fillStyle = '#dc5e47'; + const textWidth = ctx.measureText(strMsgPrint).width; + const x = (w - textWidth) / 2; + const y = h / 2; + ctx.fillText(strMsgPrint, x, y); } } +function drawRoundedRect(ctx, x, y, width, height, borderRadius) { + if (width < 2 * borderRadius) borderRadius = width / 2; + if (height < 2 * borderRadius) borderRadius = height / 2; + ctx.beginPath(); + ctx.moveTo(x + borderRadius, y); + ctx.arcTo(x + width, y, x + width, y + height, borderRadius); + ctx.arcTo(x + width, y + height, x, y + height, borderRadius); + ctx.arcTo(x, y + height, x, y, borderRadius); + ctx.arcTo(x, y, x + width, y, borderRadius); + ctx.closePath(); + ctx.stroke(); + ctx.fill(); +} + export default Segm2d; diff --git a/src/ui/Panels/Properties2d/SegmentationProperty.jsx b/src/ui/Panels/Properties2d/SegmentationProperty.jsx index f12274e5..35c4726e 100644 --- a/src/ui/Panels/Properties2d/SegmentationProperty.jsx +++ b/src/ui/Panels/Properties2d/SegmentationProperty.jsx @@ -31,12 +31,10 @@ export const SegmentationProperty = () => { return ( <> - You can use automatic 2d image segmentation only for brain-like data - Segmentation 2d (brain only) + AI Brain Segmentation (Beta): - Switch checker above on and see segmentation result on right ); }; From 2f67b5e51be24153a78b7ad9bf9d7d378b9d8f9a Mon Sep 17 00:00:00 2001 From: Danil Rostov Date: Tue, 12 Dec 2023 20:13:55 -0800 Subject: [PATCH 06/12] Use HTTPS --- src/engine/Segm2d.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/Segm2d.js b/src/engine/Segm2d.js index 6e1fe80b..0f73ecca 100644 --- a/src/engine/Segm2d.js +++ b/src/engine/Segm2d.js @@ -14,7 +14,7 @@ // ******************************************************** import * as tf from '@tensorflow/tfjs'; -const PATH_MODEL = 'http://lugachai.ru/med3web/tfjs/model.json'; +const PATH_MODEL = 'https://lugachai.ru/med3web/tfjs/model.json'; // ******************************************************** // Const From eaa05de329f090e8faa5de2235b873a992799166 Mon Sep 17 00:00:00 2001 From: Vika Abysova Date: Wed, 13 Dec 2023 15:58:18 +0200 Subject: [PATCH 07/12] update build --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 12e0cf86..446e0330 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,8 @@ "daikon": "^1.2.42", "jszip": "^3.10.1", "react": "^17.0.2", - "react-dnd": "^16.0.1", - "react-dnd-html5-backend": "^16.0.1", + "react-dnd": "^14.0.2", + "react-dnd-html5-backend": "^14.0.0", "react-dom": "^17.0.2", "react-modal": "^3.14.2", "react-popper": "^2.2.5", From b84b744ca2b5e18372560d47ddc34417c24f7cb2 Mon Sep 17 00:00:00 2001 From: Oleksandr Iefymchuk Date: Wed, 13 Dec 2023 17:40:26 +0200 Subject: [PATCH 08/12] Add plain drawinf tool for 2D --- package-lock.json | 92 ++++++++++++++++++ package.json | 1 + public/sprite.svg | 10 +- src/engine/Graphics2d.js | 28 +++++- src/engine/tools2d/ToolDelete.js | 3 +- src/engine/tools2d/ToolPaint.js | 112 ++++++++++++++++++++++ src/engine/tools2d/ToolTypes.js | 1 + src/store/ActionTypes.js | 1 + src/store/Store.js | 3 + src/ui/Header/Header.module.css | 1 + src/ui/Main.module.css | 2 +- src/ui/Panels/ColorPicker/ColorPicker.css | 9 ++ src/ui/Panels/ColorPicker/ColorPicker.jsx | 18 ++++ src/ui/Panels/Mode2dSettingsPanel.jsx | 19 +++- src/ui/TopToolbar/ExploreTools.jsx | 6 ++ src/ui/UiZoomTools.jsx | 8 +- 16 files changed, 302 insertions(+), 12 deletions(-) create mode 100644 src/engine/tools2d/ToolPaint.js create mode 100644 src/ui/Panels/ColorPicker/ColorPicker.css create mode 100644 src/ui/Panels/ColorPicker/ColorPicker.jsx diff --git a/package-lock.json b/package-lock.json index 23cafe6b..62728a18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "daikon": "^1.2.42", "jszip": "^3.10.1", "react": "^17.0.2", + "react-color": "^2.19.3", "react-dnd": "^14.0.2", "react-dnd-html5-backend": "^14.0.0", "react-dom": "^17.0.2", @@ -2296,6 +2297,14 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "node_modules/@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -17233,6 +17242,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -17470,6 +17484,11 @@ "node": ">=0.10.0" } }, + "node_modules/material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -20518,6 +20537,23 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, + "node_modules/react-color": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", + "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", + "dependencies": { + "@icons/material": "^0.2.4", + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", + "material-colors": "^1.2.1", + "prop-types": "^15.5.10", + "reactcss": "^1.2.0", + "tinycolor2": "^1.4.1" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -21229,6 +21265,14 @@ "react-dom": ">=16.6.0" } }, + "node_modules/reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "dependencies": { + "lodash": "^4.0.1" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -24132,6 +24176,11 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -28303,6 +28352,12 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "requires": {} + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -39630,6 +39685,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -39822,6 +39882,11 @@ "object-visit": "^1.0.0" } }, + "material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -42234,6 +42299,20 @@ } } }, + "react-color": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", + "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", + "requires": { + "@icons/material": "^0.2.4", + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", + "material-colors": "^1.2.1", + "prop-types": "^15.5.10", + "reactcss": "^1.2.0", + "tinycolor2": "^1.4.1" + } + }, "react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -42690,6 +42769,14 @@ "prop-types": "^15.6.2" } }, + "reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "requires": { + "lodash": "^4.0.1" + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -44971,6 +45058,11 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==" }, + "tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index 446e0330..8a186688 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "daikon": "^1.2.42", "jszip": "^3.10.1", "react": "^17.0.2", + "react-color": "^2.19.3", "react-dnd": "^14.0.2", "react-dnd-html5-backend": "^14.0.0", "react-dom": "^17.0.2", diff --git a/public/sprite.svg b/public/sprite.svg index 6e102f7c..693f7626 100644 --- a/public/sprite.svg +++ b/public/sprite.svg @@ -167,9 +167,17 @@ - + + + + + + + + + diff --git a/src/engine/Graphics2d.js b/src/engine/Graphics2d.js index 1104c73a..8ced8c63 100644 --- a/src/engine/Graphics2d.js +++ b/src/engine/Graphics2d.js @@ -8,6 +8,7 @@ import { connect } from 'react-redux'; import Modes2d from '../store/Modes2d'; import StoreActionType from '../store/ActionTypes'; import ToolPick from './tools2d/ToolPick'; +import ToolPaint from './tools2d/ToolPaint'; import ToolDistance from './tools2d/ToolDistance'; import ToolAngle from './tools2d/ToolAngle'; import ToolArea from './tools2d/ToolArea'; @@ -71,6 +72,7 @@ class Graphics2d extends React.Component { // tools2d this.m_toolPick = new ToolPick(this); + this.m_toolPaint = new ToolPaint(this); this.m_toolDistance = new ToolDistance(this); this.m_toolAngle = new ToolAngle(this); this.m_toolArea = new ToolArea(this); @@ -205,6 +207,7 @@ class Graphics2d extends React.Component { // console.log(`gra2d. render: wScreen*hScreen = ${wScreen} * ${hScreen}, but w*h=${w}*${h} `); this.m_toolPick.setScreenDim(wScreen, hScreen); + this.m_toolPaint.setScreenDim(wScreen, hScreen); this.m_toolDistance.setScreenDim(wScreen, hScreen); this.m_toolAngle.setScreenDim(wScreen, hScreen); this.m_toolArea.setScreenDim(wScreen, hScreen); @@ -296,6 +299,7 @@ class Graphics2d extends React.Component { // console.log(`gra2d. render: wScreen*hScreen = ${wScreen} * ${hScreen}, but w*h=${w}*${h} `); this.m_toolPick.setScreenDim(wScreen, hScreen); + this.m_toolPaint.setScreenDim(wScreen, hScreen); this.m_toolDistance.setScreenDim(wScreen, hScreen); this.m_toolAngle.setScreenDim(wScreen, hScreen); this.m_toolArea.setScreenDim(wScreen, hScreen); @@ -390,6 +394,7 @@ class Graphics2d extends React.Component { // console.log(`gra2d. render: wScreen*hScreen = ${wScreen} * ${hScreen}, but w*h=${w}*${h} `); this.m_toolPick.setScreenDim(wScreen, hScreen); + this.m_toolPaint.setScreenDim(wScreen, hScreen); this.m_toolDistance.setScreenDim(wScreen, hScreen); this.m_toolAngle.setScreenDim(wScreen, hScreen); this.m_toolArea.setScreenDim(wScreen, hScreen); @@ -539,6 +544,7 @@ class Graphics2d extends React.Component { }) .then(() => { this.m_toolPick.render(ctx); + this.m_toolPaint.render(ctx, store); this.m_toolDistance.render(ctx, store); this.m_toolAngle.render(ctx, store); this.m_toolArea.render(ctx, store); @@ -566,10 +572,10 @@ class Graphics2d extends React.Component { xPosNew = mouseX - (mouseX - store.render2dxPos) * (newZoom / zoom); yPosNew = mouseY - (mouseY - store.render2dyPos) * (newZoom / zoom); } else { - const initialX = canvasRect.width * zoom + store.render2dxPos; - const initialY = canvasRect.height * zoom + store.render2dyPos; - xPosNew = initialX - (initialX - store.render2dxPos) * (newZoom / zoom); - yPosNew = initialY - (initialY - store.render2dyPos) * (newZoom / zoom); + const centerX = (canvasRect.width * newZoom) / 2 + store.render2dxPos; + const centerY = (canvasRect.height * newZoom) / 2 + store.render2dyPos; + xPosNew = centerX - (centerX - store.render2dxPos) * (newZoom / zoom); + yPosNew = centerY - (centerY - store.render2dyPos) * (newZoom / zoom); } if (xPosNew < 0) { @@ -600,6 +606,13 @@ class Graphics2d extends React.Component { this.setState({ stateMouseDown: false }); + if (indexTools2d === Tools2dType.PAINT) { + const store = this.props; + const box = this.m_mount.current.getBoundingClientRect(); + const xScr = evt.clientX - box.left; + const yScr = evt.clientY - box.top; + this.m_toolPaint.onMouseUp(xScr, yScr, store); + } if (indexTools2d === Tools2dType.DISTANCE) { const store = this.props; const box = this.m_mount.current.getBoundingClientRect(); @@ -660,6 +673,9 @@ class Graphics2d extends React.Component { const xScr = xContainer; const yScr = yContainer; + if (indexTools2d === Tools2dType.PAINT) { + this.m_toolPaint.onMouseMove(xScr, yScr, store); + } if (indexTools2d === Tools2dType.DISTANCE) { this.m_toolDistance.onMouseMove(xScr, yScr, store); } @@ -718,6 +734,9 @@ class Graphics2d extends React.Component { case Tools2dType.INTENSITY: this.m_toolPick.onMouseDown(xScr, yScr, store); break; + case Tools2dType.PAINT: + this.m_toolPaint.onMouseDown(xScr, yScr, store); + break; case Tools2dType.DISTANCE: this.m_toolDistance.onMouseDown(xScr, yScr, store); break; @@ -754,6 +773,7 @@ class Graphics2d extends React.Component { * Invoke clear all tools */ clear() { + this.m_toolPaint.clear(); this.m_toolDistance.clear(); this.m_toolAngle.clear(); this.m_toolArea.clear(); diff --git a/src/engine/tools2d/ToolDelete.js b/src/engine/tools2d/ToolDelete.js index bf861718..fab4cea2 100644 --- a/src/engine/tools2d/ToolDelete.js +++ b/src/engine/tools2d/ToolDelete.js @@ -75,12 +75,13 @@ class ToolDelete { y: yScr, }; + const toolPaint = this.m_objGraphics2d.m_toolPaint; const toolDist = this.m_objGraphics2d.m_toolDistance; const toolAngle = this.m_objGraphics2d.m_toolAngle; const toolArea = this.m_objGraphics2d.m_toolArea; const toolRect = this.m_objGraphics2d.m_toolRect; const toolText = this.m_objGraphics2d.m_toolText; - const tools = [toolDist, toolAngle, toolArea, toolRect, toolText]; + const tools = [toolPaint, toolDist, toolAngle, toolArea, toolRect, toolText]; const trackedBefore = this.m_pointTracked !== null; this.m_pointTracked = null; const numTools = tools.length; diff --git a/src/engine/tools2d/ToolPaint.js b/src/engine/tools2d/ToolPaint.js new file mode 100644 index 00000000..d984e5c8 --- /dev/null +++ b/src/engine/tools2d/ToolPaint.js @@ -0,0 +1,112 @@ +import ToolDistance from './ToolDistance'; +import PointerChecker from '../utils/PointerChecker'; + +class ToolPaint { + constructor(objGra) { + this.m_objGraphics2d = objGra; + this.m_wScreen = 0; + this.m_hScreen = 0; + this.m_lines = []; + this.m_mouseDown = false; + this.m_objEdit = null; + } + + setScreenDim(wScr, hScr) { + this.m_wScreen = wScr; + this.m_hScreen = hScr; + } + + getEditPoint(vScr, store) { + const numLines = this.m_lines.length; + for (let i = 0; i < numLines; i++) { + const objLine = this.m_lines[i]; + + if (objLine.points.length >= 2) { + for (let j = 1; j < objLine.points.length; j++) { + const vScrS = ToolDistance.textureToScreen( + objLine.points[j - 1].x, + objLine.points[j - 1].y, + this.m_wScreen, + this.m_hScreen, + store + ); + const vScrE = ToolDistance.textureToScreen(objLine.points[j].x, objLine.points[j].y, this.m_wScreen, this.m_hScreen, store); + + if (PointerChecker.isPointerOnLine(vScrS, vScrE, vScr)) { + this.m_objEdit = objLine; + return objLine.points[j - 1]; + } + } + } + } + return null; + } + + deleteObject() { + if (this.m_objEdit != null) { + const ind = this.m_lines.indexOf(this.m_objEdit); + if (ind >= 0) { + this.m_lines.splice(ind, 1); + } + } + } + + onMouseDown(xScr, yScr, store) { + const vTex = ToolDistance.screenToTexture(xScr, yScr, this.m_wScreen, this.m_hScreen, store); + const newLine = { + points: [{ x: vTex.x, y: vTex.y }], + distMm: 0.0, + color: store.selectedColor, + }; + this.m_lines.push(newLine); + this.m_mouseDown = true; + } + + onMouseMove(xScr, yScr, store) { + if (!this.m_mouseDown) { + return; + } + const vTex = ToolDistance.screenToTexture(xScr, yScr, this.m_wScreen, this.m_hScreen, store); + const numLines = this.m_lines.length; + if (numLines > 0) { + const currentLine = this.m_lines[numLines - 1]; + currentLine.points.push({ x: vTex.x, y: vTex.y }); + this.m_objGraphics2d.forceUpdate(); + } + } + + onMouseUp() { + this.m_mouseDown = false; + } + + clear() { + this.m_lines = []; + } + + render(ctx, store) { + const numLines = this.m_lines.length; + ctx.lineWidth = 2; + + for (let i = 0; i < numLines; i++) { + const objLine = this.m_lines[i]; + const points = objLine.points; + + if (points.length > 1) { + ctx.strokeStyle = objLine.color; + for (let j = 1; j < points.length; j++) { + const vsTex = points[j - 1]; + const veTex = points[j]; + + const vs = ToolDistance.textureToScreen(vsTex.x, vsTex.y, this.m_wScreen, this.m_hScreen, store); + const ve = ToolDistance.textureToScreen(veTex.x, veTex.y, this.m_wScreen, this.m_hScreen, store); + + ctx.beginPath(); + ctx.moveTo(vs.x, vs.y); + ctx.lineTo(ve.x, ve.y); + ctx.stroke(); + } + } + } + } +} +export default ToolPaint; diff --git a/src/engine/tools2d/ToolTypes.js b/src/engine/tools2d/ToolTypes.js index 4087ad85..406c8c99 100644 --- a/src/engine/tools2d/ToolTypes.js +++ b/src/engine/tools2d/ToolTypes.js @@ -24,5 +24,6 @@ const Tools2dType = { ZOOM_100: 11, FILTER: 12, HAND: 13, + PAINT: 14, }; export default Tools2dType; diff --git a/src/store/ActionTypes.js b/src/store/ActionTypes.js index 350ecd8a..712b791c 100644 --- a/src/store/ActionTypes.js +++ b/src/store/ActionTypes.js @@ -53,5 +53,6 @@ const StoreActionType = { SET_SHOW_MODAL_CONFIRMATION: 41, SET_SHOW_MODAL_WINDOW_WC: 42, SET_SHOW_MODAL_SELECT_FILES: 43, + SET_SELECTED_COLOR: 44, }; export default StoreActionType; diff --git a/src/store/Store.js b/src/store/Store.js index bdd5fcf0..dd2d8dbb 100644 --- a/src/store/Store.js +++ b/src/store/Store.js @@ -52,6 +52,7 @@ export const initialState = { showModalConfirmation: false, showModalWindowCW: false, showModalSelectFiles: false, + selectedColor: '#ffff00', }; // // App reducer @@ -144,6 +145,8 @@ const medReducer = (state = initialState, action) => { return Object.assign({}, state, { spinnerTitle: action.spinnerTitle }); case StoreActionType.SET_SPINNER_PROGRESS: return Object.assign({}, state, { spinnerProgress: action.spinnerProgress }); + case StoreActionType.SET_SELECTED_COLOR: + return Object.assign({}, state, { selectedColor: action.selectedColor }); default: return state; } diff --git a/src/ui/Header/Header.module.css b/src/ui/Header/Header.module.css index 420d1b8a..df2c79c9 100644 --- a/src/ui/Header/Header.module.css +++ b/src/ui/Header/Header.module.css @@ -1,6 +1,7 @@ .header { display: flex; gap: 5px; + z-index: 12; } .header_button { diff --git a/src/ui/Main.module.css b/src/ui/Main.module.css index c34629c0..9a4c1194 100644 --- a/src/ui/Main.module.css +++ b/src/ui/Main.module.css @@ -198,7 +198,7 @@ cursor: all-scroll; border-radius: 24px; transition: opacity 300ms ease-in-out; - z-index: 1100; + z-index: 11; } .left { top: 8%; diff --git a/src/ui/Panels/ColorPicker/ColorPicker.css b/src/ui/Panels/ColorPicker/ColorPicker.css new file mode 100644 index 00000000..929ab9f6 --- /dev/null +++ b/src/ui/Panels/ColorPicker/ColorPicker.css @@ -0,0 +1,9 @@ +.colorPicker { + position: absolute; + top: 100%; + right: 0; + cursor: pointer; +} +.sketchPicker { + background-color: rgba(41, 43, 46, 0.95) !important; +} diff --git a/src/ui/Panels/ColorPicker/ColorPicker.jsx b/src/ui/Panels/ColorPicker/ColorPicker.jsx new file mode 100644 index 00000000..f4c5c5a5 --- /dev/null +++ b/src/ui/Panels/ColorPicker/ColorPicker.jsx @@ -0,0 +1,18 @@ +import { SketchPicker } from 'react-color'; + +const ColorPicker = ({ selectedColor, onChange, sketchPickerClass }) => { + return ( +
+ +
+ ); +}; + +export default ColorPicker; diff --git a/src/ui/Panels/Mode2dSettingsPanel.jsx b/src/ui/Panels/Mode2dSettingsPanel.jsx index 492ba893..d1b08b6e 100644 --- a/src/ui/Panels/Mode2dSettingsPanel.jsx +++ b/src/ui/Panels/Mode2dSettingsPanel.jsx @@ -8,11 +8,22 @@ import { SegmentationProperty } from './Properties2d/SegmentationProperty'; import SelectVolumeProperty from './Properties2d/SelectVolumeProperty'; import { SliderCaption } from '../Form'; import { TransverseProperty } from './Properties2d/TransverseProperty'; -import { useSelector } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; +import ColorPicker from '../Panels/ColorPicker/ColorPicker'; +import './ColorPicker/ColorPicker.css'; +import StoreActionType from '../../store/ActionTypes'; +import Tools2dType from '../../engine/tools2d/ToolTypes'; export const Mode2dSettingsPanel = () => { const { volumeSet } = useSelector((state) => state); const { m_volumes } = volumeSet; + const { indexTools2d } = useSelector((state) => state); + const { selectedColor } = useSelector((state) => state); + const dispatch = useDispatch(); + + const handleColorChange = (newColor) => { + dispatch({ type: StoreActionType.SET_SELECTED_COLOR, selectedColor: newColor.hex }); + }; return ( <> @@ -20,6 +31,12 @@ export const Mode2dSettingsPanel = () => { {m_volumes.length > 1 && } + {indexTools2d === Tools2dType.PAINT && ( +
+

Select color:

+ +
+ )} ); }; diff --git a/src/ui/TopToolbar/ExploreTools.jsx b/src/ui/TopToolbar/ExploreTools.jsx index 48cca240..1ddf7110 100644 --- a/src/ui/TopToolbar/ExploreTools.jsx +++ b/src/ui/TopToolbar/ExploreTools.jsx @@ -47,6 +47,12 @@ const ExploreTools = (props) => { handler: mediator.bind(null, Tools2dType.INTENSITY), id: Tools2dType.INTENSITY, }, + { + icon: 'paint', + caption: 'Paint', + handler: mediator.bind(null, Tools2dType.PAINT), + id: Tools2dType.PAINT, + }, { icon: 'line', caption: 'Measure distance between voxels', diff --git a/src/ui/UiZoomTools.jsx b/src/ui/UiZoomTools.jsx index ca187212..e84c5863 100644 --- a/src/ui/UiZoomTools.jsx +++ b/src/ui/UiZoomTools.jsx @@ -27,10 +27,10 @@ const UiZoomTools = (props) => { xPosNew = props.render2dxPos + (canvasRect.width / 2) * Math.abs(step); yPosNew = props.render2dyPos + (canvasRect.height / 2) * Math.abs(step); } else if (buttonId === Tools2dType.ZOOM_OUT && newZoom < 1) { - const initialX = canvasRect.width * currentZoom + props.render2dxPos; - const initialY = canvasRect.height * currentZoom + props.render2dyPos; - xPosNew = initialX - (initialX - props.render2dxPos) * (newZoom / currentZoom); - yPosNew = initialY - (initialY - props.render2dyPos) * (newZoom / currentZoom); + const centerX = (canvasRect.width * newZoom) / 2 + props.render2dxPos; + const centerY = (canvasRect.height * newZoom) / 2 + props.render2dyPos; + xPosNew = centerX - (centerX - props.render2dxPos) * (newZoom / currentZoom); + yPosNew = centerY - (centerY - props.render2dyPos) * (newZoom / currentZoom); } if (xPosNew < 0) { From e2e6feee50903a8d9cbdfbe613c4ce6d5aa07493 Mon Sep 17 00:00:00 2001 From: Vika Abysova Date: Wed, 13 Dec 2023 22:10:03 +0200 Subject: [PATCH 09/12] update build --- package-lock.json | 122 ++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 70 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a7fc38a..b4892a58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,8 @@ "daikon": "^1.2.42", "jszip": "^3.10.1", "react": "^17.0.2", - "react-dnd": "^16.0.1", - "react-dnd-html5-backend": "^16.0.1", + "react-dnd": "^14.0.2", + "react-dnd-html5-backend": "^14.0.0", "react-dom": "^17.0.2", "react-modal": "^3.14.2", "react-popper": "^2.2.5", @@ -4099,19 +4099,19 @@ } }, "node_modules/@react-dnd/asap": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", - "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", + "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==" }, "node_modules/@react-dnd/invariant": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", - "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", + "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==" }, "node_modules/@react-dnd/shallowequal": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", - "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", + "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" }, "node_modules/@rollup/plugin-node-resolve": { "version": "7.1.3", @@ -9199,21 +9199,13 @@ } }, "node_modules/dnd-core": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", - "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-14.0.0.tgz", + "integrity": "sha512-wTDYKyjSqWuYw3ZG0GJ7k+UIfzxTNoZLjDrut37PbcPGNfwhlKYlPUqjAKUjOOv80izshUiqusaKgJPItXSevA==", "dependencies": { - "@react-dnd/asap": "^5.0.1", - "@react-dnd/invariant": "^4.0.1", - "redux": "^4.2.0" - } - }, - "node_modules/dnd-core/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" + "@react-dnd/asap": "^4.0.0", + "@react-dnd/invariant": "^2.0.0", + "redux": "^4.0.5" } }, "node_modules/dns-equal": { @@ -20638,13 +20630,13 @@ } }, "node_modules/react-dnd": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", - "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-14.0.2.tgz", + "integrity": "sha512-JoEL78sBCg8SzjOKMlkR70GWaPORudhWuTNqJ56lb2P8Vq0eM2+er3ZrMGiSDhOmzaRPuA9SNBz46nHCrjn11A==", "dependencies": { - "@react-dnd/invariant": "^4.0.1", - "@react-dnd/shallowequal": "^4.0.1", - "dnd-core": "^16.0.1", + "@react-dnd/invariant": "^2.0.0", + "@react-dnd/shallowequal": "^2.0.0", + "dnd-core": "14.0.0", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" }, @@ -20667,11 +20659,11 @@ } }, "node_modules/react-dnd-html5-backend": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", - "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-14.0.0.tgz", + "integrity": "sha512-2wAQqRFC1hbRGmk6+dKhOXsyQQOn3cN8PSZyOUeOun9J8t3tjZ7PS2+aFu7CVu2ujMDwTJR3VTwZh8pj2kCv7g==", "dependencies": { - "dnd-core": "^16.0.1" + "dnd-core": "14.0.0" } }, "node_modules/react-dom": { @@ -29621,19 +29613,19 @@ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, "@react-dnd/asap": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", - "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", + "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==" }, "@react-dnd/invariant": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", - "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", + "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==" }, "@react-dnd/shallowequal": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", - "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", + "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" }, "@rollup/plugin-node-resolve": { "version": "7.1.3", @@ -33550,23 +33542,13 @@ } }, "dnd-core": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", - "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-14.0.0.tgz", + "integrity": "sha512-wTDYKyjSqWuYw3ZG0GJ7k+UIfzxTNoZLjDrut37PbcPGNfwhlKYlPUqjAKUjOOv80izshUiqusaKgJPItXSevA==", "requires": { - "@react-dnd/asap": "^5.0.1", - "@react-dnd/invariant": "^4.0.1", - "redux": "^4.2.0" - }, - "dependencies": { - "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" - } - } + "@react-dnd/asap": "^4.0.0", + "@react-dnd/invariant": "^2.0.0", + "redux": "^4.0.5" } }, "dns-equal": { @@ -42328,23 +42310,23 @@ } }, "react-dnd": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", - "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-14.0.2.tgz", + "integrity": "sha512-JoEL78sBCg8SzjOKMlkR70GWaPORudhWuTNqJ56lb2P8Vq0eM2+er3ZrMGiSDhOmzaRPuA9SNBz46nHCrjn11A==", "requires": { - "@react-dnd/invariant": "^4.0.1", - "@react-dnd/shallowequal": "^4.0.1", - "dnd-core": "^16.0.1", + "@react-dnd/invariant": "^2.0.0", + "@react-dnd/shallowequal": "^2.0.0", + "dnd-core": "14.0.0", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" } }, "react-dnd-html5-backend": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", - "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-14.0.0.tgz", + "integrity": "sha512-2wAQqRFC1hbRGmk6+dKhOXsyQQOn3cN8PSZyOUeOun9J8t3tjZ7PS2+aFu7CVu2ujMDwTJR3VTwZh8pj2kCv7g==", "requires": { - "dnd-core": "^16.0.1" + "dnd-core": "14.0.0" } }, "react-dom": { From 594daa8c42c30abc78928e31697096552a30b9a3 Mon Sep 17 00:00:00 2001 From: Danil Rostov Date: Fri, 15 Dec 2023 19:59:13 -0800 Subject: [PATCH 10/12] Add progress for TS model loading --- src/engine/Graphics2d.js | 14 +++++++++++--- src/engine/Segm2d.js | 29 +++++++++++++++++++---------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/engine/Graphics2d.js b/src/engine/Graphics2d.js index 1104c73a..d2eb1a24 100644 --- a/src/engine/Graphics2d.js +++ b/src/engine/Graphics2d.js @@ -27,6 +27,7 @@ class Graphics2d extends React.Component { constructor(props) { super(props); + this.store = props; this.m_mount = React.createRef(); this.onMouseDown = this.onMouseDown.bind(this); @@ -527,7 +528,7 @@ class Graphics2d extends React.Component { if (isSegm) { const w = this.m_toolPick.m_wScreen; const h = this.m_toolPick.m_hScreen; - this.segm2d.render(ctx, w, h, this.imgData); + this.segm2d.renderImage(ctx, w, h, this.imgData); } else { createImageBitmap(this.imgData) .then((imageBitmap) => { @@ -692,6 +693,12 @@ class Graphics2d extends React.Component { startY: evt.clientY, }); } + + if (this.m_isSegmented && this.segm2d.model) { + // We do not need update segmented image (with model) + // on mouse move event to performance issues. + return; + } store.graphics2d.forceUpdate(); } @@ -767,13 +774,14 @@ class Graphics2d extends React.Component { * Invoke forced rendering, after some tool visual changes */ forceUpdate(volIndex) { - // console.log('forceUpdate ...'); + console.log('forceUpdate ...'); this.prepareImageForRender(volIndex); // this.forceRender(); if (this.m_isSegmented) { // need to draw segmented image if (this.segm2d.model !== null) { - // we have loaded model: applt it to image + // we have loaded model: apply it to image + // TODO update image only on some specific events: zoom, explore this.segm2d.startApplyImage(); } } else { diff --git a/src/engine/Segm2d.js b/src/engine/Segm2d.js index 0f73ecca..76e35c61 100644 --- a/src/engine/Segm2d.js +++ b/src/engine/Segm2d.js @@ -14,7 +14,9 @@ // ******************************************************** import * as tf from '@tensorflow/tfjs'; -const PATH_MODEL = 'https://lugachai.ru/med3web/tfjs/model.json'; +import StoreActionType from '../store/ActionTypes'; + +const BRAIN_MODEl = 'https://daentjnvnffrh.cloudfront.net/models/brain/model.json'; // ******************************************************** // Const @@ -45,9 +47,11 @@ const NUM_CLASSES = 96; // ******************************************************** class Segm2d { - constructor(objGraphics2d) { + constructor(props) { this.stage = STAGE_MODEL_NOT_LOADED; - this.objGraphics2d = objGraphics2d; + this.objGraphics2d = props; + this.store = props.store; + this.model = null; this.tensorIndices = null; this.imgData = null; @@ -116,14 +120,14 @@ class Segm2d { } // for (y) } - // // Load model async onLoadModel() { this.stage = STAGE_MODEL_IS_LOADING; this.pixels = null; console.log('Loading tfjs model...'); - const modelLoaded = await tf.loadLayersModel(PATH_MODEL, { strict: false }); + this.store.dispatch({ type: StoreActionType.SET_PROGRESS_INFO, titleProgressBar: 'Loading tfjs model...' }); + const modelLoaded = await tf.loadLayersModel(BRAIN_MODEl, { strict: false, onProgress: this.onTFLoadProgress.bind(this) }); this.model = modelLoaded; this.stage = STAGE_MODEL_READY; @@ -135,9 +139,6 @@ class Segm2d { } async startApplyImage() { - if (this.stage === STAGE_SEGMENTATION_READY) { - return; - } this.stage = STAGE_IMAGE_PROCESSED; console.log('Start apply segm to image ...'); @@ -280,7 +281,7 @@ class Segm2d { this.srcImageData = imgData; } - render(ctx, w, h, imgData) { + renderImage(ctx, w, h, imgData) { this.srcImageData = imgData; this.wSrc = w; this.hSrc = h; @@ -291,7 +292,7 @@ class Segm2d { console.log('Segm2d render. stage = ' + strMessage); // load model - if (this.model === null) { + if (this.model === null && this.stage === STAGE_MODEL_NOT_LOADED) { this.onLoadModel(); } else { // change slider or similar: need to rebuild segm for the new source image @@ -327,6 +328,14 @@ class Segm2d { const y = h / 2; ctx.fillText(strMsgPrint, x, y); } + + onTFLoadProgress(progress) { + this.store.dispatch({ type: StoreActionType.SET_PROGRESS, progress }); + if (progress === 1) { + this.store.dispatch({ type: StoreActionType.SET_PROGRESS, progress: 0 }); + this.store.dispatch({ type: StoreActionType.SET_PROGRESS_INFO, titleProgressBar: null }); + } + } } function drawRoundedRect(ctx, x, y, width, height, borderRadius) { From 044a884e1bd57d062000e9eff307b58fe881049c Mon Sep 17 00:00:00 2001 From: Dmytro Ilnytskyy Date: Mon, 18 Dec 2023 14:24:51 +0200 Subject: [PATCH 11/12] EPMUII-8820: have fixed navigation bar - sizes of buttons --- src/ui/Button/Button.module.css | 15 ++++++++++----- src/ui/Header/Header.module.css | 19 +++++++++++++++++-- src/ui/Main.module.css | 7 ++++--- src/ui/OpenFile/UiReportMenu.js | 4 +--- src/ui/OpenFile/UiSaveMenu.js | 3 +-- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/ui/Button/Button.module.css b/src/ui/Button/Button.module.css index 1ff3473e..29ccb7ab 100644 --- a/src/ui/Button/Button.module.css +++ b/src/ui/Button/Button.module.css @@ -48,11 +48,21 @@ button:hover, background-color: var(--red); } +.button > svg { + width: 42px; + height: 42px; +} + @media screen and (min-width: 768px) { .reset { display: flex; justify-content: flex-end; } + + .button > svg { + width: 24px; + height: 24px; + } } .reset { display: inline-block; @@ -82,11 +92,6 @@ button:hover, border-radius: 36px; } -.button > svg { - width: 24px; - height: 24px; -} - .rounded { overflow: hidden; border-radius: 50%; diff --git a/src/ui/Header/Header.module.css b/src/ui/Header/Header.module.css index 420d1b8a..86b7b59b 100644 --- a/src/ui/Header/Header.module.css +++ b/src/ui/Header/Header.module.css @@ -1,9 +1,24 @@ .header { display: flex; - gap: 5px; + gap: 3vw; + width: 95%; + min-width: 432px; + justify-content: space-between; } .header_button { font-size: 0; - background-color: white; + background-color: inherit; +} + +@media screen and (min-width: 768px) { + .header { + gap: 5px; + justify-content: flex-end; + } + + .header_button { + font-size: 0; + background-color: white; + } } diff --git a/src/ui/Main.module.css b/src/ui/Main.module.css index c34629c0..fd01e941 100644 --- a/src/ui/Main.module.css +++ b/src/ui/Main.module.css @@ -27,9 +27,8 @@ display: flex; justify-content: center; align-items: center; - flex-direction: row-reverse; - width: 100vw; - right: 0; + flex-direction: row; + width: 96vw; height: 50px; padding: 1rem; background-color: var(--beige); @@ -116,12 +115,14 @@ .header__right { display: flex; + flex-direction: row-reverse; justify-content: flex-start; flex-wrap: nowrap; align-items: center; margin-left: auto; position: static; background: inherit; + right: 0; } .left { diff --git a/src/ui/OpenFile/UiReportMenu.js b/src/ui/OpenFile/UiReportMenu.js index 5ccbbd87..4cd716fc 100644 --- a/src/ui/OpenFile/UiReportMenu.js +++ b/src/ui/OpenFile/UiReportMenu.js @@ -46,9 +46,8 @@ export const UiReportMenu = () => { const strDisabled = !isLoadedLocal; return ( <> - + { { }; return ( <> - + Date: Tue, 19 Dec 2023 22:32:45 -0800 Subject: [PATCH 12/12] Fix onerror xhr reject event --- src/engine/lib/core/loaders/MRIFileLoader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/lib/core/loaders/MRIFileLoader.ts b/src/engine/lib/core/loaders/MRIFileLoader.ts index 7ac99a77..72c4b517 100644 --- a/src/engine/lib/core/loaders/MRIFileLoader.ts +++ b/src/engine/lib/core/loaders/MRIFileLoader.ts @@ -174,7 +174,7 @@ export class MRIFileLoader { xhr.onerror = () => { this.handleVolumeLoadFailed(`Failed to fetch file from URL: ${url}`); - reject(new Error()); + reject(); }; xhr.responseType = 'blob';