// lib import { MutableRefObject, useEffect, useRef, useState } from 'react'; import Konva from 'konva'; import { Layer, Stage } from 'react-konva'; import { Image as KonvaImage } from 'react-konva'; import { Stage as StageType } from 'konva/lib/Stage'; // app import { useAppDispatch, useAppSelector } from 'app/store'; import { baseCanvasImageSelector, clearImageToInpaint, currentCanvasSelector, outpaintingCanvasSelector, } from 'features/canvas/canvasSlice'; // component import IAICanvasMaskLines from './IAICanvasMaskLines'; import IAICanvasBrushPreview from './IAICanvasBrushPreview'; import { Vector2d } from 'konva/lib/types'; import IAICanvasBoundingBoxPreview from './IAICanvasBoundingBoxPreview'; import { useToast } from '@chakra-ui/react'; import useCanvasHotkeys from './hooks/useCanvasHotkeys'; import _ from 'lodash'; import { createSelector } from '@reduxjs/toolkit'; import { activeTabNameSelector } from 'features/options/optionsSelectors'; import IAICanvasMaskCompositer from './IAICanvasMaskCompositer'; import useCanvasWheel from './hooks/useCanvasZoom'; import useCanvasMouseDown from './hooks/useCanvasMouseDown'; import useCanvasMouseUp from './hooks/useCanvasMouseUp'; import useCanvasMouseMove from './hooks/useCanvasMouseMove'; import useCanvasMouseEnter from './hooks/useCanvasMouseEnter'; import useCanvasMouseOut from './hooks/useCanvasMouseOut'; import useCanvasDragMove from './hooks/useCanvasDragMove'; import IAICanvasOutpaintingObjects from './IAICanvasOutpaintingObjects'; import IAICanvasGrid from './IAICanvasGrid'; import IAICanvasIntermediateImage from './IAICanvasIntermediateImage'; import IAICanvasStatusText from './IAICanvasStatusText'; const canvasSelector = createSelector( [ currentCanvasSelector, outpaintingCanvasSelector, baseCanvasImageSelector, activeTabNameSelector, ], (currentCanvas, outpaintingCanvas, baseCanvasImage, activeTabName) => { const { shouldInvertMask, isMaskEnabled, shouldShowCheckboardTransparency, stageScale, shouldShowBoundingBox, shouldLockBoundingBox, isTransformingBoundingBox, isMouseOverBoundingBox, isMovingBoundingBox, stageDimensions, stageCoordinates, isMoveStageKeyHeld, tool, isMovingStage, } = currentCanvas; const { shouldShowGrid } = outpaintingCanvas; let stageCursor: string | undefined = ''; if (tool === 'move') { if (isTransformingBoundingBox) { stageCursor = undefined; } else if (isMouseOverBoundingBox) { stageCursor = 'move'; } else { if (isMovingStage) { stageCursor = 'grabbing'; } else { stageCursor = 'grab'; } } } else { stageCursor = 'none'; } return { shouldInvertMask, isMaskEnabled, shouldShowCheckboardTransparency, stageScale, shouldShowBoundingBox, shouldLockBoundingBox, shouldShowGrid, isTransformingBoundingBox, isModifyingBoundingBox: isTransformingBoundingBox || isMovingBoundingBox, stageCursor, isMouseOverBoundingBox, stageDimensions, stageCoordinates, isMoveStageKeyHeld, activeTabName, baseCanvasImage, tool, }; }, { memoizeOptions: { resultEqualityCheck: _.isEqual, }, } ); // Use a closure allow other components to use these things... not ideal... export let stageRef: MutableRefObject; export let canvasImageLayerRef: MutableRefObject; export let inpaintingImageElementRef: MutableRefObject; const IAICanvas = () => { const dispatch = useAppDispatch(); const { shouldInvertMask, isMaskEnabled, shouldShowCheckboardTransparency, stageScale, shouldShowBoundingBox, isModifyingBoundingBox, stageCursor, stageDimensions, stageCoordinates, shouldShowGrid, activeTabName, baseCanvasImage, tool, } = useAppSelector(canvasSelector); useCanvasHotkeys(); const toast = useToast(); // set the closure'd refs stageRef = useRef(null); canvasImageLayerRef = useRef(null); inpaintingImageElementRef = useRef(null); const lastCursorPositionRef = useRef({ x: 0, y: 0 }); // Use refs for values that do not affect rendering, other values in redux const didMouseMoveRef = useRef(false); // Load the image into this const [canvasBgImage, setCanvasBgImage] = useState( null ); const handleWheel = useCanvasWheel(stageRef); const handleMouseDown = useCanvasMouseDown(stageRef); const handleMouseUp = useCanvasMouseUp(stageRef, didMouseMoveRef); const handleMouseMove = useCanvasMouseMove( stageRef, didMouseMoveRef, lastCursorPositionRef ); const handleMouseEnter = useCanvasMouseEnter(stageRef); const handleMouseOut = useCanvasMouseOut(); const { handleDragStart, handleDragMove, handleDragEnd } = useCanvasDragMove(); // Load the image and set the options panel width & height useEffect(() => { if (baseCanvasImage) { const image = new Image(); image.onload = () => { inpaintingImageElementRef.current = image; setCanvasBgImage(image); }; image.onerror = () => { toast({ title: 'Unable to Load Image', description: `Image ${baseCanvasImage.url} failed to load`, status: 'error', isClosable: true, }); dispatch(clearImageToInpaint()); }; image.src = baseCanvasImage.url; } else { setCanvasBgImage(null); } }, [baseCanvasImage, dispatch, stageScale, toast]); return (
{canvasBgImage && ( <> )}
); }; export default IAICanvas;