mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): clean up canvas selectors
Do not memoize unless absolutely necessary. Minor perf improvement
This commit is contained in:
parent
2c049a3b94
commit
73481d4aec
@ -42,57 +42,34 @@ import IAICanvasStatusText from './IAICanvasStatusText';
|
|||||||
import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox';
|
import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox';
|
||||||
import IAICanvasToolPreview from './IAICanvasToolPreview';
|
import IAICanvasToolPreview from './IAICanvasToolPreview';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||||
[selectCanvasSlice, isStagingSelector],
|
|
||||||
(canvas, isStaging) => {
|
|
||||||
const {
|
|
||||||
isMaskEnabled,
|
|
||||||
stageScale,
|
|
||||||
shouldShowBoundingBox,
|
|
||||||
stageDimensions,
|
|
||||||
stageCoordinates,
|
|
||||||
tool,
|
|
||||||
shouldShowIntermediates,
|
|
||||||
shouldRestrictStrokesToBox,
|
|
||||||
shouldShowGrid,
|
|
||||||
shouldAntialias,
|
|
||||||
} = canvas;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isMaskEnabled,
|
stageCoordinates: canvas.stageCoordinates,
|
||||||
shouldShowBoundingBox,
|
stageDimensions: canvas.stageDimensions,
|
||||||
shouldShowGrid,
|
|
||||||
stageCoordinates,
|
|
||||||
stageDimensions,
|
|
||||||
stageScale,
|
|
||||||
tool,
|
|
||||||
isStaging,
|
|
||||||
shouldShowIntermediates,
|
|
||||||
shouldAntialias,
|
|
||||||
shouldRestrictStrokesToBox,
|
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const ChakraStage = chakra(Stage, {
|
const ChakraStage = chakra(Stage, {
|
||||||
shouldForwardProp: (prop) => !['sx'].includes(prop),
|
shouldForwardProp: (prop) => !['sx'].includes(prop),
|
||||||
});
|
});
|
||||||
|
|
||||||
const IAICanvas = () => {
|
const IAICanvas = () => {
|
||||||
const {
|
const isStaging = useAppSelector(isStagingSelector);
|
||||||
isMaskEnabled,
|
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||||
shouldShowBoundingBox,
|
const shouldShowBoundingBox = useAppSelector(
|
||||||
shouldShowGrid,
|
(s) => s.canvas.shouldShowBoundingBox
|
||||||
stageCoordinates,
|
);
|
||||||
stageDimensions,
|
const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid);
|
||||||
stageScale,
|
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||||
tool,
|
const tool = useAppSelector((s) => s.canvas.tool);
|
||||||
isStaging,
|
const shouldShowIntermediates = useAppSelector(
|
||||||
shouldShowIntermediates,
|
(s) => s.canvas.shouldShowIntermediates
|
||||||
shouldAntialias,
|
);
|
||||||
shouldRestrictStrokesToBox,
|
const shouldAntialias = useAppSelector((s) => s.canvas.shouldAntialias);
|
||||||
} = useAppSelector(selector);
|
const shouldRestrictStrokesToBox = useAppSelector(
|
||||||
useCanvasHotkeys();
|
(s) => s.canvas.shouldRestrictStrokesToBox
|
||||||
|
);
|
||||||
|
const { stageCoordinates, stageDimensions } = useAppSelector(selector);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const stageRef = useRef<Konva.Stage | null>(null);
|
const stageRef = useRef<Konva.Stage | null>(null);
|
||||||
@ -101,6 +78,7 @@ const IAICanvas = () => {
|
|||||||
const isMovingStage = useStore($isMovingStage);
|
const isMovingStage = useStore($isMovingStage);
|
||||||
const isTransformingBoundingBox = useStore($isTransformingBoundingBox);
|
const isTransformingBoundingBox = useStore($isTransformingBoundingBox);
|
||||||
const isMouseOverBoundingBox = useStore($isMouseOverBoundingBox);
|
const isMouseOverBoundingBox = useStore($isMouseOverBoundingBox);
|
||||||
|
useCanvasHotkeys();
|
||||||
const canvasStageRefCallback = useCallback((el: Konva.Stage) => {
|
const canvasStageRefCallback = useCallback((el: Konva.Stage) => {
|
||||||
setCanvasStage(el as Konva.Stage);
|
setCanvasStage(el as Konva.Stage);
|
||||||
stageRef.current = el;
|
stageRef.current = el;
|
||||||
|
@ -9,29 +9,28 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
|||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
stageScale,
|
|
||||||
shouldDarkenOutsideBoundingBox,
|
|
||||||
stageCoordinates,
|
stageCoordinates,
|
||||||
} = canvas;
|
} = canvas;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
shouldDarkenOutsideBoundingBox,
|
|
||||||
stageCoordinates,
|
stageCoordinates,
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
stageScale,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const IAICanvasBoundingBoxOverlay = () => {
|
const IAICanvasBoundingBoxOverlay = () => {
|
||||||
const {
|
const {
|
||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
shouldDarkenOutsideBoundingBox,
|
|
||||||
stageCoordinates,
|
stageCoordinates,
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
stageScale,
|
|
||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
|
const shouldDarkenOutsideBoundingBox = useAppSelector(
|
||||||
|
(s) => s.canvas.shouldDarkenOutsideBoundingBox
|
||||||
|
);
|
||||||
|
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group listening={false}>
|
<Group listening={false}>
|
||||||
|
@ -7,17 +7,19 @@ import { memo, useCallback, useMemo } from 'react';
|
|||||||
import { Group, Line as KonvaLine } from 'react-konva';
|
import { Group, Line as KonvaLine } from 'react-konva';
|
||||||
import { getArbitraryBaseColor } from 'theme/colors';
|
import { getArbitraryBaseColor } from 'theme/colors';
|
||||||
|
|
||||||
const selector = createMemoizedSelector([selectCanvasSlice], (canvas) => {
|
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
|
||||||
const { stageScale, stageCoordinates, stageDimensions } = canvas;
|
return {
|
||||||
return { stageScale, stageCoordinates, stageDimensions };
|
stageCoordinates: canvas.stageCoordinates,
|
||||||
|
stageDimensions: canvas.stageDimensions,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const baseGridLineColor = getArbitraryBaseColor(27);
|
const baseGridLineColor = getArbitraryBaseColor(27);
|
||||||
const fineGridLineColor = getArbitraryBaseColor(18);
|
const fineGridLineColor = getArbitraryBaseColor(18);
|
||||||
|
|
||||||
const IAICanvasGrid = () => {
|
const IAICanvasGrid = () => {
|
||||||
const { stageScale, stageCoordinates, stageDimensions } =
|
const { stageCoordinates, stageDimensions } = useAppSelector(selector);
|
||||||
useAppSelector(selector);
|
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||||
|
|
||||||
const gridSpacing = useMemo(() => {
|
const gridSpacing = useMemo(() => {
|
||||||
if (stageScale >= 2) {
|
if (stageScale >= 2) {
|
||||||
|
@ -1,33 +1,28 @@
|
|||||||
import {
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
createLruSelector,
|
|
||||||
createMemoizedSelector,
|
|
||||||
} from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||||
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
||||||
import { memo, useEffect, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import { Image as KonvaImage } from 'react-konva';
|
import { Image as KonvaImage } from 'react-konva';
|
||||||
|
|
||||||
const progressImageSelector = createLruSelector(
|
const progressImageSelector = createMemoizedSelector(
|
||||||
[selectSystemSlice, selectCanvasSlice],
|
[selectSystemSlice, selectCanvasSlice],
|
||||||
(system, canvas) => {
|
(system, canvas) => {
|
||||||
const { denoiseProgress } = system;
|
const { denoiseProgress } = system;
|
||||||
const { batchIds } = canvas;
|
const { batchIds } = canvas;
|
||||||
|
|
||||||
return denoiseProgress && batchIds.includes(denoiseProgress.batch_id)
|
return {
|
||||||
|
progressImage:
|
||||||
|
denoiseProgress && batchIds.includes(denoiseProgress.batch_id)
|
||||||
? denoiseProgress.progress_image
|
? denoiseProgress.progress_image
|
||||||
: undefined;
|
: undefined,
|
||||||
|
boundingBox: canvas.layerState.stagingArea.boundingBox,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const boundingBoxSelector = createMemoizedSelector(
|
|
||||||
[selectCanvasSlice],
|
|
||||||
(canvas) => canvas.layerState.stagingArea.boundingBox
|
|
||||||
);
|
|
||||||
|
|
||||||
const IAICanvasIntermediateImage = () => {
|
const IAICanvasIntermediateImage = () => {
|
||||||
const progressImage = useAppSelector(progressImageSelector);
|
const { progressImage, boundingBox } = useAppSelector(progressImageSelector);
|
||||||
const boundingBox = useAppSelector(boundingBoxSelector);
|
|
||||||
const [loadedImageElement, setLoadedImageElement] =
|
const [loadedImageElement, setLoadedImageElement] =
|
||||||
useState<HTMLImageElement | null>(null);
|
useState<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
|
import { getColoredMaskSVG } from 'features/canvas/util/getColoredMaskSVG';
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
import type { RectConfig } from 'konva/lib/shapes/Rect';
|
import type { RectConfig } from 'konva/lib/shapes/Rect';
|
||||||
import { isNumber } from 'lodash-es';
|
import { isNumber } from 'lodash-es';
|
||||||
@ -11,107 +12,25 @@ import { Rect } from 'react-konva';
|
|||||||
export const canvasMaskCompositerSelector = createMemoizedSelector(
|
export const canvasMaskCompositerSelector = createMemoizedSelector(
|
||||||
selectCanvasSlice,
|
selectCanvasSlice,
|
||||||
(canvas) => {
|
(canvas) => {
|
||||||
const { maskColor, stageCoordinates, stageDimensions, stageScale } = canvas;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
stageCoordinates,
|
stageCoordinates: canvas.stageCoordinates,
|
||||||
stageDimensions,
|
stageDimensions: canvas.stageDimensions,
|
||||||
stageScale,
|
|
||||||
maskColorString: rgbaColorToString(maskColor),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
type IAICanvasMaskCompositerProps = RectConfig;
|
type IAICanvasMaskCompositerProps = RectConfig;
|
||||||
|
|
||||||
const getColoredSVG = (color: string) => {
|
|
||||||
return `data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="60px" height="60px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,0)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,2.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,7.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,10)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,12.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,15)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,17.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,20)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,22.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,25)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,27.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,30)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-2.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-7.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-10)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-12.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-15)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-17.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-20)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-22.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-25)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-27.5)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(0.5,0,0,0.5,0,-30)">
|
|
||||||
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
|
||||||
</g>
|
|
||||||
</svg>`.replaceAll('black', color);
|
|
||||||
};
|
|
||||||
|
|
||||||
const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
|
const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
|
||||||
const { ...rest } = props;
|
const { ...rest } = props;
|
||||||
|
|
||||||
const { maskColorString, stageCoordinates, stageDimensions, stageScale } =
|
const { stageCoordinates, stageDimensions } = useAppSelector(
|
||||||
useAppSelector(canvasMaskCompositerSelector);
|
canvasMaskCompositerSelector
|
||||||
|
);
|
||||||
|
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||||
|
const maskColorString = useAppSelector((s) =>
|
||||||
|
rgbaColorToString(s.canvas.maskColor)
|
||||||
|
);
|
||||||
const [fillPatternImage, setFillPatternImage] =
|
const [fillPatternImage, setFillPatternImage] =
|
||||||
useState<HTMLImageElement | null>(null);
|
useState<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
@ -132,14 +51,14 @@ const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
|
|||||||
image.onload = () => {
|
image.onload = () => {
|
||||||
setFillPatternImage(image);
|
setFillPatternImage(image);
|
||||||
};
|
};
|
||||||
image.src = getColoredSVG(maskColorString);
|
image.src = getColoredMaskSVG(maskColorString);
|
||||||
}, [fillPatternImage, maskColorString]);
|
}, [fillPatternImage, maskColorString]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!fillPatternImage) {
|
if (!fillPatternImage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fillPatternImage.src = getColoredSVG(maskColorString);
|
fillPatternImage.src = getColoredMaskSVG(maskColorString);
|
||||||
}, [fillPatternImage, maskColorString]);
|
}, [fillPatternImage, maskColorString]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -20,13 +20,6 @@ const canvasBrushPreviewSelector = createMemoizedSelector(
|
|||||||
selectCanvasSlice,
|
selectCanvasSlice,
|
||||||
(canvas) => {
|
(canvas) => {
|
||||||
const {
|
const {
|
||||||
brushSize,
|
|
||||||
colorPickerColor,
|
|
||||||
maskColor,
|
|
||||||
brushColor,
|
|
||||||
tool,
|
|
||||||
layer,
|
|
||||||
stageScale,
|
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
@ -82,17 +75,6 @@ const canvasBrushPreviewSelector = createMemoizedSelector(
|
|||||||
// : undefined;
|
// : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
radius: brushSize / 2,
|
|
||||||
colorPickerOuterRadius: COLOR_PICKER_SIZE / stageScale,
|
|
||||||
colorPickerInnerRadius:
|
|
||||||
(COLOR_PICKER_SIZE - COLOR_PICKER_STROKE_RADIUS + 1) / stageScale,
|
|
||||||
maskColorString: rgbaColorToString({ ...maskColor, a: 0.5 }),
|
|
||||||
brushColorString: rgbaColorToString(brushColor),
|
|
||||||
colorPickerColorString: rgbaColorToString(colorPickerColor),
|
|
||||||
tool,
|
|
||||||
layer,
|
|
||||||
strokeWidth: 1.5 / stageScale,
|
|
||||||
dotRadius: 1.5 / stageScale,
|
|
||||||
clip,
|
clip,
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
};
|
};
|
||||||
@ -103,20 +85,28 @@ const canvasBrushPreviewSelector = createMemoizedSelector(
|
|||||||
* Draws a black circle around the canvas brush preview.
|
* Draws a black circle around the canvas brush preview.
|
||||||
*/
|
*/
|
||||||
const IAICanvasToolPreview = (props: GroupConfig) => {
|
const IAICanvasToolPreview = (props: GroupConfig) => {
|
||||||
const {
|
const radius = useAppSelector((s) => s.canvas.brushSize / 2);
|
||||||
radius,
|
const maskColorString = useAppSelector((s) =>
|
||||||
maskColorString,
|
rgbaColorToString({ ...s.canvas.maskColor, a: 0.5 })
|
||||||
tool,
|
);
|
||||||
layer,
|
const tool = useAppSelector((s) => s.canvas.tool);
|
||||||
dotRadius,
|
const layer = useAppSelector((s) => s.canvas.layer);
|
||||||
strokeWidth,
|
const dotRadius = useAppSelector((s) => 1.5 / s.canvas.stageScale);
|
||||||
brushColorString,
|
const strokeWidth = useAppSelector((s) => 1.5 / s.canvas.stageScale);
|
||||||
colorPickerColorString,
|
const brushColorString = useAppSelector((s) =>
|
||||||
colorPickerInnerRadius,
|
rgbaColorToString(s.canvas.brushColor)
|
||||||
colorPickerOuterRadius,
|
);
|
||||||
clip,
|
const colorPickerColorString = useAppSelector((s) =>
|
||||||
stageDimensions,
|
rgbaColorToString(s.canvas.colorPickerColor)
|
||||||
} = useAppSelector(canvasBrushPreviewSelector);
|
);
|
||||||
|
const colorPickerInnerRadius = useAppSelector(
|
||||||
|
(s) =>
|
||||||
|
(COLOR_PICKER_SIZE - COLOR_PICKER_STROKE_RADIUS + 1) / s.canvas.stageScale
|
||||||
|
);
|
||||||
|
const colorPickerOuterRadius = useAppSelector(
|
||||||
|
(s) => COLOR_PICKER_SIZE / s.canvas.stageScale
|
||||||
|
);
|
||||||
|
const { clip, stageDimensions } = useAppSelector(canvasBrushPreviewSelector);
|
||||||
|
|
||||||
const cursorPosition = useStore($cursorPosition);
|
const cursorPosition = useStore($cursorPosition);
|
||||||
const isMovingBoundingBox = useStore($isMovingBoundingBox);
|
const isMovingBoundingBox = useStore($isMovingBoundingBox);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { $shift } from 'common/hooks/useGlobalModifiers';
|
import { $shift } from 'common/hooks/useGlobalModifiers';
|
||||||
import {
|
import {
|
||||||
@ -17,7 +16,6 @@ import {
|
|||||||
} from 'features/canvas/store/canvasNanostore';
|
} from 'features/canvas/store/canvasNanostore';
|
||||||
import {
|
import {
|
||||||
aspectRatioChanged,
|
aspectRatioChanged,
|
||||||
selectCanvasSlice,
|
|
||||||
setBoundingBoxCoordinates,
|
setBoundingBoxCoordinates,
|
||||||
setBoundingBoxDimensions,
|
setBoundingBoxDimensions,
|
||||||
setShouldSnapToGrid,
|
setShouldSnapToGrid,
|
||||||
@ -38,47 +36,23 @@ import { Group, Rect, Transformer } from 'react-konva';
|
|||||||
|
|
||||||
const borderDash = [4, 4];
|
const borderDash = [4, 4];
|
||||||
|
|
||||||
const boundingBoxPreviewSelector = createMemoizedSelector(
|
|
||||||
[selectCanvasSlice, selectOptimalDimension],
|
|
||||||
(canvas, optimalDimension) => {
|
|
||||||
const {
|
|
||||||
boundingBoxCoordinates,
|
|
||||||
boundingBoxDimensions,
|
|
||||||
stageScale,
|
|
||||||
tool,
|
|
||||||
shouldSnapToGrid,
|
|
||||||
aspectRatio,
|
|
||||||
} = canvas;
|
|
||||||
|
|
||||||
return {
|
|
||||||
boundingBoxCoordinates,
|
|
||||||
boundingBoxDimensions,
|
|
||||||
stageScale,
|
|
||||||
shouldSnapToGrid,
|
|
||||||
tool,
|
|
||||||
hitStrokeWidth: 20 / stageScale,
|
|
||||||
aspectRatio,
|
|
||||||
optimalDimension,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
type IAICanvasBoundingBoxPreviewProps = GroupConfig;
|
type IAICanvasBoundingBoxPreviewProps = GroupConfig;
|
||||||
|
|
||||||
const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
||||||
const { ...rest } = props;
|
const { ...rest } = props;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const {
|
const boundingBoxCoordinates = useAppSelector(
|
||||||
boundingBoxCoordinates,
|
(s) => s.canvas.boundingBoxCoordinates
|
||||||
boundingBoxDimensions,
|
);
|
||||||
stageScale,
|
const boundingBoxDimensions = useAppSelector(
|
||||||
shouldSnapToGrid,
|
(s) => s.canvas.boundingBoxDimensions
|
||||||
tool,
|
);
|
||||||
hitStrokeWidth,
|
const stageScale = useAppSelector((s) => s.canvas.stageScale);
|
||||||
aspectRatio,
|
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
|
||||||
optimalDimension,
|
const tool = useAppSelector((s) => s.canvas.tool);
|
||||||
} = useAppSelector(boundingBoxPreviewSelector);
|
const hitStrokeWidth = useAppSelector((s) => 20 / s.canvas.stageScale);
|
||||||
|
const aspectRatio = useAppSelector((s) => s.canvas.aspectRatio);
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
const transformerRef = useRef<Konva.Transformer>(null);
|
const transformerRef = useRef<Konva.Transformer>(null);
|
||||||
const shapeRef = useRef<Konva.Rect>(null);
|
const shapeRef = useRef<Konva.Rect>(null);
|
||||||
const shift = useStore($shift);
|
const shift = useStore($shift);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||||
@ -16,13 +15,11 @@ import { canvasMaskSavedToGallery } from 'features/canvas/store/actions';
|
|||||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import {
|
import {
|
||||||
clearMask,
|
clearMask,
|
||||||
selectCanvasSlice,
|
|
||||||
setIsMaskEnabled,
|
setIsMaskEnabled,
|
||||||
setLayer,
|
setLayer,
|
||||||
setMaskColor,
|
setMaskColor,
|
||||||
setShouldPreserveMaskedArea,
|
setShouldPreserveMaskedArea,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
|
||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import type { RgbaColor } from 'react-colorful';
|
import type { RgbaColor } from 'react-colorful';
|
||||||
@ -30,33 +27,16 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaMask, FaSave, FaTrash } from 'react-icons/fa';
|
import { FaMask, FaSave, FaTrash } from 'react-icons/fa';
|
||||||
|
|
||||||
export const selector = createMemoizedSelector(
|
|
||||||
[selectCanvasSlice, isStagingSelector],
|
|
||||||
(canvas, isStaging) => {
|
|
||||||
const { maskColor, layer, isMaskEnabled, shouldPreserveMaskedArea } =
|
|
||||||
canvas;
|
|
||||||
|
|
||||||
return {
|
|
||||||
layer,
|
|
||||||
maskColor,
|
|
||||||
maskColorString: rgbaColorToString(maskColor),
|
|
||||||
isMaskEnabled,
|
|
||||||
shouldPreserveMaskedArea,
|
|
||||||
isStaging,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const IAICanvasMaskOptions = () => {
|
const IAICanvasMaskOptions = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const layer = useAppSelector((s) => s.canvas.layer);
|
||||||
const {
|
const maskColor = useAppSelector((s) => s.canvas.maskColor);
|
||||||
layer,
|
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||||
maskColor,
|
const shouldPreserveMaskedArea = useAppSelector(
|
||||||
isMaskEnabled,
|
(s) => s.canvas.shouldPreserveMaskedArea
|
||||||
shouldPreserveMaskedArea,
|
);
|
||||||
isStaging,
|
const isStaging = useAppSelector(isStagingSelector);
|
||||||
} = useAppSelector(selector);
|
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
['q'],
|
['q'],
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InvCheckbox } from 'common/components/InvCheckbox/wrapper';
|
import { InvCheckbox } from 'common/components/InvCheckbox/wrapper';
|
||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
@ -10,7 +9,6 @@ import {
|
|||||||
} from 'common/components/InvPopover/wrapper';
|
} from 'common/components/InvPopover/wrapper';
|
||||||
import ClearCanvasHistoryButtonModal from 'features/canvas/components/ClearCanvasHistoryButtonModal';
|
import ClearCanvasHistoryButtonModal from 'features/canvas/components/ClearCanvasHistoryButtonModal';
|
||||||
import {
|
import {
|
||||||
selectCanvasSlice,
|
|
||||||
setShouldAntialias,
|
setShouldAntialias,
|
||||||
setShouldAutoSave,
|
setShouldAutoSave,
|
||||||
setShouldCropToBoundingBoxOnSave,
|
setShouldCropToBoundingBoxOnSave,
|
||||||
@ -28,50 +26,28 @@ import { useHotkeys } from 'react-hotkeys-hook';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaWrench } from 'react-icons/fa';
|
import { FaWrench } from 'react-icons/fa';
|
||||||
|
|
||||||
export const canvasControlsSelector = createMemoizedSelector(
|
|
||||||
[selectCanvasSlice],
|
|
||||||
(canvas) => {
|
|
||||||
const {
|
|
||||||
shouldAutoSave,
|
|
||||||
shouldCropToBoundingBoxOnSave,
|
|
||||||
shouldDarkenOutsideBoundingBox,
|
|
||||||
shouldShowCanvasDebugInfo,
|
|
||||||
shouldShowGrid,
|
|
||||||
shouldShowIntermediates,
|
|
||||||
shouldSnapToGrid,
|
|
||||||
shouldRestrictStrokesToBox,
|
|
||||||
shouldAntialias,
|
|
||||||
} = canvas;
|
|
||||||
|
|
||||||
return {
|
|
||||||
shouldAutoSave,
|
|
||||||
shouldCropToBoundingBoxOnSave,
|
|
||||||
shouldDarkenOutsideBoundingBox,
|
|
||||||
shouldShowCanvasDebugInfo,
|
|
||||||
shouldShowGrid,
|
|
||||||
shouldShowIntermediates,
|
|
||||||
shouldSnapToGrid,
|
|
||||||
shouldRestrictStrokesToBox,
|
|
||||||
shouldAntialias,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const IAICanvasSettingsButtonPopover = () => {
|
const IAICanvasSettingsButtonPopover = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const shouldAutoSave = useAppSelector((s) => s.canvas.shouldAutoSave);
|
||||||
const {
|
const shouldCropToBoundingBoxOnSave = useAppSelector(
|
||||||
shouldAutoSave,
|
(s) => s.canvas.shouldCropToBoundingBoxOnSave
|
||||||
shouldCropToBoundingBoxOnSave,
|
);
|
||||||
shouldDarkenOutsideBoundingBox,
|
const shouldDarkenOutsideBoundingBox = useAppSelector(
|
||||||
shouldShowCanvasDebugInfo,
|
(s) => s.canvas.shouldDarkenOutsideBoundingBox
|
||||||
shouldShowGrid,
|
);
|
||||||
shouldShowIntermediates,
|
const shouldShowCanvasDebugInfo = useAppSelector(
|
||||||
shouldSnapToGrid,
|
(s) => s.canvas.shouldShowCanvasDebugInfo
|
||||||
shouldRestrictStrokesToBox,
|
);
|
||||||
shouldAntialias,
|
const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid);
|
||||||
} = useAppSelector(canvasControlsSelector);
|
const shouldShowIntermediates = useAppSelector(
|
||||||
|
(s) => s.canvas.shouldShowIntermediates
|
||||||
|
);
|
||||||
|
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
|
||||||
|
const shouldRestrictStrokesToBox = useAppSelector(
|
||||||
|
(s) => s.canvas.shouldRestrictStrokesToBox
|
||||||
|
);
|
||||||
|
const shouldAntialias = useAppSelector((s) => s.canvas.shouldAntialias);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
['n'],
|
['n'],
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
|
import { InvButtonGroup } from 'common/components/InvButtonGroup/InvButtonGroup';
|
||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
@ -19,7 +18,6 @@ import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
|||||||
import {
|
import {
|
||||||
resetCanvas,
|
resetCanvas,
|
||||||
resetCanvasView,
|
resetCanvasView,
|
||||||
selectCanvasSlice,
|
|
||||||
setIsMaskEnabled,
|
setIsMaskEnabled,
|
||||||
setLayer,
|
setLayer,
|
||||||
setTool,
|
setTool,
|
||||||
@ -48,27 +46,13 @@ import IAICanvasSettingsButtonPopover from './IAICanvasSettingsButtonPopover';
|
|||||||
import IAICanvasToolChooserOptions from './IAICanvasToolChooserOptions';
|
import IAICanvasToolChooserOptions from './IAICanvasToolChooserOptions';
|
||||||
import IAICanvasUndoButton from './IAICanvasUndoButton';
|
import IAICanvasUndoButton from './IAICanvasUndoButton';
|
||||||
|
|
||||||
export const selector = createMemoizedSelector(
|
|
||||||
[selectCanvasSlice, isStagingSelector],
|
|
||||||
(canvas, isStaging) => {
|
|
||||||
const { tool, shouldCropToBoundingBoxOnSave, layer, isMaskEnabled } =
|
|
||||||
canvas;
|
|
||||||
|
|
||||||
return {
|
|
||||||
isStaging,
|
|
||||||
isMaskEnabled,
|
|
||||||
tool,
|
|
||||||
layer,
|
|
||||||
shouldCropToBoundingBoxOnSave,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const IAICanvasToolbar = () => {
|
const IAICanvasToolbar = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { isStaging, isMaskEnabled, layer, tool } = useAppSelector(selector);
|
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||||
|
const layer = useAppSelector((s) => s.canvas.layer);
|
||||||
|
const tool = useAppSelector((s) => s.canvas.tool);
|
||||||
|
const isStaging = useAppSelector(isStagingSelector);
|
||||||
const canvasBaseLayer = getCanvasBaseLayer();
|
const canvasBaseLayer = getCanvasBaseLayer();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { isClipboardAPIAvailable } = useCopyImageToClipboard();
|
const { isClipboardAPIAvailable } = useCopyImageToClipboard();
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
resetCanvasInteractionState,
|
resetCanvasInteractionState,
|
||||||
@ -7,7 +6,6 @@ import {
|
|||||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import {
|
import {
|
||||||
clearMask,
|
clearMask,
|
||||||
selectCanvasSlice,
|
|
||||||
setIsMaskEnabled,
|
setIsMaskEnabled,
|
||||||
setShouldShowBoundingBox,
|
setShouldShowBoundingBox,
|
||||||
setShouldSnapToGrid,
|
setShouldSnapToGrid,
|
||||||
@ -16,49 +14,24 @@ import {
|
|||||||
import type { CanvasTool } from 'features/canvas/store/canvasTypes';
|
import type { CanvasTool } from 'features/canvas/store/canvasTypes';
|
||||||
import { getCanvasStage } from 'features/canvas/util/konvaInstanceProvider';
|
import { getCanvasStage } from 'features/canvas/util/konvaInstanceProvider';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { useRef } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
|
||||||
[selectCanvasSlice, activeTabNameSelector, isStagingSelector],
|
|
||||||
(canvas, activeTabName, isStaging) => {
|
|
||||||
const {
|
|
||||||
shouldLockBoundingBox,
|
|
||||||
shouldShowBoundingBox,
|
|
||||||
tool,
|
|
||||||
isMaskEnabled,
|
|
||||||
shouldSnapToGrid,
|
|
||||||
} = canvas;
|
|
||||||
|
|
||||||
return {
|
|
||||||
activeTabName,
|
|
||||||
shouldLockBoundingBox,
|
|
||||||
shouldShowBoundingBox,
|
|
||||||
tool,
|
|
||||||
isStaging,
|
|
||||||
isMaskEnabled,
|
|
||||||
shouldSnapToGrid,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const useInpaintingCanvasHotkeys = () => {
|
const useInpaintingCanvasHotkeys = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const {
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
activeTabName,
|
const shouldShowBoundingBox = useAppSelector(
|
||||||
shouldShowBoundingBox,
|
(s) => s.canvas.shouldShowBoundingBox
|
||||||
tool,
|
);
|
||||||
isStaging,
|
const tool = useAppSelector((s) => s.canvas.tool);
|
||||||
isMaskEnabled,
|
const isStaging = useAppSelector(isStagingSelector);
|
||||||
shouldSnapToGrid,
|
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
|
||||||
} = useAppSelector(selector);
|
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
|
||||||
|
|
||||||
const previousToolRef = useRef<CanvasTool | null>(null);
|
const previousToolRef = useRef<CanvasTool | null>(null);
|
||||||
|
|
||||||
const canvasStage = getCanvasStage();
|
const canvasStage = getCanvasStage();
|
||||||
|
|
||||||
// Beta Keys
|
// Beta Keys
|
||||||
const handleClearMask = () => dispatch(clearMask());
|
const handleClearMask = useCallback(() => dispatch(clearMask()), [dispatch]);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
['shift+c'],
|
['shift+c'],
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
$isDrawing,
|
$isDrawing,
|
||||||
@ -8,10 +7,8 @@ import {
|
|||||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import {
|
import {
|
||||||
addPointToCurrentLine,
|
addPointToCurrentLine,
|
||||||
selectCanvasSlice,
|
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
|
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
import type { Vector2d } from 'konva/lib/types';
|
import type { Vector2d } from 'konva/lib/types';
|
||||||
import type { MutableRefObject } from 'react';
|
import type { MutableRefObject } from 'react';
|
||||||
@ -19,17 +16,6 @@ import { useCallback } from 'react';
|
|||||||
|
|
||||||
import useColorPicker from './useColorUnderCursor';
|
import useColorPicker from './useColorUnderCursor';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
|
||||||
[activeTabNameSelector, selectCanvasSlice, isStagingSelector],
|
|
||||||
(activeTabName, canvas, isStaging) => {
|
|
||||||
return {
|
|
||||||
tool: canvas.tool,
|
|
||||||
activeTabName,
|
|
||||||
isStaging,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const useCanvasMouseMove = (
|
const useCanvasMouseMove = (
|
||||||
stageRef: MutableRefObject<Konva.Stage | null>,
|
stageRef: MutableRefObject<Konva.Stage | null>,
|
||||||
didMouseMoveRef: MutableRefObject<boolean>,
|
didMouseMoveRef: MutableRefObject<boolean>,
|
||||||
@ -37,7 +23,8 @@ const useCanvasMouseMove = (
|
|||||||
) => {
|
) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isDrawing = useStore($isDrawing);
|
const isDrawing = useStore($isDrawing);
|
||||||
const { tool, isStaging } = useAppSelector(selector);
|
const tool = useAppSelector((s) => s.canvas.tool);
|
||||||
|
const isStaging = useAppSelector(isStagingSelector);
|
||||||
const { updateColorUnderCursor } = useColorPicker();
|
const { updateColorUnderCursor } = useColorPicker();
|
||||||
|
|
||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
export const getColoredMaskSVG = (color: string) => {
|
||||||
|
return `data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="60px" height="60px" viewBox="0 0 30 30" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,0)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,2.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,7.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,10)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,12.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,15)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,17.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,20)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,22.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,25)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,27.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,30)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-2.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-7.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-10)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-12.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-15)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-17.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-20)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-22.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-25)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-27.5)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.5,0,0,0.5,0,-30)">
|
||||||
|
<path d="M-3.5,63.5L64,-4" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||||
|
</g>
|
||||||
|
</svg>`.replaceAll('black', color);
|
||||||
|
};
|
@ -1,11 +1,11 @@
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { selectConfigSlice } from 'features/system/store/configSlice';
|
import { selectConfigSlice } from 'features/system/store/configSlice';
|
||||||
import { selectUiSlice } from 'features/ui/store/uiSlice';
|
import { selectUiSlice } from 'features/ui/store/uiSlice';
|
||||||
import { isString } from 'lodash-es';
|
import { isString } from 'lodash-es';
|
||||||
|
|
||||||
import { tabMap } from './tabMap';
|
import { tabMap } from './tabMap';
|
||||||
|
|
||||||
export const activeTabNameSelector = createMemoizedSelector(
|
export const activeTabNameSelector = createSelector(
|
||||||
selectUiSlice,
|
selectUiSlice,
|
||||||
/**
|
/**
|
||||||
* Previously `activeTab` was an integer, but now it's a string.
|
* Previously `activeTab` was an integer, but now it's a string.
|
||||||
@ -14,7 +14,7 @@ export const activeTabNameSelector = createMemoizedSelector(
|
|||||||
(ui) => (isString(ui.activeTab) ? ui.activeTab : 'txt2img')
|
(ui) => (isString(ui.activeTab) ? ui.activeTab : 'txt2img')
|
||||||
);
|
);
|
||||||
|
|
||||||
export const activeTabIndexSelector = createMemoizedSelector(
|
export const activeTabIndexSelector = createSelector(
|
||||||
selectUiSlice,
|
selectUiSlice,
|
||||||
selectConfigSlice,
|
selectConfigSlice,
|
||||||
(ui, config) => {
|
(ui, config) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user