mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Organises features/canvas
This commit is contained in:
parent
48ad0c289c
commit
827f516baf
@ -5,7 +5,7 @@ import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { OptionsState } from 'features/options/optionsSlice';
|
||||
import { SystemState } from 'features/system/systemSlice';
|
||||
import { validateSeedWeights } from 'common/util/seedWeightPairs';
|
||||
import { initialCanvasImageSelector } from 'features/canvas/canvasSlice';
|
||||
import { initialCanvasImageSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
export const readinessSelector = createSelector(
|
||||
[
|
||||
|
@ -13,14 +13,12 @@ import {
|
||||
import { OptionsState } from 'features/options/optionsSlice';
|
||||
import {
|
||||
addLogEntry,
|
||||
errorOccurred,
|
||||
modelChangeRequested,
|
||||
setIsProcessing,
|
||||
} from 'features/system/systemSlice';
|
||||
import { InvokeTabName } from 'features/tabs/InvokeTabs';
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import { RootState } from 'app/store';
|
||||
import { initialCanvasImageSelector } from 'features/canvas/canvasSlice';
|
||||
|
||||
/**
|
||||
* Returns an object containing all functions which use `socketio.emit()`.
|
||||
@ -71,8 +69,8 @@ const makeSocketIOEmitters = (
|
||||
// }
|
||||
|
||||
// frontendToBackendParametersConfig.imageToProcessUrl = imageUrl;
|
||||
// } else
|
||||
|
||||
// } else
|
||||
|
||||
if (!['txt2img', 'img2img'].includes(generationMode)) {
|
||||
if (!galleryState.currentImage?.url) return;
|
||||
|
||||
|
@ -37,7 +37,7 @@ import {
|
||||
requestNewImages,
|
||||
requestSystemConfig,
|
||||
} from './actions';
|
||||
import { addImageToStagingArea } from 'features/canvas/canvasSlice';
|
||||
import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
|
||||
import { tabMap } from 'features/tabs/InvokeTabs';
|
||||
|
||||
/**
|
||||
|
@ -10,7 +10,7 @@ import { getPersistConfig } from 'redux-deep-persist';
|
||||
import optionsReducer from 'features/options/optionsSlice';
|
||||
import galleryReducer from 'features/gallery/gallerySlice';
|
||||
import systemReducer from 'features/system/systemSlice';
|
||||
import canvasReducer from 'features/canvas/canvasSlice';
|
||||
import canvasReducer from 'features/canvas/store/canvasSlice';
|
||||
|
||||
import { socketioMiddleware } from './socketio/middleware';
|
||||
|
||||
|
@ -5,9 +5,9 @@ import { SystemState } from 'features/system/systemSlice';
|
||||
import { stringToSeedWeightsArray } from './seedWeightPairs';
|
||||
import randomInt from './randomInt';
|
||||
import { InvokeTabName } from 'features/tabs/InvokeTabs';
|
||||
import { CanvasState, isCanvasMaskLine } from 'features/canvas/canvasSlice';
|
||||
import { CanvasState, isCanvasMaskLine } from 'features/canvas/store/canvasTypes';
|
||||
import generateMask from 'features/canvas/util/generateMask';
|
||||
import { canvasImageLayerRef } from 'features/canvas/IAICanvas';
|
||||
import { canvasImageLayerRef } from 'features/canvas/components/IAICanvas';
|
||||
import openBase64ImageInTab from './openBase64ImageInTab';
|
||||
|
||||
export type FrontendToBackendParametersConfig = {
|
||||
|
@ -1,63 +0,0 @@
|
||||
import IAICanvasBrushControl from './IAICanvasControls/IAICanvasBrushControl';
|
||||
import IAICanvasEraserControl from './IAICanvasControls/IAICanvasEraserControl';
|
||||
import IAICanvasUndoControl from './IAICanvasControls/IAICanvasUndoButton';
|
||||
import IAICanvasRedoControl from './IAICanvasControls/IAICanvasRedoButton';
|
||||
import { ButtonGroup } from '@chakra-ui/react';
|
||||
import IAICanvasMaskClear from './IAICanvasControls/IAICanvasMaskControls/IAICanvasMaskClear';
|
||||
import IAICanvasMaskVisibilityControl from './IAICanvasControls/IAICanvasMaskControls/IAICanvasMaskVisibilityControl';
|
||||
import IAICanvasMaskInvertControl from './IAICanvasControls/IAICanvasMaskControls/IAICanvasMaskInvertControl';
|
||||
import IAICanvasLockBoundingBoxControl from './IAICanvasControls/IAICanvasLockBoundingBoxControl';
|
||||
import IAICanvasShowHideBoundingBoxControl from './IAICanvasControls/IAICanvasShowHideBoundingBoxControl';
|
||||
import ImageUploaderIconButton from 'common/components/ImageUploaderIconButton';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { OptionsState } from 'features/options/optionsSlice';
|
||||
import _ from 'lodash';
|
||||
import { canvasSelector } from './canvasSlice';
|
||||
|
||||
export const canvasControlsSelector = createSelector(
|
||||
[(state: RootState) => state.options, canvasSelector, activeTabNameSelector],
|
||||
(options: OptionsState, canvas, activeTabName) => {
|
||||
const { stageScale, boundingBoxCoordinates, boundingBoxDimensions } =
|
||||
canvas;
|
||||
return {
|
||||
activeTabName,
|
||||
stageScale,
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const IAICanvasControls = () => {
|
||||
return (
|
||||
<div className="inpainting-settings">
|
||||
<ButtonGroup isAttached={true}>
|
||||
<IAICanvasBrushControl />
|
||||
<IAICanvasEraserControl />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup isAttached={true}>
|
||||
<IAICanvasMaskVisibilityControl />
|
||||
<IAICanvasMaskInvertControl />
|
||||
<IAICanvasLockBoundingBoxControl />
|
||||
<IAICanvasShowHideBoundingBoxControl />
|
||||
<IAICanvasMaskClear />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup isAttached={true}>
|
||||
<IAICanvasUndoControl />
|
||||
<IAICanvasRedoControl />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup isAttached={true}>
|
||||
<ImageUploaderIconButton />
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default IAICanvasControls;
|
@ -1,134 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaPaintBrush } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
|
||||
import {
|
||||
canvasSelector,
|
||||
setBrushSize,
|
||||
setShouldShowBrushPreview,
|
||||
setTool,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
|
||||
import _ from 'lodash';
|
||||
import IAICanvasMaskColorPicker from './IAICanvasMaskControls/IAICanvasMaskColorPicker';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const { tool, brushSize } = canvas;
|
||||
|
||||
return {
|
||||
tool,
|
||||
brushSize,
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function IAICanvasBrushControl() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { tool, brushSize, activeTabName } = useAppSelector(selector);
|
||||
|
||||
const handleSelectBrushTool = () => dispatch(setTool('brush'));
|
||||
|
||||
const handleShowBrushPreview = () => {
|
||||
dispatch(setShouldShowBrushPreview(true));
|
||||
};
|
||||
|
||||
const handleHideBrushPreview = () => {
|
||||
dispatch(setShouldShowBrushPreview(false));
|
||||
};
|
||||
|
||||
const handleChangeBrushSize = (v: number) => {
|
||||
dispatch(setShouldShowBrushPreview(true));
|
||||
dispatch(setBrushSize(v));
|
||||
};
|
||||
|
||||
useHotkeys(
|
||||
'[',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
if (brushSize - 5 > 0) {
|
||||
handleChangeBrushSize(brushSize - 5);
|
||||
} else {
|
||||
handleChangeBrushSize(1);
|
||||
}
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName, brushSize]
|
||||
);
|
||||
|
||||
// Increase brush size
|
||||
useHotkeys(
|
||||
']',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleChangeBrushSize(brushSize + 5);
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName, brushSize]
|
||||
);
|
||||
|
||||
// Set tool to brush
|
||||
useHotkeys(
|
||||
'b',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleSelectBrushTool();
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
onOpen={handleShowBrushPreview}
|
||||
onClose={handleHideBrushPreview}
|
||||
triggerComponent={
|
||||
<IAIIconButton
|
||||
aria-label="Brush (B)"
|
||||
tooltip="Brush (B)"
|
||||
icon={<FaPaintBrush />}
|
||||
onClick={handleSelectBrushTool}
|
||||
data-selected={tool === 'brush'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className="inpainting-brush-options">
|
||||
<IAISlider
|
||||
label="Brush Size"
|
||||
value={brushSize}
|
||||
onChange={handleChangeBrushSize}
|
||||
min={1}
|
||||
max={200}
|
||||
/>
|
||||
<IAINumberInput
|
||||
value={brushSize}
|
||||
onChange={handleChangeBrushSize}
|
||||
width={'80px'}
|
||||
min={1}
|
||||
max={999}
|
||||
/>
|
||||
<IAICanvasMaskColorPicker />
|
||||
</div>
|
||||
</IAIPopover>
|
||||
);
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaEraser } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { canvasSelector, setTool } from 'features/canvas/canvasSlice';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
|
||||
const eraserSelector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const { tool, isMaskEnabled } = canvas;
|
||||
|
||||
return {
|
||||
tool,
|
||||
isMaskEnabled,
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function IAICanvasEraserControl() {
|
||||
const { tool, isMaskEnabled, activeTabName } = useAppSelector(eraserSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleSelectEraserTool = () => dispatch(setTool('eraser'));
|
||||
|
||||
// Hotkeys
|
||||
// Set tool to maskEraser
|
||||
useHotkeys(
|
||||
'e',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleSelectEraserTool();
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label={
|
||||
activeTabName === 'inpainting' ? 'Eraser (E)' : 'Erase Mask (E)'
|
||||
}
|
||||
tooltip={activeTabName === 'inpainting' ? 'Eraser (E)' : 'Erase Mask (E)'}
|
||||
icon={<FaEraser />}
|
||||
onClick={handleSelectEraserTool}
|
||||
data-selected={tool === 'eraser'}
|
||||
isDisabled={!isMaskEnabled}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { canvasSelector, setTool } from 'features/canvas/canvasSlice';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { BsEraser } from 'react-icons/bs';
|
||||
|
||||
const imageEraserSelector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const { tool, isMaskEnabled } = canvas;
|
||||
|
||||
return {
|
||||
tool,
|
||||
isMaskEnabled,
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function IAICanvasImageEraserControl() {
|
||||
const { tool, isMaskEnabled, activeTabName } =
|
||||
useAppSelector(imageEraserSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleSelectEraserTool = () => dispatch(setTool('eraser'));
|
||||
|
||||
// Hotkeys
|
||||
useHotkeys(
|
||||
'shift+e',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleSelectEraserTool();
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName, isMaskEnabled]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Erase Canvas (Shift+E)"
|
||||
tooltip="Erase Canvas (Shift+E)"
|
||||
icon={<BsEraser />}
|
||||
fontSize={18}
|
||||
onClick={handleSelectEraserTool}
|
||||
data-selected={tool === 'eraser'}
|
||||
isDisabled={!isMaskEnabled}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import { FaLock, FaUnlock } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import {
|
||||
canvasSelector,
|
||||
setShouldLockBoundingBox,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import _ from 'lodash';
|
||||
|
||||
const canvasLockBoundingBoxSelector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => {
|
||||
const { shouldLockBoundingBox } = canvas;
|
||||
|
||||
return {
|
||||
shouldLockBoundingBox,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const IAICanvasLockBoundingBoxControl = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { shouldLockBoundingBox } = useAppSelector(
|
||||
canvasLockBoundingBoxSelector
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Lock Inpainting Box"
|
||||
tooltip="Lock Inpainting Box"
|
||||
icon={shouldLockBoundingBox ? <FaLock /> : <FaUnlock />}
|
||||
data-selected={shouldLockBoundingBox}
|
||||
onClick={() => {
|
||||
dispatch(setShouldLockBoundingBox(!shouldLockBoundingBox));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default IAICanvasLockBoundingBoxControl;
|
@ -1,38 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { FaMask } from 'react-icons/fa';
|
||||
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
|
||||
import IAICanvasMaskInvertControl from './IAICanvasMaskControls/IAICanvasMaskInvertControl';
|
||||
import IAICanvasMaskVisibilityControl from './IAICanvasMaskControls/IAICanvasMaskVisibilityControl';
|
||||
import IAICanvasMaskColorPicker from './IAICanvasMaskControls/IAICanvasMaskColorPicker';
|
||||
|
||||
export default function IAICanvasMaskControl() {
|
||||
const [maskOptionsOpen, setMaskOptionsOpen] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
onOpen={() => setMaskOptionsOpen(true)}
|
||||
onClose={() => setMaskOptionsOpen(false)}
|
||||
triggerComponent={
|
||||
<IAIIconButton
|
||||
aria-label="Mask Options"
|
||||
tooltip="Mask Options"
|
||||
icon={<FaMask />}
|
||||
cursor={'pointer'}
|
||||
data-selected={maskOptionsOpen}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className="inpainting-button-dropdown">
|
||||
<IAICanvasMaskVisibilityControl />
|
||||
<IAICanvasMaskInvertControl />
|
||||
<IAICanvasMaskColorPicker />
|
||||
</div>
|
||||
</IAIPopover>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
import { RgbaColor } from 'react-colorful';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import { canvasSelector, setMaskColor } from 'features/canvas/canvasSlice';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const { brushColor } = canvas;
|
||||
|
||||
return {
|
||||
brushColor,
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function IAICanvasBrushColorPicker() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { brushColor, activeTabName } = useAppSelector(selector);
|
||||
|
||||
const handleChangeBrushColor = (newColor: RgbaColor) => {
|
||||
dispatch(setMaskColor(newColor));
|
||||
};
|
||||
|
||||
// Decrease brush opacity
|
||||
useHotkeys(
|
||||
'shift+[',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleChangeBrushColor({
|
||||
...brushColor,
|
||||
a: Math.max(brushColor.a - 0.05, 0),
|
||||
});
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName, brushColor.a]
|
||||
);
|
||||
|
||||
// Increase brush opacity
|
||||
useHotkeys(
|
||||
'shift+]',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleChangeBrushColor({
|
||||
...brushColor,
|
||||
a: Math.min(brushColor.a + 0.05, 1),
|
||||
});
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName, brushColor.a]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIColorPicker color={brushColor} onChange={handleChangeBrushColor} />
|
||||
);
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { FaPlus } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import {
|
||||
clearMask,
|
||||
canvasSelector,
|
||||
isCanvasMaskLine,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
|
||||
const canvasMaskClearSelector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const {
|
||||
isMaskEnabled,
|
||||
layerState: { objects },
|
||||
} = canvas;
|
||||
|
||||
return {
|
||||
isMaskEnabled,
|
||||
activeTabName,
|
||||
isMaskEmpty: objects.filter(isCanvasMaskLine).length === 0,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function IAICanvasMaskClear() {
|
||||
const { isMaskEnabled, activeTabName, isMaskEmpty } = useAppSelector(
|
||||
canvasMaskClearSelector
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const toast = useToast();
|
||||
|
||||
const handleClearMask = () => {
|
||||
dispatch(clearMask());
|
||||
};
|
||||
|
||||
// Clear mask
|
||||
useHotkeys(
|
||||
'shift+c',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleClearMask();
|
||||
toast({
|
||||
title: 'Mask Cleared',
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
{
|
||||
enabled: !isMaskEmpty,
|
||||
},
|
||||
[activeTabName, isMaskEmpty, isMaskEnabled]
|
||||
);
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Clear Mask (Shift+C)"
|
||||
tooltip="Clear Mask (Shift+C)"
|
||||
icon={<FaPlus size={20} style={{ transform: 'rotate(45deg)' }} />}
|
||||
onClick={handleClearMask}
|
||||
isDisabled={isMaskEmpty || !isMaskEnabled}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
import React from 'react';
|
||||
import { RgbaColor } from 'react-colorful';
|
||||
import { FaPalette } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import { canvasSelector, setMaskColor } from 'features/canvas/canvasSlice';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const maskColorPickerSelector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const { isMaskEnabled, maskColor } = canvas;
|
||||
|
||||
return {
|
||||
isMaskEnabled,
|
||||
maskColor,
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function IAICanvasMaskColorPicker() {
|
||||
const { isMaskEnabled, maskColor, activeTabName } = useAppSelector(
|
||||
maskColorPickerSelector
|
||||
);
|
||||
const dispatch = useAppDispatch();
|
||||
const handleChangeMaskColor = (newColor: RgbaColor) => {
|
||||
dispatch(setMaskColor(newColor));
|
||||
};
|
||||
|
||||
// Hotkeys
|
||||
// Decrease mask opacity
|
||||
useHotkeys(
|
||||
'shift+[',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleChangeMaskColor({
|
||||
...maskColor,
|
||||
a: Math.max(maskColor.a - 0.05, 0),
|
||||
});
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName, isMaskEnabled, maskColor.a]
|
||||
);
|
||||
|
||||
// Increase mask opacity
|
||||
useHotkeys(
|
||||
'shift+]',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleChangeMaskColor({
|
||||
...maskColor,
|
||||
a: Math.min(maskColor.a + 0.05, 1),
|
||||
});
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName, isMaskEnabled, maskColor.a]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIPopover
|
||||
trigger="hover"
|
||||
styleClass="inpainting-color-picker"
|
||||
triggerComponent={
|
||||
<IAIIconButton
|
||||
aria-label="Mask Color"
|
||||
icon={<FaPalette />}
|
||||
isDisabled={!isMaskEnabled}
|
||||
cursor={'pointer'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<IAIColorPicker color={maskColor} onChange={handleChangeMaskColor} />
|
||||
</IAIPopover>
|
||||
);
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { MdInvertColors, MdInvertColorsOff } from 'react-icons/md';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import {
|
||||
canvasSelector,
|
||||
setShouldPreserveMaskedArea,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const canvasMaskInvertSelector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const { isMaskEnabled, shouldPreserveMaskedArea } = canvas;
|
||||
|
||||
return {
|
||||
shouldPreserveMaskedArea,
|
||||
isMaskEnabled,
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function IAICanvasMaskInvertControl() {
|
||||
const { shouldPreserveMaskedArea, isMaskEnabled, activeTabName } =
|
||||
useAppSelector(canvasMaskInvertSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleToggleShouldInvertMask = () =>
|
||||
dispatch(setShouldPreserveMaskedArea(!shouldPreserveMaskedArea));
|
||||
|
||||
// Invert mask
|
||||
useHotkeys(
|
||||
'shift+m',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleToggleShouldInvertMask();
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
[activeTabName, shouldPreserveMaskedArea, isMaskEnabled]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
tooltip="Invert Mask Display (Shift+M)"
|
||||
aria-label="Invert Mask Display (Shift+M)"
|
||||
data-selected={shouldPreserveMaskedArea}
|
||||
icon={
|
||||
shouldPreserveMaskedArea ? (
|
||||
<MdInvertColors size={22} />
|
||||
) : (
|
||||
<MdInvertColorsOff size={22} />
|
||||
)
|
||||
}
|
||||
onClick={handleToggleShouldInvertMask}
|
||||
isDisabled={!isMaskEnabled}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { BiHide, BiShow } from 'react-icons/bi';
|
||||
import { createSelector } from 'reselect';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { canvasSelector, setIsMaskEnabled } from 'features/canvas/canvasSlice';
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
const canvasMaskVisibilitySelector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
(canvas, activeTabName) => {
|
||||
const { isMaskEnabled } = canvas;
|
||||
|
||||
return { isMaskEnabled, activeTabName };
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default function IAICanvasMaskVisibilityControl() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { isMaskEnabled, activeTabName } = useAppSelector(
|
||||
canvasMaskVisibilitySelector
|
||||
);
|
||||
|
||||
const handleToggleShouldShowMask = () =>
|
||||
dispatch(setIsMaskEnabled(!isMaskEnabled));
|
||||
// Hotkeys
|
||||
// Show/hide mask
|
||||
useHotkeys(
|
||||
'h',
|
||||
(e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
handleToggleShouldShowMask();
|
||||
},
|
||||
{
|
||||
enabled: activeTabName === 'unifiedCanvas',
|
||||
},
|
||||
[activeTabName, isMaskEnabled]
|
||||
);
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Hide Mask (H)"
|
||||
tooltip="Hide Mask (H)"
|
||||
data-alert={!isMaskEnabled}
|
||||
icon={isMaskEnabled ? <BiShow size={22} /> : <BiHide size={22} />}
|
||||
onClick={handleToggleShouldShowMask}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import { FaVectorSquare } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import {
|
||||
canvasSelector,
|
||||
setShouldShowBoundingBox,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import _ from 'lodash';
|
||||
|
||||
const canvasShowHideBoundingBoxControlSelector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => {
|
||||
const { shouldShowBoundingBox } = canvas;
|
||||
|
||||
return {
|
||||
shouldShowBoundingBox,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: _.isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
const IAICanvasShowHideBoundingBoxControl = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { shouldShowBoundingBox } = useAppSelector(
|
||||
canvasShowHideBoundingBoxControlSelector
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Hide Inpainting Box (Shift+H)"
|
||||
tooltip="Hide Inpainting Box (Shift+H)"
|
||||
icon={<FaVectorSquare />}
|
||||
data-alert={!shouldShowBoundingBox}
|
||||
onClick={() => {
|
||||
dispatch(setShouldShowBoundingBox(!shouldShowBoundingBox));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default IAICanvasShowHideBoundingBoxControl;
|
@ -1,38 +0,0 @@
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { VscSplitHorizontal } from 'react-icons/vsc';
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { setShowDualDisplay } from 'features/options/optionsSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
|
||||
export default function IAICanvasSplitLayoutControl() {
|
||||
const dispatch = useAppDispatch();
|
||||
const showDualDisplay = useAppSelector(
|
||||
(state: RootState) => state.options.showDualDisplay
|
||||
);
|
||||
|
||||
const handleDualDisplay = () => {
|
||||
dispatch(setShowDualDisplay(!showDualDisplay));
|
||||
dispatch(setDoesCanvasNeedScaling(true));
|
||||
};
|
||||
|
||||
// Hotkeys
|
||||
// Toggle split view
|
||||
useHotkeys(
|
||||
'shift+j',
|
||||
() => {
|
||||
handleDualDisplay();
|
||||
},
|
||||
[showDualDisplay]
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
aria-label="Split Layout (Shift+J)"
|
||||
tooltip="Split Layout (Shift+J)"
|
||||
icon={<VscSplitHorizontal />}
|
||||
data-selected={showDualDisplay}
|
||||
onClick={handleDualDisplay}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,35 +1,30 @@
|
||||
// lib
|
||||
import { MutableRefObject, useCallback, useRef } from 'react';
|
||||
import Konva from 'konva';
|
||||
import { Layer, Stage } from 'react-konva';
|
||||
import { Stage as StageType } from 'konva/lib/Stage';
|
||||
|
||||
// app
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import {
|
||||
initialCanvasImageSelector,
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
shouldLockToInitialImageSelector,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
|
||||
// component
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import IAICanvasMaskLines from './IAICanvasMaskLines';
|
||||
import IAICanvasBrushPreview from './IAICanvasBrushPreview';
|
||||
import { Vector2d } from 'konva/lib/types';
|
||||
import IAICanvasBoundingBox from './IAICanvasBoundingBox';
|
||||
import useCanvasHotkeys from './hooks/useCanvasHotkeys';
|
||||
import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox';
|
||||
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 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 IAICanvasObjectRenderer from './IAICanvasObjectRenderer';
|
||||
import IAICanvasGrid from './IAICanvasGrid';
|
||||
import IAICanvasIntermediateImage from './IAICanvasIntermediateImage';
|
@ -3,8 +3,8 @@ import { GroupConfig } from 'konva/lib/Group';
|
||||
import _ from 'lodash';
|
||||
import { Circle, Group } from 'react-konva';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import { canvasSelector } from 'features/canvas/canvasSlice';
|
||||
import { rgbaColorToString } from './util/colorToString';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||
|
||||
const canvasBrushPreviewSelector = createSelector(
|
||||
canvasSelector,
|
@ -6,7 +6,7 @@ import { useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import { ReactNode, useCallback, useLayoutEffect, useState } from 'react';
|
||||
import { Group, Line as KonvaLine } from 'react-konva';
|
||||
import { canvasSelector } from './canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
@ -2,9 +2,9 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import { RectConfig } from 'konva/lib/shapes/Rect';
|
||||
import { Rect } from 'react-konva';
|
||||
import { canvasSelector } from './canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
import { rgbaColorToString } from './util/colorToString';
|
||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import Konva from 'konva';
|
||||
|
@ -2,7 +2,8 @@ import { GroupConfig } from 'konva/lib/Group';
|
||||
import { Group, Line } from 'react-konva';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { canvasSelector, isCanvasMaskLine } from './canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { isCanvasMaskLine } from '../store/canvasTypes';
|
||||
import _ from 'lodash';
|
||||
|
||||
export const canvasLinesSelector = createSelector(
|
@ -2,13 +2,10 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import { Group, Line } from 'react-konva';
|
||||
import {
|
||||
canvasSelector,
|
||||
isCanvasBaseImage,
|
||||
isCanvasBaseLine,
|
||||
} from './canvasSlice';
|
||||
import { isCanvasBaseImage, isCanvasBaseLine } from '../store/canvasTypes';
|
||||
import IAICanvasImage from './IAICanvasImage';
|
||||
import { rgbaColorToString } from './util/colorToString';
|
||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
@ -3,14 +3,13 @@ import { useLayoutEffect, useRef } from 'react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import {
|
||||
initialCanvasImageSelector,
|
||||
canvasSelector,
|
||||
resizeAndScaleCanvas,
|
||||
resizeCanvas,
|
||||
setCanvasContainerDimensions,
|
||||
setDoesCanvasNeedScaling,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { canvasSelector, initialCanvasImageSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const canvasResizerSelector = createSelector(
|
||||
canvasSelector,
|
@ -1,10 +1,10 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import { GroupConfig } from 'konva/lib/Group';
|
||||
import _ from 'lodash';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Group, Rect } from 'react-konva';
|
||||
import { canvasSelector } from './canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import IAICanvasImage from './IAICanvasImage';
|
||||
|
||||
const selector = createSelector(
|
@ -21,13 +21,13 @@ import {
|
||||
FaEyeSlash,
|
||||
FaTrash,
|
||||
} from 'react-icons/fa';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
commitStagingAreaImage,
|
||||
canvasSelector,
|
||||
discardStagedImages,
|
||||
nextStagingAreaImage,
|
||||
prevStagingAreaImage,
|
||||
} from './canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import { canvasSelector } from './canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const roundToHundreth = (val: number): number => {
|
||||
return Math.round(val * 100) / 100;
|
@ -11,13 +11,15 @@ import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import {
|
||||
initialCanvasImageSelector,
|
||||
canvasSelector,
|
||||
shouldLockToInitialImageSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
setBoundingBoxCoordinates,
|
||||
setBoundingBoxDimensions,
|
||||
setIsMouseOverBoundingBox,
|
||||
setIsMovingBoundingBox,
|
||||
setIsTransformingBoundingBox,
|
||||
shouldLockToInitialImageSelector,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { GroupConfig } from 'konva/lib/Group';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
|
@ -1,11 +1,5 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
setBrushColor,
|
||||
setBrushSize,
|
||||
setTool,
|
||||
} from './canvasSlice';
|
||||
import { setBrushColor, setBrushSize, setTool } from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
@ -14,6 +8,7 @@ import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
@ -1,10 +1,8 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
setEraserSize,
|
||||
setTool,
|
||||
} from './canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
@ -13,6 +11,7 @@ import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
@ -2,12 +2,11 @@ import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
clearMask,
|
||||
canvasSelector,
|
||||
setIsMaskEnabled,
|
||||
setLayer,
|
||||
setMaskColor,
|
||||
setShouldPreserveMaskedArea,
|
||||
} from './canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
@ -16,6 +15,7 @@ import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector],
|
@ -4,9 +4,10 @@ import { FaRedo } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { canvasSelector, redo } from 'features/canvas/canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { redo } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const canvasRedoSelector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
@ -1,19 +1,19 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
canvasSelector,
|
||||
setShouldAutoSave,
|
||||
setShouldDarkenOutsideBoundingBox,
|
||||
setShouldShowGrid,
|
||||
setShouldShowIntermediates,
|
||||
setShouldSnapToGrid,
|
||||
} from './canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { FaWrench } from 'react-icons/fa';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
export const canvasControlsSelector = createSelector(
|
||||
[canvasSelector],
|
@ -2,16 +2,14 @@ import { ButtonGroup } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
resizeAndScaleCanvas,
|
||||
isStagingSelector,
|
||||
resetCanvas,
|
||||
resetCanvasView,
|
||||
setShouldLockToInitialImage,
|
||||
setTool,
|
||||
canvasSelector,
|
||||
} from './canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import _ from 'lodash';
|
||||
import { canvasImageLayerRef, stageRef } from './IAICanvas';
|
||||
import { canvasImageLayerRef, stageRef } from '../IAICanvas';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import {
|
||||
FaArrowsAlt,
|
||||
@ -23,15 +21,16 @@ import {
|
||||
FaTrash,
|
||||
FaUpload,
|
||||
} from 'react-icons/fa';
|
||||
import IAICanvasUndoButton from './IAICanvasControls/IAICanvasUndoButton';
|
||||
import IAICanvasRedoButton from './IAICanvasControls/IAICanvasRedoButton';
|
||||
import IAICanvasUndoButton from './IAICanvasUndoButton';
|
||||
import IAICanvasRedoButton from './IAICanvasRedoButton';
|
||||
import IAICanvasSettingsButtonPopover from './IAICanvasSettingsButtonPopover';
|
||||
import IAICanvasEraserButtonPopover from './IAICanvasEraserButtonPopover';
|
||||
import IAICanvasBrushButtonPopover from './IAICanvasBrushButtonPopover';
|
||||
import IAICanvasMaskButtonPopover from './IAICanvasMaskButtonPopover';
|
||||
import { mergeAndUploadCanvas } from './util/mergeAndUploadCanvas';
|
||||
import { mergeAndUploadCanvas } from 'features/canvas/util/mergeAndUploadCanvas';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
export const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
@ -3,10 +3,11 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { FaUndo } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { canvasSelector, undo } from 'features/canvas/canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import { undo } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const canvasUndoSelector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
@ -3,12 +3,11 @@ import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import { KonvaEventObject } from 'konva/lib/Node';
|
||||
import _ from 'lodash';
|
||||
import { useCallback } from 'react';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
setIsMovingStage,
|
||||
setStageCoordinates,
|
||||
} from '../canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
|
@ -3,15 +3,15 @@ import _ from 'lodash';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import {
|
||||
CanvasTool,
|
||||
setShouldShowBoundingBox,
|
||||
setTool,
|
||||
toggleShouldLockBoundingBox,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import { canvasSelector } from '../canvasSlice';
|
||||
import { useRef } from 'react';
|
||||
import { stageRef } from '../IAICanvas';
|
||||
import { stageRef } from '../components/IAICanvas';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { CanvasTool } from '../store/canvasTypes';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, activeTabNameSelector],
|
||||
|
@ -5,13 +5,12 @@ import Konva from 'konva';
|
||||
import { KonvaEventObject } from 'konva/lib/Node';
|
||||
import _ from 'lodash';
|
||||
import { MutableRefObject, useCallback } from 'react';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
addLine,
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
setIsDrawing,
|
||||
setIsMovingStage,
|
||||
} from '../canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
||||
|
||||
const selector = createSelector(
|
||||
|
@ -5,12 +5,11 @@ import Konva from 'konva';
|
||||
import { KonvaEventObject } from 'konva/lib/Node';
|
||||
import _ from 'lodash';
|
||||
import { MutableRefObject, useCallback } from 'react';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
addLine,
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
setIsDrawing,
|
||||
} from '../canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
||||
|
||||
const selector = createSelector(
|
||||
|
@ -5,12 +5,11 @@ import Konva from 'konva';
|
||||
import { Vector2d } from 'konva/lib/types';
|
||||
import _ from 'lodash';
|
||||
import { MutableRefObject, useCallback } from 'react';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
addPointToCurrentLine,
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
setCursorPosition,
|
||||
} from '../canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
||||
|
||||
const selector = createSelector(
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import { useCallback } from 'react';
|
||||
import { setCursorPosition, setIsDrawing } from '../canvasSlice';
|
||||
import { setCursorPosition, setIsDrawing } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const useCanvasMouseOut = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
@ -4,14 +4,13 @@ import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import Konva from 'konva';
|
||||
import _ from 'lodash';
|
||||
import { MutableRefObject, useCallback } from 'react';
|
||||
import { canvasSelector, isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import {
|
||||
// addPointToCurrentEraserLine,
|
||||
addPointToCurrentLine,
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
setIsDrawing,
|
||||
setIsMovingStage,
|
||||
} from '../canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
||||
|
||||
const selector = createSelector(
|
||||
|
@ -6,12 +6,11 @@ import { KonvaEventObject } from 'konva/lib/Node';
|
||||
import _ from 'lodash';
|
||||
import { MutableRefObject, useCallback } from 'react';
|
||||
import {
|
||||
initialCanvasImageSelector,
|
||||
canvasSelector,
|
||||
setStageCoordinates,
|
||||
setStageScale,
|
||||
initialCanvasImageSelector,
|
||||
shouldLockToInitialImageSelector,
|
||||
} from '../canvasSlice';
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { setStageCoordinates, setStageScale } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
CANVAS_SCALE_BY,
|
||||
MAX_CANVAS_SCALE,
|
||||
|
@ -1,10 +1,14 @@
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import { CanvasState, initialLayerState } from './canvasSlice';
|
||||
import { initialLayerState } from './canvasSlice';
|
||||
import { CanvasState } from './canvasTypes';
|
||||
import {
|
||||
roundDownToMultiple,
|
||||
roundToMultiple,
|
||||
} from 'common/util/roundDownToMultiple';
|
||||
import _ from 'lodash';
|
||||
import { mergeAndUploadCanvas } from '../util/mergeAndUploadCanvas';
|
||||
import { uploadImage } from 'features/gallery/util/uploadImage';
|
||||
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
|
||||
|
||||
export const setInitialCanvasImage_reducer = (
|
||||
state: CanvasState,
|
||||
@ -50,3 +54,39 @@ export const setInitialCanvasImage_reducer = (
|
||||
state.isCanvasInitialized = false;
|
||||
state.doesCanvasNeedScaling = true;
|
||||
};
|
||||
|
||||
export const canvasExtraReducers = (
|
||||
builder: ActionReducerMapBuilder<CanvasState>
|
||||
) => {
|
||||
builder.addCase(mergeAndUploadCanvas.fulfilled, (state, action) => {
|
||||
if (!action.payload) return;
|
||||
const { image, kind, originalBoundingBox } = action.payload;
|
||||
|
||||
if (kind === 'temp_merged_canvas') {
|
||||
state.pastLayerStates.push({
|
||||
...state.layerState,
|
||||
});
|
||||
|
||||
state.futureLayerStates = [];
|
||||
|
||||
state.layerState.objects = [
|
||||
{
|
||||
kind: 'image',
|
||||
layer: 'base',
|
||||
...originalBoundingBox,
|
||||
image,
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
builder.addCase(uploadImage.fulfilled, (state, action) => {
|
||||
if (!action.payload) return;
|
||||
const { image, kind, activeTabName } = action.payload;
|
||||
|
||||
if (kind !== 'init') return;
|
||||
|
||||
if (activeTabName === 'unifiedCanvas') {
|
||||
setInitialCanvasImage_reducer(state, image);
|
||||
}
|
||||
});
|
||||
};
|
15
frontend/src/features/canvas/store/canvasSelectors.ts
Normal file
15
frontend/src/features/canvas/store/canvasSelectors.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { RootState } from 'app/store';
|
||||
import { CanvasImage, CanvasState, isCanvasBaseImage } from './canvasTypes';
|
||||
|
||||
export const canvasSelector = (state: RootState): CanvasState => state.canvas;
|
||||
|
||||
export const isStagingSelector = (state: RootState): boolean =>
|
||||
state.canvas.layerState.stagingArea.images.length > 0;
|
||||
|
||||
export const shouldLockToInitialImageSelector = (state: RootState): boolean =>
|
||||
state.canvas.shouldLockToInitialImage;
|
||||
|
||||
export const initialCanvasImageSelector = (
|
||||
state: RootState
|
||||
): CanvasImage | undefined =>
|
||||
state.canvas.layerState.objects.find(isCanvasBaseImage);
|
@ -5,125 +5,23 @@ import { RgbaColor } from 'react-colorful';
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import _ from 'lodash';
|
||||
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { RootState } from 'app/store';
|
||||
import { mergeAndUploadCanvas } from './util/mergeAndUploadCanvas';
|
||||
import { uploadImage } from 'features/gallery/util/uploadImage';
|
||||
import { setInitialCanvasImage_reducer } from './canvasReducers';
|
||||
import calculateScale from './util/calculateScale';
|
||||
import calculateCoordinates from './util/calculateCoordinates';
|
||||
import floorCoordinates from './util/floorCoordinates';
|
||||
|
||||
export type CanvasLayer = 'base' | 'mask';
|
||||
|
||||
export type CanvasDrawingTool = 'brush' | 'eraser';
|
||||
|
||||
export type CanvasTool = CanvasDrawingTool | 'move';
|
||||
|
||||
export type Dimensions = {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export type CanvasAnyLine = {
|
||||
kind: 'line';
|
||||
tool: CanvasDrawingTool;
|
||||
strokeWidth: number;
|
||||
points: number[];
|
||||
};
|
||||
|
||||
export type CanvasImage = {
|
||||
kind: 'image';
|
||||
layer: 'base';
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
image: InvokeAI.Image;
|
||||
};
|
||||
|
||||
export type CanvasMaskLine = CanvasAnyLine & {
|
||||
layer: 'mask';
|
||||
};
|
||||
|
||||
export type CanvasLine = CanvasAnyLine & {
|
||||
layer: 'base';
|
||||
color?: RgbaColor;
|
||||
};
|
||||
|
||||
export type CanvasObject = CanvasImage | CanvasLine | CanvasMaskLine;
|
||||
|
||||
export type CanvasLayerState = {
|
||||
objects: CanvasObject[];
|
||||
stagingArea: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
images: CanvasImage[];
|
||||
selectedImageIndex: number;
|
||||
};
|
||||
};
|
||||
|
||||
// type guards
|
||||
export const isCanvasMaskLine = (obj: CanvasObject): obj is CanvasMaskLine =>
|
||||
obj.kind === 'line' && obj.layer === 'mask';
|
||||
|
||||
export const isCanvasBaseLine = (obj: CanvasObject): obj is CanvasLine =>
|
||||
obj.kind === 'line' && obj.layer === 'base';
|
||||
|
||||
export const isCanvasBaseImage = (obj: CanvasObject): obj is CanvasImage =>
|
||||
obj.kind === 'image' && obj.layer === 'base';
|
||||
|
||||
export const isCanvasAnyLine = (
|
||||
obj: CanvasObject
|
||||
): obj is CanvasMaskLine | CanvasLine => obj.kind === 'line';
|
||||
|
||||
export interface CanvasState {
|
||||
boundingBoxCoordinates: Vector2d;
|
||||
boundingBoxDimensions: Dimensions;
|
||||
boundingBoxPreviewFill: RgbaColor;
|
||||
brushColor: RgbaColor;
|
||||
brushSize: number;
|
||||
canvasContainerDimensions: Dimensions;
|
||||
cursorPosition: Vector2d | null;
|
||||
doesCanvasNeedScaling: boolean;
|
||||
eraserSize: number;
|
||||
futureLayerStates: CanvasLayerState[];
|
||||
inpaintReplace: number;
|
||||
intermediateImage?: InvokeAI.Image;
|
||||
isCanvasInitialized: boolean;
|
||||
isDrawing: boolean;
|
||||
isMaskEnabled: boolean;
|
||||
isMouseOverBoundingBox: boolean;
|
||||
isMoveBoundingBoxKeyHeld: boolean;
|
||||
isMoveStageKeyHeld: boolean;
|
||||
isMovingBoundingBox: boolean;
|
||||
isMovingStage: boolean;
|
||||
isTransformingBoundingBox: boolean;
|
||||
layer: CanvasLayer;
|
||||
layerState: CanvasLayerState;
|
||||
maskColor: RgbaColor;
|
||||
maxHistory: number;
|
||||
minimumStageScale: number;
|
||||
pastLayerStates: CanvasLayerState[];
|
||||
shouldAutoSave: boolean;
|
||||
shouldDarkenOutsideBoundingBox: boolean;
|
||||
shouldLockBoundingBox: boolean;
|
||||
shouldLockToInitialImage: boolean;
|
||||
shouldPreserveMaskedArea: boolean;
|
||||
shouldShowBoundingBox: boolean;
|
||||
shouldShowBrush: boolean;
|
||||
shouldShowBrushPreview: boolean;
|
||||
shouldShowCheckboardTransparency: boolean;
|
||||
shouldShowGrid: boolean;
|
||||
shouldShowIntermediates: boolean;
|
||||
shouldSnapToGrid: boolean;
|
||||
shouldUseInpaintReplace: boolean;
|
||||
stageCoordinates: Vector2d;
|
||||
stageDimensions: Dimensions;
|
||||
stageScale: number;
|
||||
tool: CanvasTool;
|
||||
}
|
||||
import {
|
||||
canvasExtraReducers,
|
||||
setInitialCanvasImage_reducer,
|
||||
} from './canvasExtraReducers';
|
||||
import calculateScale from '../util/calculateScale';
|
||||
import calculateCoordinates from '../util/calculateCoordinates';
|
||||
import floorCoordinates from '../util/floorCoordinates';
|
||||
import {
|
||||
CanvasLayer,
|
||||
CanvasLayerState,
|
||||
CanvasState,
|
||||
CanvasTool,
|
||||
Dimensions,
|
||||
isCanvasAnyLine,
|
||||
isCanvasBaseImage,
|
||||
isCanvasMaskLine,
|
||||
} from './canvasTypes';
|
||||
|
||||
export const initialLayerState: CanvasLayerState = {
|
||||
objects: [],
|
||||
@ -473,9 +371,6 @@ export const canvasSlice = createSlice({
|
||||
|
||||
const { width: imageWidth, height: imageHeight } = initialCanvasImage;
|
||||
|
||||
// const { clientWidth, clientHeight, imageWidth, imageHeight } =
|
||||
// action.payload;
|
||||
|
||||
const { shouldLockToInitialImage } = state;
|
||||
|
||||
const padding = shouldLockToInitialImage ? 1 : 0.95;
|
||||
@ -604,40 +499,7 @@ export const canvasSlice = createSlice({
|
||||
state.shouldLockToInitialImage = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(mergeAndUploadCanvas.fulfilled, (state, action) => {
|
||||
if (!action.payload) return;
|
||||
const { image, kind, originalBoundingBox } = action.payload;
|
||||
|
||||
if (kind === 'temp_merged_canvas') {
|
||||
state.pastLayerStates.push({
|
||||
...state.layerState,
|
||||
});
|
||||
|
||||
state.futureLayerStates = [];
|
||||
|
||||
state.layerState.objects = [
|
||||
{
|
||||
kind: 'image',
|
||||
layer: 'base',
|
||||
...originalBoundingBox,
|
||||
image,
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
builder.addCase(uploadImage.fulfilled, (state, action) => {
|
||||
if (!action.payload) return;
|
||||
const { image, kind, activeTabName } = action.payload;
|
||||
|
||||
if (kind !== 'init') return;
|
||||
|
||||
if (activeTabName === 'unifiedCanvas') {
|
||||
setInitialCanvasImage_reducer(state, image);
|
||||
}
|
||||
});
|
||||
},
|
||||
extraReducers: canvasExtraReducers,
|
||||
});
|
||||
|
||||
export const {
|
||||
@ -699,16 +561,3 @@ export const {
|
||||
} = canvasSlice.actions;
|
||||
|
||||
export default canvasSlice.reducer;
|
||||
|
||||
export const canvasSelector = (state: RootState): CanvasState => state.canvas;
|
||||
|
||||
export const isStagingSelector = (state: RootState): boolean =>
|
||||
state.canvas.layerState.stagingArea.images.length > 0;
|
||||
|
||||
export const shouldLockToInitialImageSelector = (state: RootState): boolean =>
|
||||
state.canvas.shouldLockToInitialImage;
|
||||
|
||||
export const initialCanvasImageSelector = (
|
||||
state: RootState
|
||||
): CanvasImage | undefined =>
|
||||
state.canvas.layerState.objects.find(isCanvasBaseImage);
|
115
frontend/src/features/canvas/store/canvasTypes.ts
Normal file
115
frontend/src/features/canvas/store/canvasTypes.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import { Vector2d } from 'konva/lib/types';
|
||||
import { RgbaColor } from 'react-colorful';
|
||||
|
||||
export type CanvasLayer = 'base' | 'mask';
|
||||
|
||||
export type CanvasDrawingTool = 'brush' | 'eraser';
|
||||
|
||||
export type CanvasTool = CanvasDrawingTool | 'move';
|
||||
|
||||
export type Dimensions = {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export type CanvasAnyLine = {
|
||||
kind: 'line';
|
||||
tool: CanvasDrawingTool;
|
||||
strokeWidth: number;
|
||||
points: number[];
|
||||
};
|
||||
|
||||
export type CanvasImage = {
|
||||
kind: 'image';
|
||||
layer: 'base';
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
image: InvokeAI.Image;
|
||||
};
|
||||
|
||||
export type CanvasMaskLine = CanvasAnyLine & {
|
||||
layer: 'mask';
|
||||
};
|
||||
|
||||
export type CanvasLine = CanvasAnyLine & {
|
||||
layer: 'base';
|
||||
color?: RgbaColor;
|
||||
};
|
||||
|
||||
export type CanvasObject = CanvasImage | CanvasLine | CanvasMaskLine;
|
||||
|
||||
export type CanvasLayerState = {
|
||||
objects: CanvasObject[];
|
||||
stagingArea: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
images: CanvasImage[];
|
||||
selectedImageIndex: number;
|
||||
};
|
||||
};
|
||||
|
||||
// type guards
|
||||
export const isCanvasMaskLine = (obj: CanvasObject): obj is CanvasMaskLine =>
|
||||
obj.kind === 'line' && obj.layer === 'mask';
|
||||
|
||||
export const isCanvasBaseLine = (obj: CanvasObject): obj is CanvasLine =>
|
||||
obj.kind === 'line' && obj.layer === 'base';
|
||||
|
||||
export const isCanvasBaseImage = (obj: CanvasObject): obj is CanvasImage =>
|
||||
obj.kind === 'image' && obj.layer === 'base';
|
||||
|
||||
export const isCanvasAnyLine = (
|
||||
obj: CanvasObject
|
||||
): obj is CanvasMaskLine | CanvasLine => obj.kind === 'line';
|
||||
|
||||
export interface CanvasState {
|
||||
boundingBoxCoordinates: Vector2d;
|
||||
boundingBoxDimensions: Dimensions;
|
||||
boundingBoxPreviewFill: RgbaColor;
|
||||
brushColor: RgbaColor;
|
||||
brushSize: number;
|
||||
canvasContainerDimensions: Dimensions;
|
||||
cursorPosition: Vector2d | null;
|
||||
doesCanvasNeedScaling: boolean;
|
||||
eraserSize: number;
|
||||
futureLayerStates: CanvasLayerState[];
|
||||
inpaintReplace: number;
|
||||
intermediateImage?: InvokeAI.Image;
|
||||
isCanvasInitialized: boolean;
|
||||
isDrawing: boolean;
|
||||
isMaskEnabled: boolean;
|
||||
isMouseOverBoundingBox: boolean;
|
||||
isMoveBoundingBoxKeyHeld: boolean;
|
||||
isMoveStageKeyHeld: boolean;
|
||||
isMovingBoundingBox: boolean;
|
||||
isMovingStage: boolean;
|
||||
isTransformingBoundingBox: boolean;
|
||||
layer: CanvasLayer;
|
||||
layerState: CanvasLayerState;
|
||||
maskColor: RgbaColor;
|
||||
maxHistory: number;
|
||||
minimumStageScale: number;
|
||||
pastLayerStates: CanvasLayerState[];
|
||||
shouldAutoSave: boolean;
|
||||
shouldDarkenOutsideBoundingBox: boolean;
|
||||
shouldLockBoundingBox: boolean;
|
||||
shouldLockToInitialImage: boolean;
|
||||
shouldPreserveMaskedArea: boolean;
|
||||
shouldShowBoundingBox: boolean;
|
||||
shouldShowBrush: boolean;
|
||||
shouldShowBrushPreview: boolean;
|
||||
shouldShowCheckboardTransparency: boolean;
|
||||
shouldShowGrid: boolean;
|
||||
shouldShowIntermediates: boolean;
|
||||
shouldSnapToGrid: boolean;
|
||||
shouldUseInpaintReplace: boolean;
|
||||
stageCoordinates: Vector2d;
|
||||
stageDimensions: Dimensions;
|
||||
stageScale: number;
|
||||
tool: CanvasTool;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import Konva from 'konva';
|
||||
import { IRect } from 'konva/lib/types';
|
||||
import { CanvasMaskLine } from 'features/canvas/canvasSlice';
|
||||
import { CanvasMaskLine } from 'features/canvas/store/canvasTypes';
|
||||
|
||||
/**
|
||||
* Generating a mask image from InpaintingCanvas.tsx is not as simple
|
||||
|
@ -39,7 +39,7 @@ import {
|
||||
setDoesCanvasNeedScaling,
|
||||
setInitialCanvasImage,
|
||||
setShouldLockToInitialImage,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { GalleryState } from './gallerySlice';
|
||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
|
@ -26,7 +26,7 @@ import {
|
||||
setDoesCanvasNeedScaling,
|
||||
setInitialCanvasImage,
|
||||
setShouldLockToInitialImage,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { hoverableImageSelector } from './gallerySliceSelectors';
|
||||
|
||||
interface HoverableImageProps {
|
||||
|
@ -31,7 +31,7 @@ import IAIPopover from 'common/components/IAIPopover';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { BiReset } from 'react-icons/bi';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
import _ from 'lodash';
|
||||
import useClickOutsideWatcher from 'common/hooks/useClickOutsideWatcher';
|
||||
|
||||
|
@ -1,11 +1,9 @@
|
||||
import React from 'react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import {
|
||||
canvasSelector,
|
||||
setShouldDarkenOutsideBoundingBox,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { setShouldDarkenOutsideBoundingBox } from 'features/canvas/store/canvasSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const selector = createSelector(
|
||||
canvasSelector,
|
||||
|
@ -3,15 +3,13 @@ import IAISlider from 'common/components/IAISlider';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import {
|
||||
canvasSelector,
|
||||
setBoundingBoxDimensions,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import _ from 'lodash';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const boundingBoxDimensionsSelector = createSelector(
|
||||
const selector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => {
|
||||
const { stageDimensions, boundingBoxDimensions, shouldLockBoundingBox } =
|
||||
@ -40,7 +38,7 @@ export default function BoundingBoxDimensionSlider(
|
||||
const { dimension, label } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
const { shouldLockBoundingBox, stageDimensions, boundingBoxDimensions } =
|
||||
useAppSelector(boundingBoxDimensionsSelector);
|
||||
useAppSelector(selector);
|
||||
|
||||
const canvasDimension = stageDimensions[dimension];
|
||||
const boundingBoxDimension = boundingBoxDimensions[dimension];
|
||||
|
@ -1,19 +1,17 @@
|
||||
import React from 'react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
import {
|
||||
canvasSelector,
|
||||
setShouldLockBoundingBox,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { setShouldLockBoundingBox } from 'features/canvas/store/canvasSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const boundingBoxLockSelector = createSelector(
|
||||
const selector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => canvas.shouldLockBoundingBox
|
||||
);
|
||||
|
||||
export default function BoundingBoxLock() {
|
||||
const shouldLockBoundingBox = useAppSelector(boundingBoxLockSelector);
|
||||
const shouldLockBoundingBox = useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleChangeShouldLockBoundingBox = () => {
|
||||
|
@ -2,19 +2,17 @@ import React from 'react';
|
||||
import { BiHide, BiShow } from 'react-icons/bi';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import {
|
||||
canvasSelector,
|
||||
setShouldShowBoundingBox,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { setShouldShowBoundingBox } from 'features/canvas/store/canvasSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const boundingBoxVisibilitySelector = createSelector(
|
||||
const selector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => canvas.shouldShowBoundingBox
|
||||
);
|
||||
|
||||
export default function BoundingBoxVisibility() {
|
||||
const shouldShowBoundingBox = useAppSelector(boundingBoxVisibilitySelector);
|
||||
const shouldShowBoundingBox = useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleShowBoundingBox = () =>
|
||||
|
@ -2,13 +2,11 @@ import { useToast } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import {
|
||||
canvasSelector,
|
||||
setClearBrushHistory,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { setClearBrushHistory } from 'features/canvas/store/canvasSlice';
|
||||
import _ from 'lodash';
|
||||
|
||||
const clearBrushHistorySelector = createSelector(
|
||||
const selector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => {
|
||||
const { pastLayerStates, futureLayerStates } = canvas;
|
||||
@ -30,7 +28,7 @@ export default function ClearBrushHistory() {
|
||||
const dispatch = useAppDispatch();
|
||||
const toast = useToast();
|
||||
|
||||
const { mayClearBrushHistory } = useAppSelector(clearBrushHistorySelector);
|
||||
const { mayClearBrushHistory } = useAppSelector(selector);
|
||||
|
||||
const handleClearBrushHistory = () => {
|
||||
dispatch(setClearBrushHistory());
|
||||
|
@ -6,12 +6,12 @@ import IAISwitch from '../../../../common/components/IAISwitch';
|
||||
import IAISlider from '../../../../common/components/IAISlider';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import {
|
||||
canvasSelector,
|
||||
setInpaintReplace,
|
||||
setShouldUseInpaintReplace,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const canvasInpaintReplaceSelector = createSelector(
|
||||
const selector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => {
|
||||
const { inpaintReplace, shouldUseInpaintReplace } = canvas;
|
||||
@ -28,9 +28,7 @@ const canvasInpaintReplaceSelector = createSelector(
|
||||
);
|
||||
|
||||
export default function InpaintReplace() {
|
||||
const { inpaintReplace, shouldUseInpaintReplace } = useAppSelector(
|
||||
canvasInpaintReplaceSelector
|
||||
);
|
||||
const { inpaintReplace, shouldUseInpaintReplace } = useAppSelector(selector);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { MdPhotoLibrary } from 'react-icons/md';
|
||||
import { RootState, useAppDispatch, useAppSelector } from 'app/store';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { setShouldShowGallery } from 'features/gallery/gallerySlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const FloatingGalleryButton = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
@ -10,7 +10,7 @@ import CancelButton from 'features/options/ProcessButtons/CancelButton';
|
||||
import InvokeButton from 'features/options/ProcessButtons/InvokeButton';
|
||||
import _ from 'lodash';
|
||||
import LoopbackButton from 'features/options/ProcessButtons/Loopback';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const canInvokeSelector = createSelector(
|
||||
(state: RootState) => state.options,
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
setShouldPinOptionsPanel,
|
||||
setShouldShowOptionsPanel,
|
||||
} from 'features/options/optionsSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
import InvokeAILogo from 'assets/images/logo.png';
|
||||
|
||||
type Props = { children: ReactNode };
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
import ImageToImageWorkarea from './ImageToImage';
|
||||
import TextToImageWorkarea from './TextToImage';
|
||||
import Lightbox from 'features/lightbox/Lightbox';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
import UnifiedCanvasWorkarea from './UnifiedCanvas/UnifiedCanvasWorkarea';
|
||||
import { setShouldShowGallery } from 'features/gallery/gallerySlice';
|
||||
import UnifiedCanvasIcon from 'common/icons/UnifiedCanvasIcon';
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
OptionsState,
|
||||
setShowDualDisplay,
|
||||
} from 'features/options/optionsSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const workareaSelector = createSelector(
|
||||
[(state: RootState) => state.options, activeTabNameSelector],
|
||||
|
@ -1,4 +1,4 @@
|
||||
@use '../../styles/Mixins/' as *;
|
||||
@use '../../../styles/Mixins/' as *;
|
||||
|
||||
.inpainting-main-area {
|
||||
display: flex;
|
@ -1,16 +1,13 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
// import IAICanvas from 'features/canvas/IAICanvas';
|
||||
import IAICanvasResizer from 'features/canvas/IAICanvasResizer';
|
||||
import IAICanvasResizer from 'features/canvas/components/IAICanvasResizer';
|
||||
import _ from 'lodash';
|
||||
import { useLayoutEffect } from 'react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||
import ImageUploadButton from 'common/components/ImageUploaderButton';
|
||||
import {
|
||||
canvasSelector,
|
||||
setDoesCanvasNeedScaling,
|
||||
} from 'features/canvas/canvasSlice';
|
||||
import IAICanvas from 'features/canvas/IAICanvas';
|
||||
import IAICanvasOutpaintingControls from 'features/canvas/IAICanvasOutpaintingControls';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
import IAICanvas from 'features/canvas/components/IAICanvas';
|
||||
import IAICanvasOutpaintingControls from 'features/canvas/components/IAICanvasToolbar/IAICanvasToolbar';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector],
|
||||
|
@ -3,7 +3,7 @@ import UnifiedCanvasDisplay from './UnifiedCanvasDisplay';
|
||||
import InvokeWorkarea from 'features/tabs/InvokeWorkarea';
|
||||
import { useAppDispatch } from 'app/store';
|
||||
import { useEffect } from 'react';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/canvasSlice';
|
||||
import { setDoesCanvasNeedScaling } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
export default function UnifiedCanvasWorkarea() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
@ -46,7 +46,7 @@
|
||||
@use '../features/tabs/TextToImage/TextToImage.scss';
|
||||
@use '../features/tabs/ImageToImage/ImageToImage.scss';
|
||||
@use '../features/tabs/FloatingButton.scss';
|
||||
@use '../features/tabs/CanvasWorkarea.scss';
|
||||
@use '../features/tabs/UnifiedCanvas/CanvasWorkarea.scss';
|
||||
|
||||
// Component Shared
|
||||
@use '../common/components/IAINumberInput.scss';
|
||||
|
Loading…
Reference in New Issue
Block a user