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) {