chore(ui): format

Lots of changed bc the line length is now 120. May as well do it now.
This commit is contained in:
psychedelicious
2024-01-27 20:55:55 +11:00
parent b922ee566a
commit 189c430e46
568 changed files with 3602 additions and 11172 deletions

View File

@ -1,8 +1,4 @@
import {
Button,
ConfirmationAlertDialog,
useDisclosure,
} from '@invoke-ai/ui-library';
import { Button, ConfirmationAlertDialog, useDisclosure } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { clearCanvasHistory } from 'features/canvas/store/canvasSlice';
@ -15,19 +11,11 @@ const ClearCanvasHistoryButtonModal = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure();
const acceptCallback = useCallback(
() => dispatch(clearCanvasHistory()),
[dispatch]
);
const acceptCallback = useCallback(() => dispatch(clearCanvasHistory()), [dispatch]);
return (
<>
<Button
onClick={onOpen}
size="sm"
leftIcon={<PiTrashSimpleFill />}
isDisabled={isStaging}
>
<Button onClick={onOpen} size="sm" leftIcon={<PiTrashSimpleFill />} isDisabled={isStaging}>
{t('unifiedCanvas.clearCanvasHistory')}
</Button>
<ConfirmationAlertDialog

View File

@ -19,10 +19,7 @@ import {
$tool,
} from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
canvasResized,
selectCanvasSlice,
} from 'features/canvas/store/canvasSlice';
import { canvasResized, selectCanvasSlice } from 'features/canvas/store/canvasSlice';
import type Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import type { Vector2d } from 'konva/lib/types';
@ -55,18 +52,12 @@ const ChakraStage = chakra(Stage, {
const IAICanvas = () => {
const isStaging = useAppSelector(isStagingSelector);
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
const shouldShowBoundingBox = useAppSelector(
(s) => s.canvas.shouldShowBoundingBox
);
const shouldShowBoundingBox = useAppSelector((s) => s.canvas.shouldShowBoundingBox);
const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid);
const stageScale = useAppSelector((s) => s.canvas.stageScale);
const shouldShowIntermediates = useAppSelector(
(s) => s.canvas.shouldShowIntermediates
);
const shouldShowIntermediates = useAppSelector((s) => s.canvas.shouldShowIntermediates);
const shouldAntialias = useAppSelector((s) => s.canvas.shouldAntialias);
const shouldRestrictStrokesToBox = useAppSelector(
(s) => s.canvas.shouldRestrictStrokesToBox
);
const shouldRestrictStrokesToBox = useAppSelector((s) => s.canvas.shouldRestrictStrokesToBox);
const { stageCoordinates, stageDimensions } = useAppSelector(selector);
const dispatch = useAppDispatch();
const containerRef = useRef<HTMLDivElement>(null);
@ -95,22 +86,12 @@ const IAICanvas = () => {
return 'default';
}
return 'none';
}, [
isMouseOverBoundingBox,
isMovingStage,
isStaging,
isTransformingBoundingBox,
shouldRestrictStrokesToBox,
tool,
]);
}, [isMouseOverBoundingBox, isMovingStage, isStaging, isTransformingBoundingBox, shouldRestrictStrokesToBox, tool]);
const canvasBaseLayerRefCallback = useCallback(
(layerElement: Konva.Layer) => {
$canvasBaseLayer.set(layerElement);
canvasBaseLayerRef.current = layerElement;
},
[]
);
const canvasBaseLayerRefCallback = useCallback((layerElement: Konva.Layer) => {
$canvasBaseLayer.set(layerElement);
canvasBaseLayerRef.current = layerElement;
}, []);
const lastCursorPositionRef = useRef<Vector2d>({ x: 0, y: 0 });
@ -120,18 +101,10 @@ const IAICanvas = () => {
const handleWheel = useCanvasWheel(stageRef);
const handleMouseDown = useCanvasMouseDown(stageRef);
const handleMouseUp = useCanvasMouseUp(stageRef, didMouseMoveRef);
const handleMouseMove = useCanvasMouseMove(
stageRef,
didMouseMoveRef,
lastCursorPositionRef
);
const { handleDragStart, handleDragMove, handleDragEnd } =
useCanvasDragMove();
const handleMouseMove = useCanvasMouseMove(stageRef, didMouseMoveRef, lastCursorPositionRef);
const { handleDragStart, handleDragMove, handleDragEnd } = useCanvasDragMove();
const handleMouseOut = useCanvasMouseOut();
const handleContextMenu = useCallback(
(e: KonvaEventObject<MouseEvent>) => e.evt.preventDefault(),
[]
);
const handleContextMenu = useCallback((e: KonvaEventObject<MouseEvent>) => e.evt.preventDefault(), []);
useEffect(() => {
if (!containerRef.current) {
@ -169,14 +142,7 @@ const IAICanvas = () => {
const scale = useMemo(() => ({ x: stageScale, y: stageScale }), [stageScale]);
return (
<Flex
id="canvas-container"
ref={containerRef}
position="relative"
height="100%"
width="100%"
borderRadius="base"
>
<Flex id="canvas-container" ref={containerRef} position="relative" height="100%" width="100%" borderRadius="base">
<Box position="absolute">
<ChakraStage
tabIndex={-1}
@ -205,19 +171,10 @@ const IAICanvas = () => {
<IAICanvasGrid />
</Layer>
<Layer
id="base"
ref={canvasBaseLayerRefCallback}
listening={false}
imageSmoothingEnabled={shouldAntialias}
>
<Layer id="base" ref={canvasBaseLayerRefCallback} listening={false} imageSmoothingEnabled={shouldAntialias}>
<IAICanvasObjectRenderer />
</Layer>
<Layer
id="mask"
visible={isMaskEnabled && !isStaging}
listening={false}
>
<Layer id="mask" visible={isMaskEnabled && !isStaging} listening={false}>
<IAICanvasMaskLines visible={true} listening={false} />
<IAICanvasMaskCompositer listening={false} />
</Layer>
@ -225,17 +182,10 @@ const IAICanvas = () => {
<IAICanvasBoundingBoxOverlay />
</Layer>
<Layer id="preview" imageSmoothingEnabled={shouldAntialias}>
{!isStaging && (
<IAICanvasToolPreview
visible={tool !== 'move'}
listening={false}
/>
)}
{!isStaging && <IAICanvasToolPreview visible={tool !== 'move'} listening={false} />}
<IAICanvasStagingArea listening={false} visible={isStaging} />
{shouldShowIntermediates && <IAICanvasIntermediateImage />}
<IAICanvasBoundingBox
visible={shouldShowBoundingBox && !isStaging}
/>
<IAICanvasBoundingBox visible={shouldShowBoundingBox && !isStaging} />
</Layer>
</ChakraStage>
</Box>

View File

@ -5,12 +5,7 @@ import { memo } from 'react';
import { Group, Rect } from 'react-konva';
const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
const {
boundingBoxCoordinates,
boundingBoxDimensions,
stageDimensions,
stageCoordinates,
} = canvas;
const { boundingBoxCoordinates, boundingBoxDimensions, stageDimensions, stageCoordinates } = canvas;
return {
boundingBoxCoordinates,
@ -21,15 +16,8 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
});
const IAICanvasBoundingBoxOverlay = () => {
const {
boundingBoxCoordinates,
boundingBoxDimensions,
stageCoordinates,
stageDimensions,
} = useAppSelector(selector);
const shouldDarkenOutsideBoundingBox = useAppSelector(
(s) => s.canvas.shouldDarkenOutsideBoundingBox
);
const { boundingBoxCoordinates, boundingBoxDimensions, stageCoordinates, stageDimensions } = useAppSelector(selector);
const shouldDarkenOutsideBoundingBox = useAppSelector((s) => s.canvas.shouldDarkenOutsideBoundingBox);
const stageScale = useAppSelector((s) => s.canvas.stageScale);
return (

View File

@ -13,13 +13,8 @@ type IAICanvasImageProps = {
};
const IAICanvasImage = (props: IAICanvasImageProps) => {
const { x, y, imageName } = props.canvasImage;
const { currentData: imageDTO, isError } = useGetImageDTOQuery(
imageName ?? skipToken
);
const [image, status] = useImage(
imageDTO?.image_url ?? '',
$authToken.get() ? 'use-credentials' : 'anonymous'
);
const { currentData: imageDTO, isError } = useGetImageDTOQuery(imageName ?? skipToken);
const [image, status] = useImage(imageDTO?.image_url ?? '', $authToken.get() ? 'use-credentials' : 'anonymous');
if (isError || status === 'failed') {
return <IAICanvasImageErrorFallback canvasImage={props.canvasImage} />;

View File

@ -7,9 +7,7 @@ import { Group, Rect, Text } from 'react-konva';
type IAICanvasImageErrorFallbackProps = {
canvasImage: CanvasImage;
};
const IAICanvasImageErrorFallback = ({
canvasImage,
}: IAICanvasImageErrorFallbackProps) => {
const IAICanvasImageErrorFallback = ({ canvasImage }: IAICanvasImageErrorFallbackProps) => {
const [rectFill, textFill] = useToken('colors', ['base.500', 'base.900']);
const { t } = useTranslation();
return (

View File

@ -5,26 +5,20 @@ import { selectSystemSlice } from 'features/system/store/systemSlice';
import { memo, useEffect, useState } from 'react';
import { Image as KonvaImage } from 'react-konva';
const progressImageSelector = createMemoizedSelector(
[selectSystemSlice, selectCanvasSlice],
(system, canvas) => {
const { denoiseProgress } = system;
const { batchIds } = canvas;
const progressImageSelector = createMemoizedSelector([selectSystemSlice, selectCanvasSlice], (system, canvas) => {
const { denoiseProgress } = system;
const { batchIds } = canvas;
return {
progressImage:
denoiseProgress && batchIds.includes(denoiseProgress.batch_id)
? denoiseProgress.progress_image
: undefined,
boundingBox: canvas.layerState.stagingArea.boundingBox,
};
}
);
return {
progressImage:
denoiseProgress && batchIds.includes(denoiseProgress.batch_id) ? denoiseProgress.progress_image : undefined,
boundingBox: canvas.layerState.stagingArea.boundingBox,
};
});
const IAICanvasIntermediateImage = () => {
const { progressImage, boundingBox } = useAppSelector(progressImageSelector);
const [loadedImageElement, setLoadedImageElement] =
useState<HTMLImageElement | null>(null);
const [loadedImageElement, setLoadedImageElement] = useState<HTMLImageElement | null>(null);
useEffect(() => {
if (!progressImage) {

View File

@ -9,30 +9,22 @@ import { isNumber } from 'lodash-es';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Rect } from 'react-konva';
export const canvasMaskCompositerSelector = createMemoizedSelector(
selectCanvasSlice,
(canvas) => {
return {
stageCoordinates: canvas.stageCoordinates,
stageDimensions: canvas.stageDimensions,
};
}
);
export const canvasMaskCompositerSelector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
return {
stageCoordinates: canvas.stageCoordinates,
stageDimensions: canvas.stageDimensions,
};
});
type IAICanvasMaskCompositerProps = RectConfig;
const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
const { ...rest } = props;
const { stageCoordinates, stageDimensions } = useAppSelector(
canvasMaskCompositerSelector
);
const { stageCoordinates, stageDimensions } = useAppSelector(canvasMaskCompositerSelector);
const stageScale = useAppSelector((s) => s.canvas.stageScale);
const maskColorString = useAppSelector((s) =>
rgbaColorToString(s.canvas.maskColor)
);
const [fillPatternImage, setFillPatternImage] =
useState<HTMLImageElement | null>(null);
const maskColorString = useAppSelector((s) => rgbaColorToString(s.canvas.maskColor));
const [fillPatternImage, setFillPatternImage] = useState<HTMLImageElement | null>(null);
const [offset, setOffset] = useState<number>(0);
@ -66,10 +58,7 @@ const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
return () => clearInterval(timer);
}, []);
const fillPatternScale = useMemo(
() => ({ x: 1 / stageScale, y: 1 / stageScale }),
[stageScale]
);
const fillPatternScale = useMemo(() => ({ x: 1 / stageScale, y: 1 / stageScale }), [stageScale]);
if (
!fillPatternImage ||

View File

@ -27,9 +27,7 @@ const IAICanvasLines = (props: InpaintingCanvasLinesProps) => {
lineJoin="round"
shadowForStrokeEnabled={false}
listening={false}
globalCompositeOperation={
line.tool === 'brush' ? 'source-over' : 'destination-out'
}
globalCompositeOperation={line.tool === 'brush' ? 'source-over' : 'destination-out'}
/>
))}
</Group>

View File

@ -31,9 +31,7 @@ const IAICanvasObjectRenderer = () => {
lineJoin="round"
shadowForStrokeEnabled={false}
listening={false}
globalCompositeOperation={
obj.tool === 'brush' ? 'source-over' : 'destination-out'
}
globalCompositeOperation={obj.tool === 'brush' ? 'source-over' : 'destination-out'}
/>
);
if (obj.clip) {

View File

@ -22,9 +22,7 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
return {
currentStagingAreaImage:
images.length > 0 && selectedImageIndex !== undefined
? images[selectedImageIndex]
: undefined,
images.length > 0 && selectedImageIndex !== undefined ? images[selectedImageIndex] : undefined,
isOnFirstImage: selectedImageIndex === 0,
isOnLastImage: selectedImageIndex === images.length - 1,
shouldShowStagingImage,
@ -39,21 +37,12 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
type Props = GroupConfig;
const IAICanvasStagingArea = (props: Props) => {
const {
currentStagingAreaImage,
shouldShowStagingImage,
shouldShowStagingOutline,
x,
y,
width,
height,
} = useAppSelector(selector);
const { currentStagingAreaImage, shouldShowStagingImage, shouldShowStagingOutline, x, y, width, height } =
useAppSelector(selector);
return (
<Group {...props}>
{shouldShowStagingImage && currentStagingAreaImage && (
<IAICanvasImage canvasImage={currentStagingAreaImage} />
)}
{shouldShowStagingImage && currentStagingAreaImage && <IAICanvasImage canvasImage={currentStagingAreaImage} />}
{shouldShowStagingOutline && (
<Group listening={false}>
<Rect

View File

@ -38,8 +38,7 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
return {
currentIndex: selectedImageIndex,
total: images.length,
currentStagingAreaImage:
images.length > 0 ? images[selectedImageIndex] : undefined,
currentStagingAreaImage: images.length > 0 ? images[selectedImageIndex] : undefined,
shouldShowStagingImage,
shouldShowStagingOutline,
};
@ -47,12 +46,7 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
const IAICanvasStagingAreaToolbar = () => {
const dispatch = useAppDispatch();
const {
currentStagingAreaImage,
shouldShowStagingImage,
currentIndex,
total,
} = useAppSelector(selector);
const { currentStagingAreaImage, shouldShowStagingImage, currentIndex, total } = useAppSelector(selector);
const { t } = useTranslation();
@ -64,20 +58,11 @@ const IAICanvasStagingAreaToolbar = () => {
dispatch(setShouldShowStagingOutline(false));
}, [dispatch]);
const handlePrevImage = useCallback(
() => dispatch(prevStagingAreaImage()),
[dispatch]
);
const handlePrevImage = useCallback(() => dispatch(prevStagingAreaImage()), [dispatch]);
const handleNextImage = useCallback(
() => dispatch(nextStagingAreaImage()),
[dispatch]
);
const handleNextImage = useCallback(() => dispatch(nextStagingAreaImage()), [dispatch]);
const handleAccept = useCallback(
() => dispatch(commitStagingAreaImage()),
[dispatch]
);
const handleAccept = useCallback(() => dispatch(commitStagingAreaImage()), [dispatch]);
useHotkeys(['left'], handlePrevImage, {
enabled: () => true,
@ -94,9 +79,7 @@ const IAICanvasStagingAreaToolbar = () => {
preventDefault: true,
});
const { data: imageDTO } = useGetImageDTOQuery(
currentStagingAreaImage?.imageName ?? skipToken
);
const { data: imageDTO } = useGetImageDTOQuery(currentStagingAreaImage?.imageName ?? skipToken);
const handleToggleShouldShowStagingImage = useCallback(() => {
dispatch(setShouldShowStagingImage(!shouldShowStagingImage));
@ -166,16 +149,8 @@ const IAICanvasStagingAreaToolbar = () => {
colorScheme="invokeBlue"
/>
<IconButton
tooltip={
shouldShowStagingImage
? t('unifiedCanvas.showResultsOn')
: t('unifiedCanvas.showResultsOff')
}
aria-label={
shouldShowStagingImage
? t('unifiedCanvas.showResultsOn')
: t('unifiedCanvas.showResultsOff')
}
tooltip={shouldShowStagingImage ? t('unifiedCanvas.showResultsOn') : t('unifiedCanvas.showResultsOff')}
aria-label={shouldShowStagingImage ? t('unifiedCanvas.showResultsOn') : t('unifiedCanvas.showResultsOff')}
data-alert={!shouldShowStagingImage}
icon={shouldShowStagingImage ? <PiEyeBold /> : <PiEyeSlashBold />}
onClick={handleToggleShouldShowStagingImage}

View File

@ -16,10 +16,7 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
stageDimensions: { width: stageWidth, height: stageHeight },
stageCoordinates: { x: stageX, y: stageY },
boundingBoxDimensions: { width: boxWidth, height: boxHeight },
scaledBoundingBoxDimensions: {
width: scaledBoxWidth,
height: scaledBoxHeight,
},
scaledBoundingBoxDimensions: { width: scaledBoxWidth, height: scaledBoxHeight },
boundingBoxCoordinates: { x: boxX, y: boxY },
stageScale,
shouldShowCanvasDebugInfo,
@ -31,10 +28,8 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
let boundingBoxColor = 'inherit';
if (
(boundingBoxScaleMethod === 'none' &&
(boxWidth < 512 || boxHeight < 512)) ||
(boundingBoxScaleMethod === 'manual' &&
scaledBoxWidth * scaledBoxHeight < 512 * 512)
(boundingBoxScaleMethod === 'none' && (boxWidth < 512 || boxHeight < 512)) ||
(boundingBoxScaleMethod === 'manual' && scaledBoxWidth * scaledBoxHeight < 512 * 512)
) {
boundingBoxColor = warningColor;
}
@ -45,14 +40,10 @@ const selector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
activeLayerColor,
layer,
boundingBoxColor,
boundingBoxCoordinatesString: `(${roundToHundreth(boxX)}, ${roundToHundreth(
boxY
)})`,
boundingBoxCoordinatesString: `(${roundToHundreth(boxX)}, ${roundToHundreth(boxY)})`,
boundingBoxDimensionsString: `${boxWidth}×${boxHeight}`,
scaledBoundingBoxDimensionsString: `${scaledBoxWidth}×${scaledBoxHeight}`,
canvasCoordinatesString: `${roundToHundreth(stageX)}×${roundToHundreth(
stageY
)}`,
canvasCoordinatesString: `${roundToHundreth(stageX)}×${roundToHundreth(stageY)}`,
canvasDimensionsString: `${stageWidth}×${stageHeight}`,
canvasScaleString: Math.round(stageScale * 100),
shouldShowCanvasDebugInfo,
@ -99,9 +90,7 @@ const IAICanvasStatusText = () => {
bg="base.800"
>
<GenerationModeStatusText />
<Box color={activeLayerColor}>{`${t('unifiedCanvas.activeLayer')}: ${t(
`unifiedCanvas.${layer}`
)}`}</Box>
<Box color={activeLayerColor}>{`${t('unifiedCanvas.activeLayer')}: ${t(`unifiedCanvas.${layer}`)}`}</Box>
<Box>{`${t('unifiedCanvas.canvasScale')}: ${canvasScaleString}%`}</Box>
{shouldPreserveMaskedArea && (
<Box color={warningColor}>
@ -109,9 +98,7 @@ const IAICanvasStatusText = () => {
</Box>
)}
{shouldShowBoundingBox && (
<Box color={boundingBoxColor}>{`${t(
'unifiedCanvas.boundingBox'
)}: ${boundingBoxDimensionsString}`}</Box>
<Box color={boundingBoxColor}>{`${t('unifiedCanvas.boundingBox')}: ${boundingBoxDimensionsString}`}</Box>
)}
{shouldShowScaledBoundingBox && (
<Box color={boundingBoxColor}>{`${t(
@ -120,15 +107,9 @@ const IAICanvasStatusText = () => {
)}
{shouldShowCanvasDebugInfo && (
<>
<Box>{`${t(
'unifiedCanvas.boundingBoxPosition'
)}: ${boundingBoxCoordinatesString}`}</Box>
<Box>{`${t(
'unifiedCanvas.canvasDimensions'
)}: ${canvasDimensionsString}`}</Box>
<Box>{`${t(
'unifiedCanvas.canvasPosition'
)}: ${canvasCoordinatesString}`}</Box>
<Box>{`${t('unifiedCanvas.boundingBoxPosition')}: ${boundingBoxCoordinatesString}`}</Box>
<Box>{`${t('unifiedCanvas.canvasDimensions')}: ${canvasDimensionsString}`}</Box>
<Box>{`${t('unifiedCanvas.canvasPosition')}: ${canvasCoordinatesString}`}</Box>
<IAICanvasStatusTextCursorPos />
</>
)}

View File

@ -14,11 +14,7 @@ const IAICanvasStatusTextCursorPos = () => {
return `(${roundToHundreth(x)}, ${roundToHundreth(y)})`;
}, [cursorPosition?.x, cursorPosition?.y]);
return (
<Box>{`${t(
'unifiedCanvas.cursorPosition'
)}: ${cursorCoordinatesString}`}</Box>
);
return <Box>{`${t('unifiedCanvas.cursorPosition')}: ${cursorCoordinatesString}`}</Box>;
};
export default memo(IAICanvasStatusTextCursorPos);

View File

@ -9,104 +9,84 @@ import {
} from 'features/canvas/store/canvasNanostore';
import { selectCanvasSlice } from 'features/canvas/store/canvasSlice';
import { rgbaColorToString } from 'features/canvas/util/colorToString';
import {
COLOR_PICKER_SIZE,
COLOR_PICKER_STROKE_RADIUS,
} from 'features/canvas/util/constants';
import { COLOR_PICKER_SIZE, COLOR_PICKER_STROKE_RADIUS } from 'features/canvas/util/constants';
import type { GroupConfig } from 'konva/lib/Group';
import { memo, useMemo } from 'react';
import { Circle, Group } from 'react-konva';
const canvasBrushPreviewSelector = createMemoizedSelector(
selectCanvasSlice,
(canvas) => {
const {
stageDimensions,
boundingBoxCoordinates,
boundingBoxDimensions,
shouldRestrictStrokesToBox,
} = canvas;
const canvasBrushPreviewSelector = createMemoizedSelector(selectCanvasSlice, (canvas) => {
const { stageDimensions, boundingBoxCoordinates, boundingBoxDimensions, shouldRestrictStrokesToBox } = canvas;
const clip = shouldRestrictStrokesToBox
? {
clipX: boundingBoxCoordinates.x,
clipY: boundingBoxCoordinates.y,
clipWidth: boundingBoxDimensions.width,
clipHeight: boundingBoxDimensions.height,
}
: {};
const clip = shouldRestrictStrokesToBox
? {
clipX: boundingBoxCoordinates.x,
clipY: boundingBoxCoordinates.y,
clipWidth: boundingBoxDimensions.width,
clipHeight: boundingBoxDimensions.height,
}
: {};
// // big brain time; this is the *inverse* of the clip that is needed for shouldRestrictStrokesToBox
// // it took some fiddling to work out, so I am leaving it here in case it is needed for something else...
// const clipFunc = shouldRestrictStrokesToBox
// ? (ctx: SceneContext) => {
// console.log(
// stageCoordinates.x / stageScale,
// stageCoordinates.y / stageScale,
// stageDimensions.height / stageScale,
// stageDimensions.width / stageScale
// );
// ctx.fillStyle = 'red';
// ctx.rect(
// -stageCoordinates.x / stageScale,
// -stageCoordinates.y / stageScale,
// stageDimensions.width / stageScale,
// stageCoordinates.y / stageScale + boundingBoxCoordinates.y
// );
// ctx.rect(
// -stageCoordinates.x / stageScale,
// boundingBoxCoordinates.y + boundingBoxDimensions.height,
// stageDimensions.width / stageScale,
// stageDimensions.height / stageScale
// );
// ctx.rect(
// -stageCoordinates.x / stageScale,
// -stageCoordinates.y / stageScale,
// stageCoordinates.x / stageScale + boundingBoxCoordinates.x,
// stageDimensions.height / stageScale
// );
// ctx.rect(
// boundingBoxCoordinates.x + boundingBoxDimensions.width,
// -stageCoordinates.y / stageScale,
// stageDimensions.width / stageScale -
// (boundingBoxCoordinates.x + boundingBoxDimensions.width),
// stageDimensions.height / stageScale
// );
// }
// : undefined;
// // big brain time; this is the *inverse* of the clip that is needed for shouldRestrictStrokesToBox
// // it took some fiddling to work out, so I am leaving it here in case it is needed for something else...
// const clipFunc = shouldRestrictStrokesToBox
// ? (ctx: SceneContext) => {
// console.log(
// stageCoordinates.x / stageScale,
// stageCoordinates.y / stageScale,
// stageDimensions.height / stageScale,
// stageDimensions.width / stageScale
// );
// ctx.fillStyle = 'red';
// ctx.rect(
// -stageCoordinates.x / stageScale,
// -stageCoordinates.y / stageScale,
// stageDimensions.width / stageScale,
// stageCoordinates.y / stageScale + boundingBoxCoordinates.y
// );
// ctx.rect(
// -stageCoordinates.x / stageScale,
// boundingBoxCoordinates.y + boundingBoxDimensions.height,
// stageDimensions.width / stageScale,
// stageDimensions.height / stageScale
// );
// ctx.rect(
// -stageCoordinates.x / stageScale,
// -stageCoordinates.y / stageScale,
// stageCoordinates.x / stageScale + boundingBoxCoordinates.x,
// stageDimensions.height / stageScale
// );
// ctx.rect(
// boundingBoxCoordinates.x + boundingBoxDimensions.width,
// -stageCoordinates.y / stageScale,
// stageDimensions.width / stageScale -
// (boundingBoxCoordinates.x + boundingBoxDimensions.width),
// stageDimensions.height / stageScale
// );
// }
// : undefined;
return {
clip,
stageDimensions,
};
}
);
return {
clip,
stageDimensions,
};
});
/**
* Draws a black circle around the canvas brush preview.
*/
const IAICanvasToolPreview = (props: GroupConfig) => {
const radius = useAppSelector((s) => s.canvas.brushSize / 2);
const maskColorString = useAppSelector((s) =>
rgbaColorToString({ ...s.canvas.maskColor, a: 0.5 })
);
const maskColorString = useAppSelector((s) => rgbaColorToString({ ...s.canvas.maskColor, a: 0.5 }));
const tool = useStore($tool);
const layer = useAppSelector((s) => s.canvas.layer);
const dotRadius = useAppSelector((s) => 1.5 / s.canvas.stageScale);
const strokeWidth = useAppSelector((s) => 1.5 / s.canvas.stageScale);
const brushColorString = useAppSelector((s) =>
rgbaColorToString(s.canvas.brushColor)
);
const colorPickerColorString = useAppSelector((s) =>
rgbaColorToString(s.canvas.colorPickerColor)
);
const brushColorString = useAppSelector((s) => rgbaColorToString(s.canvas.brushColor));
const colorPickerColorString = useAppSelector((s) => rgbaColorToString(s.canvas.colorPickerColor));
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
(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);
@ -123,8 +103,7 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
);
const shouldDrawBrushPreview = useMemo(
() =>
!(isMovingBoundingBox || isTransformingBoundingBox || !cursorPosition),
() => !(isMovingBoundingBox || isTransformingBoundingBox || !cursorPosition),
[cursorPosition, isMovingBoundingBox, isTransformingBoundingBox]
);
@ -162,9 +141,7 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
y={brushY}
radius={radius}
fill={layer === 'mask' ? maskColorString : brushColorString}
globalCompositeOperation={
tool === 'eraser' ? 'destination-out' : 'source-out'
}
globalCompositeOperation={tool === 'eraser' ? 'destination-out' : 'source-out'}
listening={false}
/>
<Circle
@ -187,20 +164,8 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
/>
</>
)}
<Circle
x={brushX}
y={brushY}
radius={dotRadius * 2}
fill="rgba(255,255,255,0.4)"
listening={false}
/>
<Circle
x={brushX}
y={brushY}
radius={dotRadius}
fill="rgba(0,0,0,1)"
listening={false}
/>
<Circle x={brushX} y={brushY} radius={dotRadius * 2} fill="rgba(255,255,255,0.4)" listening={false} />
<Circle x={brushX} y={brushY} radius={dotRadius} fill="rgba(0,0,0,1)" listening={false} />
</Group>
);
};

View File

@ -1,11 +1,7 @@
import { useShiftModifier } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
roundDownToMultiple,
roundDownToMultipleMin,
roundToMultiple,
} from 'common/util/roundDownToMultiple';
import { roundDownToMultiple, roundDownToMultipleMin, roundToMultiple } from 'common/util/roundDownToMultiple';
import {
$isDrawing,
$isMouseOverBoundingBox,
@ -20,10 +16,7 @@ import {
setBoundingBoxDimensions,
setShouldSnapToGrid,
} from 'features/canvas/store/canvasSlice';
import {
CANVAS_GRID_SIZE_COARSE,
CANVAS_GRID_SIZE_FINE,
} from 'features/canvas/store/constants';
import { CANVAS_GRID_SIZE_COARSE, CANVAS_GRID_SIZE_FINE } from 'features/canvas/store/constants';
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import type Konva from 'konva';
@ -41,12 +34,8 @@ type IAICanvasBoundingBoxPreviewProps = GroupConfig;
const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
const { ...rest } = props;
const dispatch = useAppDispatch();
const boundingBoxCoordinates = useAppSelector(
(s) => s.canvas.boundingBoxCoordinates
);
const boundingBoxDimensions = useAppSelector(
(s) => s.canvas.boundingBoxDimensions
);
const boundingBoxCoordinates = useAppSelector((s) => s.canvas.boundingBoxCoordinates);
const boundingBoxDimensions = useAppSelector((s) => s.canvas.boundingBoxDimensions);
const stageScale = useAppSelector((s) => s.canvas.stageScale);
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
const hitStrokeWidth = useAppSelector((s) => 20 / s.canvas.stageScale);
@ -59,9 +48,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
const isDrawing = useStore($isDrawing);
const isMovingBoundingBox = useStore($isMovingBoundingBox);
const isTransformingBoundingBox = useStore($isTransformingBoundingBox);
const isMouseOverBoundingBoxOutline = useStore(
$isMouseOverBoundingBoxOutline
);
const isMouseOverBoundingBoxOutline = useStore($isMouseOverBoundingBoxOutline);
useEffect(() => {
if (!transformerRef.current || !shapeRef.current) {
@ -71,14 +58,8 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
transformerRef.current.getLayer()?.batchDraw();
}, []);
const gridSize = useMemo(
() => (shift ? CANVAS_GRID_SIZE_FINE : CANVAS_GRID_SIZE_COARSE),
[shift]
);
const scaledStep = useMemo(
() => gridSize * stageScale,
[gridSize, stageScale]
);
const gridSize = useMemo(() => (shift ? CANVAS_GRID_SIZE_FINE : CANVAS_GRID_SIZE_COARSE), [shift]);
const scaledStep = useMemo(() => gridSize * stageScale, [gridSize, stageScale]);
useHotkeys(
'N',
@ -143,10 +124,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
const y = Math.round(rect.y());
if (aspectRatio.isLocked) {
const newDimensions = calculateNewSize(
aspectRatio.value,
width * height
);
const newDimensions = calculateNewSize(aspectRatio.value, width * height);
dispatch(
setBoundingBoxDimensions(
{
@ -186,14 +164,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
rect.scaleX(1);
rect.scaleY(1);
},
[
aspectRatio.isLocked,
aspectRatio.value,
dispatch,
shouldSnapToGrid,
gridSize,
optimalDimension,
]
[aspectRatio.isLocked, aspectRatio.value, dispatch, shouldSnapToGrid, gridSize, optimalDimension]
);
const anchorDragBoundFunc = useCallback(
@ -255,9 +226,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
}, []);
const handleMouseOut = useCallback(() => {
!isTransformingBoundingBox &&
!isMovingBoundingBox &&
$isMouseOverBoundingBoxOutline.set(false);
!isTransformingBoundingBox && !isMovingBoundingBox && $isMouseOverBoundingBoxOutline.set(false);
}, [isMovingBoundingBox, isTransformingBoundingBox]);
const handleMouseEnterBoundingBox = useCallback(() => {
@ -269,35 +238,18 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
}, []);
const stroke = useMemo(() => {
if (
isMouseOverBoundingBoxOutline ||
isMovingBoundingBox ||
isTransformingBoundingBox
) {
if (isMouseOverBoundingBoxOutline || isMovingBoundingBox || isTransformingBoundingBox) {
return 'rgba(255,255,255,0.5)';
}
return 'white';
}, [
isMouseOverBoundingBoxOutline,
isMovingBoundingBox,
isTransformingBoundingBox,
]);
}, [isMouseOverBoundingBoxOutline, isMovingBoundingBox, isTransformingBoundingBox]);
const strokeWidth = useMemo(() => {
if (
isMouseOverBoundingBoxOutline ||
isMovingBoundingBox ||
isTransformingBoundingBox
) {
if (isMouseOverBoundingBoxOutline || isMovingBoundingBox || isTransformingBoundingBox) {
return 6 / stageScale;
}
return 1 / stageScale;
}, [
isMouseOverBoundingBoxOutline,
isMovingBoundingBox,
isTransformingBoundingBox,
stageScale,
]);
}, [isMouseOverBoundingBoxOutline, isMovingBoundingBox, isTransformingBoundingBox, stageScale]);
const enabledAnchors = useMemo(() => {
if (tool !== 'move') {

View File

@ -30,11 +30,7 @@ import { memo, useCallback } from 'react';
import type { RgbaColor } from 'react-colorful';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import {
PiExcludeBold,
PiFloppyDiskBackFill,
PiTrashSimpleFill,
} from 'react-icons/pi';
import { PiExcludeBold, PiFloppyDiskBackFill, PiTrashSimpleFill } from 'react-icons/pi';
const formLabelProps: FormLabelProps = {
flexGrow: 1,
@ -46,9 +42,7 @@ const IAICanvasMaskOptions = () => {
const layer = useAppSelector((s) => s.canvas.layer);
const maskColor = useAppSelector((s) => s.canvas.maskColor);
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
const shouldPreserveMaskedArea = useAppSelector(
(s) => s.canvas.shouldPreserveMaskedArea
);
const shouldPreserveMaskedArea = useAppSelector((s) => s.canvas.shouldPreserveMaskedArea);
const isStaging = useAppSelector(isStagingSelector);
useHotkeys(
@ -134,38 +128,21 @@ const IAICanvasMaskOptions = () => {
<FormControlGroup formLabelProps={formLabelProps}>
<FormControl>
<FormLabel>{`${t('unifiedCanvas.enableMask')} (H)`}</FormLabel>
<Checkbox
isChecked={isMaskEnabled}
onChange={handleToggleEnableMask}
/>
<Checkbox isChecked={isMaskEnabled} onChange={handleToggleEnableMask} />
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.preserveMaskedArea')}</FormLabel>
<Checkbox
isChecked={shouldPreserveMaskedArea}
onChange={handleChangePreserveMaskedArea}
/>
<Checkbox isChecked={shouldPreserveMaskedArea} onChange={handleChangePreserveMaskedArea} />
</FormControl>
</FormControlGroup>
<Box pt={2} pb={2}>
<IAIColorPicker
color={maskColor}
onChange={handleChangeMaskColor}
/>
<IAIColorPicker color={maskColor} onChange={handleChangeMaskColor} />
</Box>
<ButtonGroup isAttached={false}>
<Button
size="sm"
leftIcon={<PiFloppyDiskBackFill />}
onClick={handleSaveMask}
>
<Button size="sm" leftIcon={<PiFloppyDiskBackFill />} onClick={handleSaveMask}>
{t('unifiedCanvas.saveMask')}
</Button>
<Button
size="sm"
leftIcon={<PiTrashSimpleFill />}
onClick={handleClearMask}
>
<Button size="sm" leftIcon={<PiTrashSimpleFill />} onClick={handleClearMask}>
{t('unifiedCanvas.clearMask')}
</Button>
</ButtonGroup>

View File

@ -38,23 +38,13 @@ const IAICanvasSettingsButtonPopover = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const shouldAutoSave = useAppSelector((s) => s.canvas.shouldAutoSave);
const shouldCropToBoundingBoxOnSave = useAppSelector(
(s) => s.canvas.shouldCropToBoundingBoxOnSave
);
const shouldDarkenOutsideBoundingBox = useAppSelector(
(s) => s.canvas.shouldDarkenOutsideBoundingBox
);
const shouldShowCanvasDebugInfo = useAppSelector(
(s) => s.canvas.shouldShowCanvasDebugInfo
);
const shouldCropToBoundingBoxOnSave = useAppSelector((s) => s.canvas.shouldCropToBoundingBoxOnSave);
const shouldDarkenOutsideBoundingBox = useAppSelector((s) => s.canvas.shouldDarkenOutsideBoundingBox);
const shouldShowCanvasDebugInfo = useAppSelector((s) => s.canvas.shouldShowCanvasDebugInfo);
const shouldShowGrid = useAppSelector((s) => s.canvas.shouldShowGrid);
const shouldShowIntermediates = useAppSelector(
(s) => s.canvas.shouldShowIntermediates
);
const shouldShowIntermediates = useAppSelector((s) => s.canvas.shouldShowIntermediates);
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
const shouldRestrictStrokesToBox = useAppSelector(
(s) => s.canvas.shouldRestrictStrokesToBox
);
const shouldRestrictStrokesToBox = useAppSelector((s) => s.canvas.shouldRestrictStrokesToBox);
const shouldAntialias = useAppSelector((s) => s.canvas.shouldAntialias);
useHotkeys(
@ -70,49 +60,40 @@ const IAICanvasSettingsButtonPopover = () => {
);
const handleChangeShouldSnapToGrid = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldSnapToGrid(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldSnapToGrid(e.target.checked)),
[dispatch]
);
const handleChangeShouldShowIntermediates = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldShowIntermediates(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldShowIntermediates(e.target.checked)),
[dispatch]
);
const handleChangeShouldShowGrid = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldShowGrid(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldShowGrid(e.target.checked)),
[dispatch]
);
const handleChangeShouldDarkenOutsideBoundingBox = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked)),
[dispatch]
);
const handleChangeShouldAutoSave = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldAutoSave(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldAutoSave(e.target.checked)),
[dispatch]
);
const handleChangeShouldCropToBoundingBoxOnSave = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked)),
[dispatch]
);
const handleChangeShouldRestrictStrokesToBox = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldRestrictStrokesToBox(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldRestrictStrokesToBox(e.target.checked)),
[dispatch]
);
const handleChangeShouldShowCanvasDebugInfo = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldShowCanvasDebugInfo(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldShowCanvasDebugInfo(e.target.checked)),
[dispatch]
);
const handleChangeShouldAntialias = useCallback(
(e: ChangeEvent<HTMLInputElement>) =>
dispatch(setShouldAntialias(e.target.checked)),
(e: ChangeEvent<HTMLInputElement>) => dispatch(setShouldAntialias(e.target.checked)),
[dispatch]
);
@ -131,29 +112,18 @@ const IAICanvasSettingsButtonPopover = () => {
<FormControlGroup formLabelProps={formLabelProps}>
<FormControl>
<FormLabel>{t('unifiedCanvas.showIntermediates')}</FormLabel>
<Checkbox
isChecked={shouldShowIntermediates}
onChange={handleChangeShouldShowIntermediates}
/>
<Checkbox isChecked={shouldShowIntermediates} onChange={handleChangeShouldShowIntermediates} />
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.showGrid')}</FormLabel>
<Checkbox
isChecked={shouldShowGrid}
onChange={handleChangeShouldShowGrid}
/>
<Checkbox isChecked={shouldShowGrid} onChange={handleChangeShouldShowGrid} />
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.snapToGrid')}</FormLabel>
<Checkbox
isChecked={shouldSnapToGrid}
onChange={handleChangeShouldSnapToGrid}
/>
<Checkbox isChecked={shouldSnapToGrid} onChange={handleChangeShouldSnapToGrid} />
</FormControl>
<FormControl>
<FormLabel>
{t('unifiedCanvas.darkenOutsideSelection')}
</FormLabel>
<FormLabel>{t('unifiedCanvas.darkenOutsideSelection')}</FormLabel>
<Checkbox
isChecked={shouldDarkenOutsideBoundingBox}
onChange={handleChangeShouldDarkenOutsideBoundingBox}
@ -161,10 +131,7 @@ const IAICanvasSettingsButtonPopover = () => {
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.autoSaveToGallery')}</FormLabel>
<Checkbox
isChecked={shouldAutoSave}
onChange={handleChangeShouldAutoSave}
/>
<Checkbox isChecked={shouldAutoSave} onChange={handleChangeShouldAutoSave} />
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.saveBoxRegionOnly')}</FormLabel>
@ -175,24 +142,15 @@ const IAICanvasSettingsButtonPopover = () => {
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.limitStrokesToBox')}</FormLabel>
<Checkbox
isChecked={shouldRestrictStrokesToBox}
onChange={handleChangeShouldRestrictStrokesToBox}
/>
<Checkbox isChecked={shouldRestrictStrokesToBox} onChange={handleChangeShouldRestrictStrokesToBox} />
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.showCanvasDebugInfo')}</FormLabel>
<Checkbox
isChecked={shouldShowCanvasDebugInfo}
onChange={handleChangeShouldShowCanvasDebugInfo}
/>
<Checkbox isChecked={shouldShowCanvasDebugInfo} onChange={handleChangeShouldShowCanvasDebugInfo} />
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.antialiasing')}</FormLabel>
<Checkbox
isChecked={shouldAntialias}
onChange={handleChangeShouldAntialias}
/>
<Checkbox isChecked={shouldAntialias} onChange={handleChangeShouldAntialias} />
</FormControl>
</FormControlGroup>
<ClearCanvasHistoryButtonModal />

View File

@ -15,17 +15,9 @@ import {
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIColorPicker from 'common/components/IAIColorPicker';
import {
$tool,
resetToolInteractionState,
} from 'features/canvas/store/canvasNanostore';
import { $tool, resetToolInteractionState } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
addEraseRect,
addFillRect,
setBrushColor,
setBrushSize,
} from 'features/canvas/store/canvasSlice';
import { addEraseRect, addFillRect, setBrushColor, setBrushSize } from 'features/canvas/store/canvasSlice';
import { clamp } from 'lodash-es';
import { memo, useCallback } from 'react';
import type { RgbaColor } from 'react-colorful';
@ -275,11 +267,7 @@ const IAICanvasToolChooserOptions = () => {
</FormControl>
</Flex>
<Box w="full" pt={2} pb={2}>
<IAIColorPicker
color={brushColor}
onChange={handleChangeBrushColor}
withNumberInput
/>
<IAIColorPicker color={brushColor} onChange={handleChangeBrushColor} withNumberInput />
</Box>
</Flex>
</PopoverBody>

View File

@ -1,12 +1,5 @@
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
import {
ButtonGroup,
Combobox,
Flex,
FormControl,
IconButton,
Tooltip,
} from '@invoke-ai/ui-library';
import { ButtonGroup, Combobox, Flex, FormControl, IconButton, Tooltip } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
@ -20,12 +13,7 @@ import {
} from 'features/canvas/store/actions';
import { $canvasBaseLayer, $tool } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
resetCanvas,
resetCanvasView,
setIsMaskEnabled,
setLayer,
} from 'features/canvas/store/canvasSlice';
import { resetCanvas, resetCanvasView, setIsMaskEnabled, setLayer } from 'features/canvas/store/canvasSlice';
import type { CanvasLayer } from 'features/canvas/store/canvasTypes';
import { LAYER_NAMES_DICT } from 'features/canvas/store/canvasTypes';
import { memo, useCallback, useMemo } from 'react';
@ -203,20 +191,13 @@ const IAICanvasToolbar = () => {
[dispatch, isMaskEnabled]
);
const value = useMemo(
() => LAYER_NAMES_DICT.filter((o) => o.value === layer)[0],
[layer]
);
const value = useMemo(() => LAYER_NAMES_DICT.filter((o) => o.value === layer)[0], [layer]);
return (
<Flex alignItems="center" gap={2} flexWrap="wrap">
<Tooltip label={`${t('unifiedCanvas.layer')} (Q)`}>
<FormControl isDisabled={isStaging} w="5rem">
<Combobox
value={value}
options={LAYER_NAMES_DICT}
onChange={handleChangeLayer}
/>
<Combobox value={value} options={LAYER_NAMES_DICT} onChange={handleChangeLayer} />
</FormControl>
</Tooltip>

View File

@ -1,9 +1,5 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
$isMovingBoundingBox,
$isMovingStage,
$tool,
} from 'features/canvas/store/canvasNanostore';
import { $isMovingBoundingBox, $isMovingStage, $tool } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { setStageCoordinates } from 'features/canvas/store/canvasSlice';
import type { KonvaEventObject } from 'konva/lib/Node';
@ -13,9 +9,7 @@ const useCanvasDrag = () => {
const dispatch = useAppDispatch();
const isStaging = useAppSelector(isStagingSelector);
const handleDragStart = useCallback(() => {
if (
!(($tool.get() === 'move' || isStaging) && !$isMovingBoundingBox.get())
) {
if (!(($tool.get() === 'move' || isStaging) && !$isMovingBoundingBox.get())) {
return;
}
$isMovingStage.set(true);
@ -36,9 +30,7 @@ const useCanvasDrag = () => {
);
const handleDragEnd = useCallback(() => {
if (
!(($tool.get() === 'move' || isStaging) && !$isMovingBoundingBox.get())
) {
if (!(($tool.get() === 'move' || isStaging) && !$isMovingBoundingBox.get())) {
return;
}
$isMovingStage.set(false);

View File

@ -8,30 +8,16 @@ import { useDebounce } from 'react-use';
export const useCanvasGenerationMode = () => {
const layerState = useAppSelector((s) => s.canvas.layerState);
const boundingBoxCoordinates = useAppSelector(
(s) => s.canvas.boundingBoxCoordinates
);
const boundingBoxDimensions = useAppSelector(
(s) => s.canvas.boundingBoxDimensions
);
const boundingBoxCoordinates = useAppSelector((s) => s.canvas.boundingBoxCoordinates);
const boundingBoxDimensions = useAppSelector((s) => s.canvas.boundingBoxDimensions);
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
const shouldPreserveMaskedArea = useAppSelector(
(s) => s.canvas.shouldPreserveMaskedArea
);
const [generationMode, setGenerationMode] = useState<
GenerationMode | undefined
>();
const shouldPreserveMaskedArea = useAppSelector((s) => s.canvas.shouldPreserveMaskedArea);
const [generationMode, setGenerationMode] = useState<GenerationMode | undefined>();
useEffect(() => {
setGenerationMode(undefined);
}, [
layerState,
boundingBoxCoordinates,
boundingBoxDimensions,
isMaskEnabled,
shouldPreserveMaskedArea,
]);
}, [layerState, boundingBoxCoordinates, boundingBoxDimensions, isMaskEnabled, shouldPreserveMaskedArea]);
useDebounce(
async () => {
@ -51,21 +37,12 @@ export const useCanvasGenerationMode = () => {
const { baseImageData, maskImageData } = canvasBlobsAndImageData;
// Determine the generation mode
const generationMode = getCanvasGenerationMode(
baseImageData,
maskImageData
);
const generationMode = getCanvasGenerationMode(baseImageData, maskImageData);
setGenerationMode(generationMode);
},
1000,
[
layerState,
boundingBoxCoordinates,
boundingBoxDimensions,
isMaskEnabled,
shouldPreserveMaskedArea,
]
[layerState, boundingBoxCoordinates, boundingBoxDimensions, isMaskEnabled, shouldPreserveMaskedArea]
);
return generationMode;

View File

@ -22,9 +22,7 @@ import { useHotkeys } from 'react-hotkeys-hook';
const useInpaintingCanvasHotkeys = () => {
const dispatch = useAppDispatch();
const activeTabName = useAppSelector(activeTabNameSelector);
const shouldShowBoundingBox = useAppSelector(
(s) => s.canvas.shouldShowBoundingBox
);
const shouldShowBoundingBox = useAppSelector((s) => s.canvas.shouldShowBoundingBox);
const isStaging = useAppSelector(isStagingSelector);
const isMaskEnabled = useAppSelector((s) => s.canvas.isMaskEnabled);
const shouldSnapToGrid = useAppSelector((s) => s.canvas.shouldSnapToGrid);
@ -44,8 +42,7 @@ const useInpaintingCanvasHotkeys = () => {
[]
);
const handleToggleEnableMask = () =>
dispatch(setIsMaskEnabled(!isMaskEnabled));
const handleToggleEnableMask = () => dispatch(setIsMaskEnabled(!isMaskEnabled));
useHotkeys(
['h'],

View File

@ -1,9 +1,5 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
$isDrawing,
$isMovingStage,
$tool,
} from 'features/canvas/store/canvasNanostore';
import { $isDrawing, $isMovingStage, $tool } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { addLine } from 'features/canvas/store/canvasSlice';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';

View File

@ -1,9 +1,5 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
$cursorPosition,
$isDrawing,
$tool,
} from 'features/canvas/store/canvasNanostore';
import { $cursorPosition, $isDrawing, $tool } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { addPointToCurrentLine } from 'features/canvas/store/canvasSlice';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
@ -49,17 +45,8 @@ const useCanvasMouseMove = (
}
didMouseMoveRef.current = true;
dispatch(
addPointToCurrentLine([scaledCursorPosition.x, scaledCursorPosition.y])
);
}, [
didMouseMoveRef,
dispatch,
isStaging,
lastCursorPositionRef,
stageRef,
updateColorUnderCursor,
]);
dispatch(addPointToCurrentLine([scaledCursorPosition.x, scaledCursorPosition.y]));
}, [didMouseMoveRef, dispatch, isStaging, lastCursorPositionRef, stageRef, updateColorUnderCursor]);
};
export default useCanvasMouseMove;

View File

@ -1,10 +1,6 @@
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
$isDrawing,
$isMovingStage,
$tool,
} from 'features/canvas/store/canvasNanostore';
import { $isDrawing, $isMovingStage, $tool } from 'features/canvas/store/canvasNanostore';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import { addPointToCurrentLine } from 'features/canvas/store/canvasSlice';
import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition';
@ -39,9 +35,7 @@ const useCanvasMouseUp = (
* the line's existing points. This allows the line to render as a circle
* centered on that point.
*/
dispatch(
addPointToCurrentLine([scaledCursorPosition.x, scaledCursorPosition.y])
);
dispatch(addPointToCurrentLine([scaledCursorPosition.x, scaledCursorPosition.y]));
} else {
didMouseMoveRef.current = false;
}

View File

@ -1,15 +1,8 @@
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { $isMoveStageKeyHeld } from 'features/canvas/store/canvasNanostore';
import {
setStageCoordinates,
setStageScale,
} from 'features/canvas/store/canvasSlice';
import {
CANVAS_SCALE_BY,
MAX_CANVAS_SCALE,
MIN_CANVAS_SCALE,
} from 'features/canvas/util/constants';
import { setStageCoordinates, setStageScale } from 'features/canvas/store/canvasSlice';
import { CANVAS_SCALE_BY, MAX_CANVAS_SCALE, MIN_CANVAS_SCALE } from 'features/canvas/util/constants';
import type Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import { clamp } from 'lodash-es';
@ -49,11 +42,7 @@ const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
delta = -delta;
}
const newScale = clamp(
stageScale * CANVAS_SCALE_BY ** delta,
MIN_CANVAS_SCALE,
MAX_CANVAS_SCALE
);
const newScale = clamp(stageScale * CANVAS_SCALE_BY ** delta, MIN_CANVAS_SCALE, MAX_CANVAS_SCALE);
const newCoordinates = {
x: cursorPos.x - mousePointTo.x * newScale,

View File

@ -1,13 +1,6 @@
import { useAppDispatch } from 'app/store/storeHooks';
import {
$canvasBaseLayer,
$canvasStage,
$tool,
} from 'features/canvas/store/canvasNanostore';
import {
commitColorPickerColor,
setColorPickerColor,
} from 'features/canvas/store/canvasSlice';
import { $canvasBaseLayer, $canvasStage, $tool } from 'features/canvas/store/canvasNanostore';
import { commitColorPickerColor, setColorPickerColor } from 'features/canvas/store/canvasSlice';
import Konva from 'konva';
import { useCallback } from 'react';
@ -31,19 +24,9 @@ const useColorPicker = () => {
const [r, g, b, a] = canvasBaseLayer
.getContext()
.getImageData(
position.x * pixelRatio,
position.y * pixelRatio,
1,
1
).data;
.getImageData(position.x * pixelRatio, position.y * pixelRatio, 1, 1).data;
if (
r === undefined ||
g === undefined ||
b === undefined ||
a === undefined
) {
if (r === undefined || g === undefined || b === undefined || a === undefined) {
return;
}

View File

@ -3,28 +3,16 @@ import type { ImageDTO } from 'services/api/types';
export const canvasSavedToGallery = createAction('canvas/canvasSavedToGallery');
export const canvasMaskSavedToGallery = createAction(
'canvas/canvasMaskSavedToGallery'
);
export const canvasMaskSavedToGallery = createAction('canvas/canvasMaskSavedToGallery');
export const canvasCopiedToClipboard = createAction(
'canvas/canvasCopiedToClipboard'
);
export const canvasCopiedToClipboard = createAction('canvas/canvasCopiedToClipboard');
export const canvasDownloadedAsImage = createAction(
'canvas/canvasDownloadedAsImage'
);
export const canvasDownloadedAsImage = createAction('canvas/canvasDownloadedAsImage');
export const canvasMerged = createAction('canvas/canvasMerged');
export const stagingAreaImageSaved = createAction<{ imageDTO: ImageDTO }>(
'canvas/stagingAreaImageSaved'
);
export const stagingAreaImageSaved = createAction<{ imageDTO: ImageDTO }>('canvas/stagingAreaImageSaved');
export const canvasMaskToControlAdapter = createAction<{ id: string }>(
'canvas/canvasMaskToControlAdapter'
);
export const canvasMaskToControlAdapter = createAction<{ id: string }>('canvas/canvasMaskToControlAdapter');
export const canvasImageToControlAdapter = createAction<{ id: string }>(
'canvas/canvasImageToControlAdapter'
);
export const canvasImageToControlAdapter = createAction<{ id: string }>('canvas/canvasImageToControlAdapter');

View File

@ -16,8 +16,7 @@ export const $isTransformingBoundingBox = atom<boolean>(false);
export const $isMouseOverBoundingBoxOutline = atom<boolean>(false);
export const $isModifyingBoundingBox = computed(
[$isTransformingBoundingBox, $isMovingBoundingBox],
(isTransformingBoundingBox, isMovingBoundingBox) =>
isTransformingBoundingBox || isMovingBoundingBox
(isTransformingBoundingBox, isMovingBoundingBox) => isTransformingBoundingBox || isMovingBoundingBox
);
export const resetCanvasInteractionState = () => {

View File

@ -6,12 +6,9 @@ import { isCanvasBaseImage } from './canvasTypes';
export const isStagingSelector = createSelector(
selectCanvasSlice,
(canvas) =>
canvas.batchIds.length > 0 ||
canvas.layerState.stagingArea.images.length > 0
(canvas) => canvas.batchIds.length > 0 || canvas.layerState.stagingArea.images.length > 0
);
export const initialCanvasImageSelector = createMemoizedSelector(
selectCanvasSlice,
(canvas) => canvas.layerState.objects.find(isCanvasBaseImage)
export const initialCanvasImageSelector = createMemoizedSelector(selectCanvasSlice, (canvas) =>
canvas.layerState.objects.find(isCanvasBaseImage)
);

View File

@ -1,10 +1,7 @@
import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
import type { RootState } from 'app/store/store';
import {
roundDownToMultiple,
roundToMultiple,
} from 'common/util/roundDownToMultiple';
import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMultiple';
import calculateCoordinates from 'features/canvas/util/calculateCoordinates';
import calculateScale from 'features/canvas/util/calculateScale';
import { STAGE_PADDING_PERCENTAGE } from 'features/canvas/util/constants';
@ -14,10 +11,7 @@ import { initialAspectRatioState } from 'features/parameters/components/ImageSiz
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
import { modelChanged } from 'features/parameters/store/generationSlice';
import type { PayloadActionWithOptimalDimension } from 'features/parameters/store/types';
import {
getIsSizeOptimal,
getOptimalDimension,
} from 'features/parameters/util/optimalDimension';
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import type { IRect, Vector2d } from 'konva/lib/types';
import { clamp, cloneDeep } from 'lodash-es';
import type { RgbaColor } from 'react-colorful';
@ -105,10 +99,7 @@ const setBoundingBoxDimensionsReducer = (
};
state.boundingBoxDimensions = newDimensions;
if (state.boundingBoxScaleMethod === 'auto') {
const scaledDimensions = getScaledBoundingBoxDimensions(
newDimensions,
optimalDimension
);
const scaledDimensions = getScaledBoundingBoxDimensions(newDimensions, optimalDimension);
state.scaledBoundingBoxDimensions = scaledDimensions;
}
};
@ -131,9 +122,7 @@ export const canvasSlice = createSlice({
},
clearMask: (state) => {
state.pastLayerStates.push(cloneDeep(state.layerState));
state.layerState.objects = state.layerState.objects.filter(
(obj) => !isCanvasMaskLine(obj)
);
state.layerState.objects = state.layerState.objects.filter((obj) => !isCanvasMaskLine(obj));
state.futureLayerStates = [];
state.shouldPreserveMaskedArea = false;
},
@ -157,32 +146,17 @@ export const canvasSlice = createSlice({
const { stageDimensions } = state;
const newBoundingBoxDimensions = {
width: roundDownToMultiple(
clamp(width, CANVAS_GRID_SIZE_FINE, optimalDimension),
CANVAS_GRID_SIZE_FINE
),
height: roundDownToMultiple(
clamp(height, CANVAS_GRID_SIZE_FINE, optimalDimension),
CANVAS_GRID_SIZE_FINE
),
width: roundDownToMultiple(clamp(width, CANVAS_GRID_SIZE_FINE, optimalDimension), CANVAS_GRID_SIZE_FINE),
height: roundDownToMultiple(clamp(height, CANVAS_GRID_SIZE_FINE, optimalDimension), CANVAS_GRID_SIZE_FINE),
};
const newBoundingBoxCoordinates = {
x: roundToMultiple(
width / 2 - newBoundingBoxDimensions.width / 2,
CANVAS_GRID_SIZE_FINE
),
y: roundToMultiple(
height / 2 - newBoundingBoxDimensions.height / 2,
CANVAS_GRID_SIZE_FINE
),
x: roundToMultiple(width / 2 - newBoundingBoxDimensions.width / 2, CANVAS_GRID_SIZE_FINE),
y: roundToMultiple(height / 2 - newBoundingBoxDimensions.height / 2, CANVAS_GRID_SIZE_FINE),
};
if (state.boundingBoxScaleMethod === 'auto') {
const scaledDimensions = getScaledBoundingBoxDimensions(
newBoundingBoxDimensions,
optimalDimension
);
const scaledDimensions = getScaledBoundingBoxDimensions(newBoundingBoxDimensions, optimalDimension);
state.scaledBoundingBoxDimensions = scaledDimensions;
}
@ -247,10 +221,7 @@ export const canvasSlice = createSlice({
setStageScale: (state, action: PayloadAction<number>) => {
state.stageScale = action.payload;
},
setShouldDarkenOutsideBoundingBox: (
state,
action: PayloadAction<boolean>
) => {
setShouldDarkenOutsideBoundingBox: (state, action: PayloadAction<boolean>) => {
state.shouldDarkenOutsideBoundingBox = action.payload;
},
clearCanvasHistory: (state) => {
@ -306,8 +277,7 @@ export const canvasSlice = createSlice({
imageName: image.image_name,
});
state.layerState.stagingArea.selectedImageIndex =
state.layerState.stagingArea.images.length - 1;
state.layerState.stagingArea.selectedImageIndex = state.layerState.stagingArea.images.length - 1;
state.futureLayerStates = [];
},
@ -318,9 +288,7 @@ export const canvasSlice = createSlice({
state.pastLayerStates.shift();
}
state.layerState.stagingArea = cloneDeep(
cloneDeep(initialLayerState)
).stagingArea;
state.layerState.stagingArea = cloneDeep(cloneDeep(initialLayerState)).stagingArea;
state.futureLayerStates = [];
state.shouldShowStagingOutline = true;
@ -328,8 +296,7 @@ export const canvasSlice = createSlice({
state.batchIds = [];
},
addFillRect: (state) => {
const { boundingBoxCoordinates, boundingBoxDimensions, brushColor } =
state;
const { boundingBoxCoordinates, boundingBoxDimensions, brushColor } = state;
state.pastLayerStates.push(cloneDeep(state.layerState));
@ -365,12 +332,8 @@ export const canvasSlice = createSlice({
state.futureLayerStates = [];
},
addLine: (
state,
action: PayloadAction<{ points: number[]; tool: CanvasTool }>
) => {
const { layer, brushColor, brushSize, shouldRestrictStrokesToBox } =
state;
addLine: (state, action: PayloadAction<{ points: number[]; tool: CanvasTool }>) => {
const { layer, brushColor, brushSize, shouldRestrictStrokesToBox } = state;
const { points, tool } = action.payload;
if (tool === 'move' || tool === 'colorPicker') {
@ -380,8 +343,7 @@ export const canvasSlice = createSlice({
const newStrokeWidth = brushSize / 2;
// set & then spread this to only conditionally add the "color" key
const newColor =
layer === 'base' && tool === 'brush' ? { color: brushColor } : {};
const newColor = layer === 'base' && tool === 'brush' ? { color: brushColor } : {};
state.pastLayerStates.push(cloneDeep(state.layerState));
@ -488,10 +450,7 @@ export const canvasSlice = createSlice({
1
);
},
canvasResized: (
state,
action: PayloadAction<{ width: number; height: number }>
) => {
canvasResized: (state, action: PayloadAction<{ width: number; height: number }>) => {
state.stageDimensions = {
width: Math.floor(action.payload.width),
height: Math.floor(action.payload.height),
@ -540,8 +499,7 @@ export const canvasSlice = createSlice({
const nextIndex = state.layerState.stagingArea.selectedImageIndex + 1;
const lastIndex = state.layerState.stagingArea.images.length - 1;
state.layerState.stagingArea.selectedImageIndex =
nextIndex > lastIndex ? 0 : nextIndex;
state.layerState.stagingArea.selectedImageIndex = nextIndex > lastIndex ? 0 : nextIndex;
},
prevStagingAreaImage: (state) => {
if (!state.layerState.stagingArea.images.length) {
@ -551,8 +509,7 @@ export const canvasSlice = createSlice({
const prevIndex = state.layerState.stagingArea.selectedImageIndex - 1;
const lastIndex = state.layerState.stagingArea.images.length - 1;
state.layerState.stagingArea.selectedImageIndex =
prevIndex < 0 ? lastIndex : prevIndex;
state.layerState.stagingArea.selectedImageIndex = prevIndex < 0 ? lastIndex : prevIndex;
},
commitStagingAreaImage: (state) => {
if (!state.layerState.stagingArea.images.length) {
@ -582,19 +539,13 @@ export const canvasSlice = createSlice({
state.batchIds = [];
},
setBoundingBoxScaleMethod: {
reducer: (
state,
action: PayloadActionWithOptimalDimension<BoundingBoxScaleMethod>
) => {
reducer: (state, action: PayloadActionWithOptimalDimension<BoundingBoxScaleMethod>) => {
const boundingBoxScaleMethod = action.payload;
const { optimalDimension } = action.meta;
state.boundingBoxScaleMethod = boundingBoxScaleMethod;
if (boundingBoxScaleMethod === 'auto') {
const scaledDimensions = getScaledBoundingBoxDimensions(
state.boundingBoxDimensions,
optimalDimension
);
const scaledDimensions = getScaledBoundingBoxDimensions(state.boundingBoxDimensions, optimalDimension);
state.scaledBoundingBoxDimensions = scaledDimensions;
}
},
@ -605,25 +556,15 @@ export const canvasSlice = createSlice({
},
}),
},
setScaledBoundingBoxDimensions: (
state,
action: PayloadAction<Partial<Dimensions>>
) => {
setScaledBoundingBoxDimensions: (state, action: PayloadAction<Partial<Dimensions>>) => {
state.scaledBoundingBoxDimensions = {
...state.scaledBoundingBoxDimensions,
...action.payload,
};
},
setBoundingBoxDimensions: {
reducer: (
state,
action: PayloadActionWithOptimalDimension<Partial<Dimensions>>
) => {
setBoundingBoxDimensionsReducer(
state,
action.payload,
action.meta.optimalDimension
);
reducer: (state, action: PayloadActionWithOptimalDimension<Partial<Dimensions>>) => {
setBoundingBoxDimensionsReducer(state, action.payload, action.meta.optimalDimension);
},
prepare: (payload: Partial<Dimensions>, optimalDimension: number) => ({
payload,
@ -660,10 +601,7 @@ export const canvasSlice = createSlice({
setShouldAntialias: (state, action: PayloadAction<boolean>) => {
state.shouldAntialias = action.payload;
},
setShouldCropToBoundingBoxOnSave: (
state,
action: PayloadAction<boolean>
) => {
setShouldCropToBoundingBoxOnSave: (state, action: PayloadAction<boolean>) => {
state.shouldCropToBoundingBoxOnSave = action.payload;
},
setColorPickerColor: (state, action: PayloadAction<RgbaColor>) => {
@ -688,9 +626,7 @@ export const canvasSlice = createSlice({
},
extraReducers: (builder) => {
builder.addCase(modelChanged, (state, action) => {
if (
action.meta.previousModel?.base_model === action.payload?.base_model
) {
if (action.meta.previousModel?.base_model === action.payload?.base_model) {
// The base model hasn't changed, we don't need to optimize the size
return;
}
@ -716,25 +652,15 @@ export const canvasSlice = createSlice({
}
if (batch_status.in_progress === 0 && batch_status.pending === 0) {
state.batchIds = state.batchIds.filter(
(id) => id !== batch_status.batch_id
);
state.batchIds = state.batchIds.filter((id) => id !== batch_status.batch_id);
}
});
builder.addMatcher(
queueApi.endpoints.clearQueue.matchFulfilled,
(state) => {
state.batchIds = [];
}
);
builder.addMatcher(
queueApi.endpoints.cancelByBatchIds.matchFulfilled,
(state, action) => {
state.batchIds = state.batchIds.filter(
(id) => !action.meta.arg.originalArgs.batch_ids.includes(id)
);
}
);
builder.addMatcher(queueApi.endpoints.clearQueue.matchFulfilled, (state) => {
state.batchIds = [];
});
builder.addMatcher(queueApi.endpoints.cancelByBatchIds.matchFulfilled, (state, action) => {
state.batchIds = state.batchIds.filter((id) => !action.meta.arg.originalArgs.batch_ids.includes(id));
});
},
});

View File

@ -14,9 +14,8 @@ export const LAYER_NAMES = ['base', 'mask'] as const;
export const zBoundingBoxScaleMethod = z.enum(['none', 'auto', 'manual']);
export type BoundingBoxScaleMethod = z.infer<typeof zBoundingBoxScaleMethod>;
export const isBoundingBoxScaleMethod = (
v: unknown
): v is BoundingBoxScaleMethod => zBoundingBoxScaleMethod.safeParse(v).success;
export const isBoundingBoxScaleMethod = (v: unknown): v is BoundingBoxScaleMethod =>
zBoundingBoxScaleMethod.safeParse(v).success;
export type CanvasDrawingTool = 'brush' | 'eraser';
@ -75,12 +74,7 @@ export type CanvasEraseRect = {
height: number;
};
export type CanvasObject =
| CanvasImage
| CanvasBaseLine
| CanvasMaskLine
| CanvasFillRect
| CanvasEraseRect;
export type CanvasObject = CanvasImage | CanvasBaseLine | CanvasMaskLine | CanvasFillRect | CanvasEraseRect;
export type CanvasLayerState = {
objects: CanvasObject[];
@ -112,9 +106,7 @@ export const isCanvasFillRect = (obj: CanvasObject): obj is CanvasFillRect =>
export const isCanvasEraseRect = (obj: CanvasObject): obj is CanvasEraseRect =>
obj.kind === 'eraseRect' && obj.layer === 'base';
export const isCanvasAnyLine = (
obj: CanvasObject
): obj is CanvasMaskLine | CanvasBaseLine => obj.kind === 'line';
export const isCanvasAnyLine = (obj: CanvasObject): obj is CanvasMaskLine | CanvasBaseLine => obj.kind === 'line';
export interface CanvasState {
_version: 1;

View File

@ -9,12 +9,8 @@ const calculateCoordinates = (
contentHeight: number,
scale: number
): Vector2d => {
const x = Math.floor(
containerWidth / 2 - (containerX + contentWidth / 2) * scale
);
const y = Math.floor(
containerHeight / 2 - (containerY + contentHeight / 2) * scale
);
const x = Math.floor(containerWidth / 2 - (containerX + contentWidth / 2) * scale);
const y = Math.floor(containerHeight / 2 - (containerY + contentHeight / 2) * scale);
return { x, y };
};

View File

@ -44,8 +44,7 @@ const createMaskStage = async (
lineCap: 'round',
lineJoin: 'round',
shadowForStrokeEnabled: false,
globalCompositeOperation:
line.tool === 'brush' ? 'source-over' : 'destination-out',
globalCompositeOperation: line.tool === 'brush' ? 'source-over' : 'destination-out',
})
)
);

View File

@ -1,11 +1,7 @@
/**
* Gets an ImageData object from an image dataURL by drawing it to a canvas.
*/
export const dataURLToImageData = async (
dataURL: string,
width: number,
height: number
): Promise<ImageData> =>
export const dataURLToImageData = async (dataURL: string, width: number, height: number): Promise<ImageData> =>
new Promise((resolve, reject) => {
const canvas = document.createElement('canvas');
canvas.width = width;

View File

@ -6,21 +6,14 @@ import { konvaNodeToBlob } from './konvaNodeToBlob';
/**
* Get the canvas base layer blob, with or without bounding box according to `shouldCropToBoundingBoxOnSave`
*/
export const getBaseLayerBlob = async (
state: RootState,
alwaysUseBoundingBox: boolean = false
) => {
export const getBaseLayerBlob = async (state: RootState, alwaysUseBoundingBox: boolean = false) => {
const canvasBaseLayer = $canvasBaseLayer.get();
if (!canvasBaseLayer) {
throw new Error('Problem getting base layer blob');
}
const {
shouldCropToBoundingBoxOnSave,
boundingBoxCoordinates,
boundingBoxDimensions,
} = state.canvas;
const { shouldCropToBoundingBoxOnSave, boundingBoxCoordinates, boundingBoxDimensions } = state.canvas;
const clonedBaseLayer = canvasBaseLayer.clone();

View File

@ -1,12 +1,6 @@
import { logger } from 'app/logging/logger';
import {
$canvasBaseLayer,
$canvasStage,
} from 'features/canvas/store/canvasNanostore';
import type {
CanvasLayerState,
Dimensions,
} from 'features/canvas/store/canvasTypes';
import { $canvasBaseLayer, $canvasStage } from 'features/canvas/store/canvasNanostore';
import type { CanvasLayerState, Dimensions } from 'features/canvas/store/canvasTypes';
import { isCanvasMaskLine } from 'features/canvas/store/canvasTypes';
import { konvaNodeToImageData } from 'features/canvas/util/konvaNodeToImageData';
import type { Vector2d } from 'konva/lib/types';
@ -57,10 +51,7 @@ export const getCanvasData = async (
// For the base layer, use the offset boundingBox
const baseBlob = await konvaNodeToBlob(clonedBaseLayer, offsetBoundingBox);
const baseImageData = await konvaNodeToImageData(
clonedBaseLayer,
offsetBoundingBox
);
const baseImageData = await konvaNodeToImageData(clonedBaseLayer, offsetBoundingBox);
// For the mask layer, use the normal boundingBox
const maskStage = await createMaskStage(

View File

@ -1,17 +1,9 @@
import {
areAnyPixelsBlack,
getImageDataTransparency,
} from 'common/util/arrayBuffer';
import { areAnyPixelsBlack, getImageDataTransparency } from 'common/util/arrayBuffer';
import type { GenerationMode } from 'features/canvas/store/canvasTypes';
export const getCanvasGenerationMode = (
baseImageData: ImageData,
maskImageData: ImageData
): GenerationMode => {
const {
isPartiallyTransparent: baseIsPartiallyTransparent,
isFullyTransparent: baseIsFullyTransparent,
} = getImageDataTransparency(baseImageData.data);
export const getCanvasGenerationMode = (baseImageData: ImageData, maskImageData: ImageData): GenerationMode => {
const { isPartiallyTransparent: baseIsPartiallyTransparent, isFullyTransparent: baseIsFullyTransparent } =
getImageDataTransparency(baseImageData.data);
// check mask for black
const doesMaskHaveBlackPixels = areAnyPixelsBlack(maskImageData.data);

View File

@ -2,10 +2,7 @@ import { roundToMultiple } from 'common/util/roundDownToMultiple';
import type { Dimensions } from 'features/canvas/store/canvasTypes';
import { CANVAS_GRID_SIZE_FINE } from 'features/canvas/store/constants';
const getScaledBoundingBoxDimensions = (
dimensions: Dimensions,
optimalDimension: number
) => {
const getScaledBoundingBoxDimensions = (dimensions: Dimensions, optimalDimension: number) => {
const { width, height } = dimensions;
const scaledDimensions = { width, height };
@ -22,16 +19,10 @@ const getScaledBoundingBoxDimensions = (
} else {
if (aspectRatio > 1) {
scaledDimensions.width = maxDimension;
scaledDimensions.height = roundToMultiple(
maxDimension / aspectRatio,
CANVAS_GRID_SIZE_FINE
);
scaledDimensions.height = roundToMultiple(maxDimension / aspectRatio, CANVAS_GRID_SIZE_FINE);
} else if (aspectRatio < 1) {
scaledDimensions.height = maxDimension;
scaledDimensions.width = roundToMultiple(
maxDimension * aspectRatio,
CANVAS_GRID_SIZE_FINE
);
scaledDimensions.width = roundToMultiple(maxDimension * aspectRatio, CANVAS_GRID_SIZE_FINE);
}
currentArea = scaledDimensions.width * scaledDimensions.height;
}

View File

@ -7,8 +7,6 @@ import { CANVAS_TAB_TESTID } from 'features/canvas/store/constants';
* it's safer to check the canvas tab and grab its parent.
*/
export const isElChildOfCanvasTab = (el: HTMLElement) => {
const canvasContainerEl = document.querySelector(
`[data-testid="${CANVAS_TAB_TESTID}"]`
);
const canvasContainerEl = document.querySelector(`[data-testid="${CANVAS_TAB_TESTID}"]`);
return Boolean(canvasContainerEl?.parentElement?.contains(el));
};

View File

@ -9,9 +9,6 @@ import { canvasToBlob } from './canvasToBlob';
* @param boundingBox - The bounding box to crop to
* @returns A Promise that resolves with Blob of the node cropped to the bounding box
*/
export const konvaNodeToBlob = async (
node: Konva.Node,
boundingBox: IRect
): Promise<Blob> => {
export const konvaNodeToBlob = async (node: Konva.Node, boundingBox: IRect): Promise<Blob> => {
return await canvasToBlob(node.toCanvas(boundingBox));
};

View File

@ -9,16 +9,9 @@ import { dataURLToImageData } from './dataURLToImageData';
* @param boundingBox - The bounding box to crop to
* @returns A Promise that resolves with ImageData object of the node cropped to the bounding box
*/
export const konvaNodeToImageData = async (
node: Konva.Node,
boundingBox: IRect
): Promise<ImageData> => {
export const konvaNodeToImageData = async (node: Konva.Node, boundingBox: IRect): Promise<ImageData> => {
// get a dataURL of the bbox'd region
const dataURL = node.toDataURL(boundingBox);
return await dataURLToImageData(
dataURL,
boundingBox.width,
boundingBox.height
);
return await dataURLToImageData(dataURL, boundingBox.width, boundingBox.height);
};

View File

@ -1,10 +1,7 @@
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import type { Dimensions } from 'features/canvas/store/canvasTypes';
const roundDimensionsToMultiple = (
dimensions: Dimensions,
multiple: number
): Dimensions => {
const roundDimensionsToMultiple = (dimensions: Dimensions, multiple: number): Dimensions => {
return {
width: roundToMultiple(dimensions.width, multiple),
height: roundToMultiple(dimensions.height, multiple),