mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Abandons "inpainting" canvas lock
This commit is contained in:
parent
c0005eb063
commit
635e7da05d
@ -84,14 +84,14 @@ export const store = configureStore({
|
|||||||
devTools: {
|
devTools: {
|
||||||
// Uncommenting these very rapidly called actions makes the redux dev tools output much more readable
|
// Uncommenting these very rapidly called actions makes the redux dev tools output much more readable
|
||||||
actionsDenylist: [
|
actionsDenylist: [
|
||||||
// 'canvas/setCursorPosition',
|
'canvas/setCursorPosition',
|
||||||
// 'canvas/setStageCoordinates',
|
'canvas/setStageCoordinates',
|
||||||
// 'canvas/setStageScale',
|
'canvas/setStageScale',
|
||||||
// 'canvas/setIsDrawing',
|
'canvas/setIsDrawing',
|
||||||
// 'canvas/setBoundingBoxCoordinates',
|
// 'canvas/setBoundingBoxCoordinates',
|
||||||
// 'canvas/setBoundingBoxDimensions',
|
// 'canvas/setBoundingBoxDimensions',
|
||||||
// 'canvas/setIsDrawing',
|
'canvas/setIsDrawing',
|
||||||
// 'canvas/addPointToCurrentLine',
|
'canvas/addPointToCurrentLine',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import { MutableRefObject, useCallback, useRef } from 'react';
|
import { MutableRefObject, useRef } from 'react';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { Layer, Stage } from 'react-konva';
|
import { Layer, Stage } from 'react-konva';
|
||||||
import { Stage as StageType } from 'konva/lib/Stage';
|
import { Stage as StageType } from 'konva/lib/Stage';
|
||||||
import { useAppSelector } from 'app/store';
|
import { useAppSelector } from 'app/store';
|
||||||
import {
|
import {
|
||||||
initialCanvasImageSelector,
|
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
isStagingSelector,
|
isStagingSelector,
|
||||||
shouldLockToInitialImageSelector,
|
|
||||||
} from 'features/canvas/store/canvasSelectors';
|
} from 'features/canvas/store/canvasSelectors';
|
||||||
import IAICanvasMaskLines from './IAICanvasMaskLines';
|
import IAICanvasMaskLines from './IAICanvasMaskLines';
|
||||||
import IAICanvasBrushPreview from './IAICanvasBrushPreview';
|
import IAICanvasBrushPreview from './IAICanvasBrushPreview';
|
||||||
@ -16,7 +14,6 @@ import IAICanvasBoundingBox from './IAICanvasToolbar/IAICanvasBoundingBox';
|
|||||||
import useCanvasHotkeys from '../hooks/useCanvasHotkeys';
|
import useCanvasHotkeys from '../hooks/useCanvasHotkeys';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
|
||||||
import IAICanvasMaskCompositer from './IAICanvasMaskCompositer';
|
import IAICanvasMaskCompositer from './IAICanvasMaskCompositer';
|
||||||
import useCanvasWheel from '../hooks/useCanvasZoom';
|
import useCanvasWheel from '../hooks/useCanvasZoom';
|
||||||
import useCanvasMouseDown from '../hooks/useCanvasMouseDown';
|
import useCanvasMouseDown from '../hooks/useCanvasMouseDown';
|
||||||
@ -33,20 +30,8 @@ import IAICanvasStagingArea from './IAICanvasStagingArea';
|
|||||||
import IAICanvasStagingAreaToolbar from './IAICanvasStagingAreaToolbar';
|
import IAICanvasStagingAreaToolbar from './IAICanvasStagingAreaToolbar';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[
|
[canvasSelector, isStagingSelector],
|
||||||
shouldLockToInitialImageSelector,
|
(canvas, isStaging) => {
|
||||||
canvasSelector,
|
|
||||||
isStagingSelector,
|
|
||||||
activeTabNameSelector,
|
|
||||||
initialCanvasImageSelector,
|
|
||||||
],
|
|
||||||
(
|
|
||||||
shouldLockToInitialImage,
|
|
||||||
canvas,
|
|
||||||
isStaging,
|
|
||||||
activeTabName,
|
|
||||||
initialCanvasImage
|
|
||||||
) => {
|
|
||||||
const {
|
const {
|
||||||
isMaskEnabled,
|
isMaskEnabled,
|
||||||
stageScale,
|
stageScale,
|
||||||
@ -90,8 +75,6 @@ const selector = createSelector(
|
|||||||
tool,
|
tool,
|
||||||
isStaging,
|
isStaging,
|
||||||
shouldShowIntermediates,
|
shouldShowIntermediates,
|
||||||
shouldLockToInitialImage,
|
|
||||||
initialCanvasImage,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -118,8 +101,6 @@ const IAICanvas = () => {
|
|||||||
tool,
|
tool,
|
||||||
isStaging,
|
isStaging,
|
||||||
shouldShowIntermediates,
|
shouldShowIntermediates,
|
||||||
shouldLockToInitialImage,
|
|
||||||
initialCanvasImage,
|
|
||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
useCanvasHotkeys();
|
useCanvasHotkeys();
|
||||||
|
|
||||||
@ -145,34 +126,6 @@ const IAICanvas = () => {
|
|||||||
const { handleDragStart, handleDragMove, handleDragEnd } =
|
const { handleDragStart, handleDragMove, handleDragEnd } =
|
||||||
useCanvasDragMove();
|
useCanvasDragMove();
|
||||||
|
|
||||||
const dragBoundFunc = useCallback(
|
|
||||||
(newCoordinates: Vector2d) => {
|
|
||||||
if (shouldLockToInitialImage && initialCanvasImage) {
|
|
||||||
newCoordinates.x = _.clamp(
|
|
||||||
newCoordinates.x,
|
|
||||||
stageDimensions.width -
|
|
||||||
Math.floor(initialCanvasImage.width * stageScale),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
newCoordinates.y = _.clamp(
|
|
||||||
newCoordinates.y,
|
|
||||||
stageDimensions.height -
|
|
||||||
Math.floor(initialCanvasImage.height * stageScale),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newCoordinates;
|
|
||||||
},
|
|
||||||
[
|
|
||||||
initialCanvasImage,
|
|
||||||
shouldLockToInitialImage,
|
|
||||||
stageDimensions.height,
|
|
||||||
stageDimensions.width,
|
|
||||||
stageScale,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="inpainting-canvas-container">
|
<div className="inpainting-canvas-container">
|
||||||
<div className="inpainting-canvas-wrapper">
|
<div className="inpainting-canvas-wrapper">
|
||||||
@ -188,7 +141,6 @@ const IAICanvas = () => {
|
|||||||
width={stageDimensions.width}
|
width={stageDimensions.width}
|
||||||
height={stageDimensions.height}
|
height={stageDimensions.height}
|
||||||
scale={{ x: stageScale, y: stageScale }}
|
scale={{ x: stageScale, y: stageScale }}
|
||||||
dragBoundFunc={dragBoundFunc}
|
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseOut}
|
onMouseLeave={handleMouseOut}
|
||||||
|
@ -38,7 +38,6 @@ const IAICanvasObjectRenderer = () => {
|
|||||||
);
|
);
|
||||||
} else if (isCanvasBaseLine(obj)) {
|
} else if (isCanvasBaseLine(obj)) {
|
||||||
return (
|
return (
|
||||||
<Group {...obj.clipRect}>
|
|
||||||
<Line
|
<Line
|
||||||
key={i}
|
key={i}
|
||||||
points={obj.points}
|
points={obj.points}
|
||||||
@ -53,7 +52,6 @@ const IAICanvasObjectRenderer = () => {
|
|||||||
obj.tool === 'brush' ? 'source-over' : 'destination-out'
|
obj.tool === 'brush' ? 'source-over' : 'destination-out'
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Group>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
@ -9,21 +9,19 @@ import {
|
|||||||
setDoesCanvasNeedScaling,
|
setDoesCanvasNeedScaling,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { canvasSelector, initialCanvasImageSelector } from 'features/canvas/store/canvasSelectors';
|
import {
|
||||||
|
canvasSelector,
|
||||||
|
initialCanvasImageSelector,
|
||||||
|
} from 'features/canvas/store/canvasSelectors';
|
||||||
|
|
||||||
const canvasResizerSelector = createSelector(
|
const canvasResizerSelector = createSelector(
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
initialCanvasImageSelector,
|
initialCanvasImageSelector,
|
||||||
activeTabNameSelector,
|
activeTabNameSelector,
|
||||||
(canvas, initialCanvasImage, activeTabName) => {
|
(canvas, initialCanvasImage, activeTabName) => {
|
||||||
const {
|
const { doesCanvasNeedScaling, isCanvasInitialized } = canvas;
|
||||||
doesCanvasNeedScaling,
|
|
||||||
shouldLockToInitialImage,
|
|
||||||
isCanvasInitialized,
|
|
||||||
} = canvas;
|
|
||||||
return {
|
return {
|
||||||
doesCanvasNeedScaling,
|
doesCanvasNeedScaling,
|
||||||
shouldLockToInitialImage,
|
|
||||||
activeTabName,
|
activeTabName,
|
||||||
initialCanvasImage,
|
initialCanvasImage,
|
||||||
isCanvasInitialized,
|
isCanvasInitialized,
|
||||||
@ -35,7 +33,6 @@ const IAICanvasResizer = () => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const {
|
const {
|
||||||
doesCanvasNeedScaling,
|
doesCanvasNeedScaling,
|
||||||
shouldLockToInitialImage,
|
|
||||||
activeTabName,
|
activeTabName,
|
||||||
initialCanvasImage,
|
initialCanvasImage,
|
||||||
isCanvasInitialized,
|
isCanvasInitialized,
|
||||||
@ -58,11 +55,7 @@ const IAICanvasResizer = () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isCanvasInitialized || shouldLockToInitialImage) {
|
|
||||||
dispatch(resizeAndScaleCanvas());
|
|
||||||
} else {
|
|
||||||
dispatch(resizeCanvas());
|
dispatch(resizeCanvas());
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(setDoesCanvasNeedScaling(false));
|
dispatch(setDoesCanvasNeedScaling(false));
|
||||||
}, 0);
|
}, 0);
|
||||||
@ -72,7 +65,6 @@ const IAICanvasResizer = () => {
|
|||||||
doesCanvasNeedScaling,
|
doesCanvasNeedScaling,
|
||||||
activeTabName,
|
activeTabName,
|
||||||
isCanvasInitialized,
|
isCanvasInitialized,
|
||||||
shouldLockToInitialImage,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { KonvaEventObject } from 'konva/lib/Node';
|
import { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import { Box } from 'konva/lib/shapes/Transformer';
|
|
||||||
import { Vector2d } from 'konva/lib/types';
|
import { Vector2d } from 'konva/lib/types';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { Group, Rect, Transformer } from 'react-konva';
|
import { Group, Rect, Transformer } from 'react-konva';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store';
|
import { useAppDispatch, useAppSelector } from 'app/store';
|
||||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
|
||||||
import {
|
import {
|
||||||
initialCanvasImageSelector,
|
roundDownToMultiple,
|
||||||
canvasSelector,
|
roundToMultiple,
|
||||||
shouldLockToInitialImageSelector,
|
} from 'common/util/roundDownToMultiple';
|
||||||
} from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import {
|
import {
|
||||||
setBoundingBoxCoordinates,
|
setBoundingBoxCoordinates,
|
||||||
setBoundingBoxDimensions,
|
setBoundingBoxDimensions,
|
||||||
@ -21,14 +19,10 @@ import {
|
|||||||
setIsTransformingBoundingBox,
|
setIsTransformingBoundingBox,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { GroupConfig } from 'konva/lib/Group';
|
import { GroupConfig } from 'konva/lib/Group';
|
||||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
|
||||||
|
|
||||||
const boundingBoxPreviewSelector = createSelector(
|
const boundingBoxPreviewSelector = createSelector(
|
||||||
shouldLockToInitialImageSelector,
|
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
initialCanvasImageSelector,
|
(canvas) => {
|
||||||
activeTabNameSelector,
|
|
||||||
(shouldLockToInitialImage, canvas, initialCanvasImage, activeTabName) => {
|
|
||||||
const {
|
const {
|
||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
@ -54,14 +48,11 @@ const boundingBoxPreviewSelector = createSelector(
|
|||||||
isTransformingBoundingBox,
|
isTransformingBoundingBox,
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
stageScale,
|
stageScale,
|
||||||
initialCanvasImage,
|
|
||||||
activeTabName,
|
|
||||||
shouldSnapToGrid,
|
shouldSnapToGrid,
|
||||||
tool,
|
tool,
|
||||||
stageCoordinates,
|
stageCoordinates,
|
||||||
boundingBoxStrokeWidth: (isMouseOverBoundingBox ? 8 : 1) / stageScale,
|
boundingBoxStrokeWidth: (isMouseOverBoundingBox ? 8 : 1) / stageScale,
|
||||||
hitStrokeWidth: 20 / stageScale,
|
hitStrokeWidth: 20 / stageScale,
|
||||||
shouldLockToInitialImage,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -88,13 +79,10 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
stageCoordinates,
|
stageCoordinates,
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
stageScale,
|
stageScale,
|
||||||
initialCanvasImage,
|
|
||||||
activeTabName,
|
|
||||||
shouldSnapToGrid,
|
shouldSnapToGrid,
|
||||||
tool,
|
tool,
|
||||||
boundingBoxStrokeWidth,
|
boundingBoxStrokeWidth,
|
||||||
hitStrokeWidth,
|
hitStrokeWidth,
|
||||||
shouldLockToInitialImage,
|
|
||||||
} = useAppSelector(boundingBoxPreviewSelector);
|
} = useAppSelector(boundingBoxPreviewSelector);
|
||||||
|
|
||||||
const transformerRef = useRef<Konva.Transformer>(null);
|
const transformerRef = useRef<Konva.Transformer>(null);
|
||||||
@ -139,40 +127,6 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
[dispatch, shouldSnapToGrid]
|
[dispatch, shouldSnapToGrid]
|
||||||
);
|
);
|
||||||
|
|
||||||
const dragBoundFunc = useCallback(
|
|
||||||
(position: Vector2d) => {
|
|
||||||
/**
|
|
||||||
* This limits the bounding box's drag coordinates.
|
|
||||||
*/
|
|
||||||
if (!shouldLockToInitialImage) return boundingBoxCoordinates;
|
|
||||||
|
|
||||||
const { x, y } = position;
|
|
||||||
|
|
||||||
const maxX =
|
|
||||||
stageDimensions.width -
|
|
||||||
boundingBoxDimensions.width -
|
|
||||||
(stageDimensions.width % 64);
|
|
||||||
|
|
||||||
const maxY =
|
|
||||||
stageDimensions.height -
|
|
||||||
boundingBoxDimensions.height -
|
|
||||||
(stageDimensions.height % 64);
|
|
||||||
|
|
||||||
const clampedX = Math.floor(_.clamp(x, 0, maxX));
|
|
||||||
const clampedY = Math.floor(_.clamp(y, 0, maxY));
|
|
||||||
|
|
||||||
return { x: clampedX, y: clampedY };
|
|
||||||
},
|
|
||||||
[
|
|
||||||
shouldLockToInitialImage,
|
|
||||||
boundingBoxCoordinates,
|
|
||||||
stageDimensions.width,
|
|
||||||
stageDimensions.height,
|
|
||||||
boundingBoxDimensions.width,
|
|
||||||
boundingBoxDimensions.height,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnTransform = useCallback(() => {
|
const handleOnTransform = useCallback(() => {
|
||||||
/**
|
/**
|
||||||
* The Konva Transformer changes the object's anchor point and scale factor,
|
* The Konva Transformer changes the object's anchor point and scale factor,
|
||||||
@ -202,15 +156,15 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
setBoundingBoxCoordinates({
|
setBoundingBoxCoordinates({
|
||||||
x,
|
x: shouldSnapToGrid ? roundDownToMultiple(x, 64) : x,
|
||||||
y,
|
y: shouldSnapToGrid ? roundDownToMultiple(y, 64) : y,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reset the scale now that the coords/dimensions have been un-scaled
|
// Reset the scale now that the coords/dimensions have been un-scaled
|
||||||
rect.scaleX(1);
|
rect.scaleX(1);
|
||||||
rect.scaleY(1);
|
rect.scaleY(1);
|
||||||
}, [dispatch]);
|
}, [dispatch, shouldSnapToGrid]);
|
||||||
|
|
||||||
const anchorDragBoundFunc = useCallback(
|
const anchorDragBoundFunc = useCallback(
|
||||||
(
|
(
|
||||||
@ -225,40 +179,26 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
*
|
*
|
||||||
* We need to snap the new dimensions to steps of 64. But because the whole
|
* We need to snap the new dimensions to steps of 64. But because the whole
|
||||||
* stage is scaled, our actual desired step is actually 64 * the stage scale.
|
* stage is scaled, our actual desired step is actually 64 * the stage scale.
|
||||||
|
*
|
||||||
|
* Additionally, we need to ensure we offset the position so that we snap to a
|
||||||
|
* multiple of 64 that is aligned with the grid, and not from the absolute zero
|
||||||
|
* coordinate.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return {
|
// Calculate the offset of the grid.
|
||||||
x: roundToMultiple(newPos.x, scaledStep),
|
const offsetX = oldPos.x % scaledStep;
|
||||||
y: roundToMultiple(newPos.y, scaledStep),
|
const offsetY = oldPos.y % scaledStep;
|
||||||
|
|
||||||
|
const newCoordinates = {
|
||||||
|
x: roundDownToMultiple(newPos.x, scaledStep) + offsetX,
|
||||||
|
y: roundDownToMultiple(newPos.y, scaledStep) + offsetY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return newCoordinates;
|
||||||
},
|
},
|
||||||
[scaledStep]
|
[scaledStep]
|
||||||
);
|
);
|
||||||
|
|
||||||
const boundBoxFunc = useCallback(
|
|
||||||
(oldBoundBox: Box, newBoundBox: Box) => {
|
|
||||||
/**
|
|
||||||
* The transformer uses this callback to limit valid transformations.
|
|
||||||
* Unlike anchorDragBoundFunc, it does get a width and height, so
|
|
||||||
* the logic to constrain the size of the bounding box is very simple.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// On the Inpainting canvas, the bounding box needs to stay in the stage
|
|
||||||
if (
|
|
||||||
shouldLockToInitialImage &&
|
|
||||||
(newBoundBox.width + newBoundBox.x > stageDimensions.width ||
|
|
||||||
newBoundBox.height + newBoundBox.y > stageDimensions.height ||
|
|
||||||
newBoundBox.x < 0 ||
|
|
||||||
newBoundBox.y < 0)
|
|
||||||
) {
|
|
||||||
return oldBoundBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newBoundBox;
|
|
||||||
},
|
|
||||||
[shouldLockToInitialImage, stageDimensions.height, stageDimensions.width]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleStartedTransforming = () => {
|
const handleStartedTransforming = () => {
|
||||||
dispatch(setIsTransformingBoundingBox(true));
|
dispatch(setIsTransformingBoundingBox(true));
|
||||||
};
|
};
|
||||||
@ -310,11 +250,11 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
globalCompositeOperation={'destination-out'}
|
globalCompositeOperation={'destination-out'}
|
||||||
/>
|
/>
|
||||||
<Rect
|
<Rect
|
||||||
{...(shouldLockToInitialImage ? { dragBoundFunc } : {})}
|
|
||||||
listening={!isDrawing && tool === 'move'}
|
|
||||||
draggable={true}
|
draggable={true}
|
||||||
fillEnabled={false}
|
fillEnabled={false}
|
||||||
height={boundingBoxDimensions.height}
|
height={boundingBoxDimensions.height}
|
||||||
|
hitStrokeWidth={hitStrokeWidth}
|
||||||
|
listening={!isDrawing && tool === 'move'}
|
||||||
onDragEnd={handleEndedModifying}
|
onDragEnd={handleEndedModifying}
|
||||||
onDragMove={handleOnDragMove}
|
onDragMove={handleOnDragMove}
|
||||||
onMouseDown={handleStartedMoving}
|
onMouseDown={handleStartedMoving}
|
||||||
@ -329,7 +269,6 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
width={boundingBoxDimensions.width}
|
width={boundingBoxDimensions.width}
|
||||||
x={boundingBoxCoordinates.x}
|
x={boundingBoxCoordinates.x}
|
||||||
y={boundingBoxCoordinates.y}
|
y={boundingBoxCoordinates.y}
|
||||||
hitStrokeWidth={hitStrokeWidth}
|
|
||||||
/>
|
/>
|
||||||
<Transformer
|
<Transformer
|
||||||
anchorCornerRadius={3}
|
anchorCornerRadius={3}
|
||||||
@ -340,7 +279,6 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
borderDash={[4, 4]}
|
borderDash={[4, 4]}
|
||||||
borderEnabled={true}
|
borderEnabled={true}
|
||||||
borderStroke={'black'}
|
borderStroke={'black'}
|
||||||
boundBoxFunc={boundBoxFunc}
|
|
||||||
draggable={false}
|
draggable={false}
|
||||||
enabledAnchors={tool === 'move' ? undefined : []}
|
enabledAnchors={tool === 'move' ? undefined : []}
|
||||||
flipEnabled={false}
|
flipEnabled={false}
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
resizeAndScaleCanvas,
|
resizeAndScaleCanvas,
|
||||||
resetCanvas,
|
resetCanvas,
|
||||||
resetCanvasView,
|
resetCanvasView,
|
||||||
setShouldLockToInitialImage,
|
|
||||||
setTool,
|
setTool,
|
||||||
fitBoundingBoxToStage,
|
fitBoundingBoxToStage,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
@ -39,11 +38,10 @@ import {
|
|||||||
export const selector = createSelector(
|
export const selector = createSelector(
|
||||||
[canvasSelector, isStagingSelector],
|
[canvasSelector, isStagingSelector],
|
||||||
(canvas, isStaging) => {
|
(canvas, isStaging) => {
|
||||||
const { tool, shouldLockToInitialImage } = canvas;
|
const { tool } = canvas;
|
||||||
return {
|
return {
|
||||||
tool,
|
tool,
|
||||||
isStaging,
|
isStaging,
|
||||||
shouldLockToInitialImage,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -55,16 +53,7 @@ export const selector = createSelector(
|
|||||||
|
|
||||||
const IAICanvasOutpaintingControls = () => {
|
const IAICanvasOutpaintingControls = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { tool, isStaging, shouldLockToInitialImage } =
|
const { tool, isStaging } = useAppSelector(selector);
|
||||||
useAppSelector(selector);
|
|
||||||
|
|
||||||
const handleToggleShouldLockToInitialImage = (
|
|
||||||
e: ChangeEvent<HTMLInputElement>
|
|
||||||
) => {
|
|
||||||
dispatch(setShouldLockToInitialImage(e.target.checked));
|
|
||||||
dispatch(resizeAndScaleCanvas());
|
|
||||||
dispatch(fitBoundingBoxToStage());
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="inpainting-settings">
|
<div className="inpainting-settings">
|
||||||
@ -151,11 +140,6 @@ const IAICanvasOutpaintingControls = () => {
|
|||||||
onClick={() => dispatch(resetCanvas())}
|
onClick={() => dispatch(resetCanvas())}
|
||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
<IAICheckbox
|
|
||||||
label={'Lock Canvas to Initial Image'}
|
|
||||||
isChecked={shouldLockToInitialImage}
|
|
||||||
onChange={handleToggleShouldLockToInitialImage}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,9 +8,11 @@ import { MutableRefObject, useCallback } from 'react';
|
|||||||
import {
|
import {
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
initialCanvasImageSelector,
|
initialCanvasImageSelector,
|
||||||
shouldLockToInitialImageSelector,
|
|
||||||
} from 'features/canvas/store/canvasSelectors';
|
} from 'features/canvas/store/canvasSelectors';
|
||||||
import { setStageCoordinates, setStageScale } from 'features/canvas/store/canvasSlice';
|
import {
|
||||||
|
setStageCoordinates,
|
||||||
|
setStageScale,
|
||||||
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import {
|
import {
|
||||||
CANVAS_SCALE_BY,
|
CANVAS_SCALE_BY,
|
||||||
MAX_CANVAS_SCALE,
|
MAX_CANVAS_SCALE,
|
||||||
@ -18,13 +20,8 @@ import {
|
|||||||
} from '../util/constants';
|
} from '../util/constants';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[
|
[activeTabNameSelector, canvasSelector, initialCanvasImageSelector],
|
||||||
activeTabNameSelector,
|
(activeTabName, canvas, initialCanvasImage) => {
|
||||||
canvasSelector,
|
|
||||||
initialCanvasImageSelector,
|
|
||||||
shouldLockToInitialImageSelector,
|
|
||||||
],
|
|
||||||
(activeTabName, canvas, initialCanvasImage, shouldLockToInitialImage) => {
|
|
||||||
const {
|
const {
|
||||||
isMoveStageKeyHeld,
|
isMoveStageKeyHeld,
|
||||||
stageScale,
|
stageScale,
|
||||||
@ -36,7 +33,6 @@ const selector = createSelector(
|
|||||||
stageScale,
|
stageScale,
|
||||||
activeTabName,
|
activeTabName,
|
||||||
initialCanvasImage,
|
initialCanvasImage,
|
||||||
shouldLockToInitialImage,
|
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
minimumStageScale,
|
minimumStageScale,
|
||||||
};
|
};
|
||||||
@ -51,7 +47,6 @@ const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
|
|||||||
stageScale,
|
stageScale,
|
||||||
activeTabName,
|
activeTabName,
|
||||||
initialCanvasImage,
|
initialCanvasImage,
|
||||||
shouldLockToInitialImage,
|
|
||||||
stageDimensions,
|
stageDimensions,
|
||||||
minimumStageScale,
|
minimumStageScale,
|
||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
@ -83,7 +78,7 @@ const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
|
|||||||
|
|
||||||
const newScale = _.clamp(
|
const newScale = _.clamp(
|
||||||
stageScale * CANVAS_SCALE_BY ** delta,
|
stageScale * CANVAS_SCALE_BY ** delta,
|
||||||
shouldLockToInitialImage ? minimumStageScale : MIN_CANVAS_SCALE,
|
MIN_CANVAS_SCALE,
|
||||||
MAX_CANVAS_SCALE
|
MAX_CANVAS_SCALE
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -92,36 +87,10 @@ const useCanvasWheel = (stageRef: MutableRefObject<Konva.Stage | null>) => {
|
|||||||
y: cursorPos.y - mousePointTo.y * newScale,
|
y: cursorPos.y - mousePointTo.y * newScale,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldLockToInitialImage) {
|
|
||||||
newCoordinates.x = _.clamp(
|
|
||||||
newCoordinates.x,
|
|
||||||
stageDimensions.width -
|
|
||||||
Math.floor(initialCanvasImage.width * newScale),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
newCoordinates.y = _.clamp(
|
|
||||||
newCoordinates.y,
|
|
||||||
stageDimensions.height -
|
|
||||||
Math.floor(initialCanvasImage.height * newScale),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(setStageScale(newScale));
|
dispatch(setStageScale(newScale));
|
||||||
dispatch(setStageCoordinates(newCoordinates));
|
dispatch(setStageCoordinates(newCoordinates));
|
||||||
},
|
},
|
||||||
[
|
[stageRef, isMoveStageKeyHeld, initialCanvasImage, stageScale, dispatch]
|
||||||
activeTabName,
|
|
||||||
stageRef,
|
|
||||||
isMoveStageKeyHeld,
|
|
||||||
initialCanvasImage,
|
|
||||||
stageScale,
|
|
||||||
shouldLockToInitialImage,
|
|
||||||
minimumStageScale,
|
|
||||||
dispatch,
|
|
||||||
stageDimensions.width,
|
|
||||||
stageDimensions.height,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,9 +6,6 @@ export const canvasSelector = (state: RootState): CanvasState => state.canvas;
|
|||||||
export const isStagingSelector = (state: RootState): boolean =>
|
export const isStagingSelector = (state: RootState): boolean =>
|
||||||
state.canvas.layerState.stagingArea.images.length > 0;
|
state.canvas.layerState.stagingArea.images.length > 0;
|
||||||
|
|
||||||
export const shouldLockToInitialImageSelector = (state: RootState): boolean =>
|
|
||||||
state.canvas.shouldLockToInitialImage;
|
|
||||||
|
|
||||||
export const initialCanvasImageSelector = (
|
export const initialCanvasImageSelector = (
|
||||||
state: RootState
|
state: RootState
|
||||||
): CanvasImage | undefined =>
|
): CanvasImage | undefined =>
|
||||||
|
@ -49,7 +49,6 @@ const initialCanvasState: CanvasState = {
|
|||||||
eraserSize: 50,
|
eraserSize: 50,
|
||||||
futureLayerStates: [],
|
futureLayerStates: [],
|
||||||
inpaintReplace: 0.1,
|
inpaintReplace: 0.1,
|
||||||
initialCanvasImageClipRect: undefined,
|
|
||||||
isCanvasInitialized: false,
|
isCanvasInitialized: false,
|
||||||
isDrawing: false,
|
isDrawing: false,
|
||||||
isMaskEnabled: true,
|
isMaskEnabled: true,
|
||||||
@ -68,7 +67,6 @@ const initialCanvasState: CanvasState = {
|
|||||||
shouldAutoSave: false,
|
shouldAutoSave: false,
|
||||||
shouldDarkenOutsideBoundingBox: false,
|
shouldDarkenOutsideBoundingBox: false,
|
||||||
shouldLockBoundingBox: false,
|
shouldLockBoundingBox: false,
|
||||||
shouldLockToInitialImage: false,
|
|
||||||
shouldPreserveMaskedArea: false,
|
shouldPreserveMaskedArea: false,
|
||||||
shouldShowBoundingBox: true,
|
shouldShowBoundingBox: true,
|
||||||
shouldShowBrush: true,
|
shouldShowBrush: true,
|
||||||
@ -298,10 +296,6 @@ export const canvasSlice = createSlice({
|
|||||||
tool,
|
tool,
|
||||||
strokeWidth: newStrokeWidth,
|
strokeWidth: newStrokeWidth,
|
||||||
points: action.payload,
|
points: action.payload,
|
||||||
clipRect:
|
|
||||||
state.shouldLockToInitialImage && state.initialCanvasImageClipRect
|
|
||||||
? state.initialCanvasImageClipRect
|
|
||||||
: undefined,
|
|
||||||
...newColor,
|
...newColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -375,16 +369,10 @@ export const canvasSlice = createSlice({
|
|||||||
state.layerState.objects.find(isCanvasBaseImage);
|
state.layerState.objects.find(isCanvasBaseImage);
|
||||||
|
|
||||||
if (!initialCanvasImage) return;
|
if (!initialCanvasImage) return;
|
||||||
const { shouldLockToInitialImage, initialCanvasImageClipRect } = state;
|
|
||||||
|
|
||||||
let { width: imageWidth, height: imageHeight } = initialCanvasImage;
|
const { width: imageWidth, height: imageHeight } = initialCanvasImage;
|
||||||
|
|
||||||
if (shouldLockToInitialImage && initialCanvasImageClipRect) {
|
const padding = 0.95;
|
||||||
imageWidth = initialCanvasImageClipRect.clipWidth;
|
|
||||||
imageHeight = initialCanvasImageClipRect.clipHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
const padding = shouldLockToInitialImage ? 1 : 0.95;
|
|
||||||
|
|
||||||
const newScale = calculateScale(
|
const newScale = calculateScale(
|
||||||
containerWidth,
|
containerWidth,
|
||||||
@ -395,12 +383,8 @@ export const canvasSlice = createSlice({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const newDimensions = {
|
const newDimensions = {
|
||||||
width: shouldLockToInitialImage
|
width: Math.floor(containerWidth),
|
||||||
? Math.floor(imageWidth * newScale)
|
height: Math.floor(containerHeight),
|
||||||
: Math.floor(containerWidth),
|
|
||||||
height: shouldLockToInitialImage
|
|
||||||
? Math.floor(imageHeight * newScale)
|
|
||||||
: Math.floor(containerHeight),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const newCoordinates = calculateCoordinates(
|
const newCoordinates = calculateCoordinates(
|
||||||
@ -416,7 +400,7 @@ export const canvasSlice = createSlice({
|
|||||||
if (!_.isEqual(state.stageDimensions, newDimensions)) {
|
if (!_.isEqual(state.stageDimensions, newDimensions)) {
|
||||||
state.minimumStageScale = newScale;
|
state.minimumStageScale = newScale;
|
||||||
state.stageScale = newScale;
|
state.stageScale = newScale;
|
||||||
state.stageCoordinates = newCoordinates;
|
state.stageCoordinates = floorCoordinates(newCoordinates);
|
||||||
state.stageDimensions = newDimensions;
|
state.stageDimensions = newDimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,23 +424,16 @@ export const canvasSlice = createSlice({
|
|||||||
const { contentRect } = action.payload;
|
const { contentRect } = action.payload;
|
||||||
|
|
||||||
const baseCanvasImage = state.layerState.objects.find(isCanvasBaseImage);
|
const baseCanvasImage = state.layerState.objects.find(isCanvasBaseImage);
|
||||||
const { shouldLockToInitialImage, initialCanvasImageClipRect } = state;
|
|
||||||
if (!baseCanvasImage) return;
|
if (!baseCanvasImage) return;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
stageDimensions: { width: stageWidth, height: stageHeight },
|
stageDimensions: { width: stageWidth, height: stageHeight },
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
let { x, y, width, height } = contentRect;
|
const { x, y, width, height } = contentRect;
|
||||||
|
|
||||||
if (shouldLockToInitialImage && initialCanvasImageClipRect) {
|
const padding = 0.95;
|
||||||
x = initialCanvasImageClipRect.clipX;
|
|
||||||
y = initialCanvasImageClipRect.clipY;
|
|
||||||
width = initialCanvasImageClipRect.clipWidth;
|
|
||||||
height = initialCanvasImageClipRect.clipHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
const padding = shouldLockToInitialImage ? 1 : 0.95;
|
|
||||||
const newScale = calculateScale(
|
const newScale = calculateScale(
|
||||||
stageWidth,
|
stageWidth,
|
||||||
stageHeight,
|
stageHeight,
|
||||||
@ -515,39 +492,36 @@ export const canvasSlice = createSlice({
|
|||||||
|
|
||||||
state.futureLayerStates = [];
|
state.futureLayerStates = [];
|
||||||
},
|
},
|
||||||
setShouldLockToInitialImage: (state, action: PayloadAction<boolean>) => {
|
|
||||||
state.shouldLockToInitialImage = action.payload;
|
|
||||||
},
|
|
||||||
fitBoundingBoxToStage: (state) => {
|
fitBoundingBoxToStage: (state) => {
|
||||||
const { boundingBoxDimensions, boundingBoxCoordinates, stageDimensions } =
|
const {
|
||||||
state;
|
boundingBoxDimensions,
|
||||||
|
boundingBoxCoordinates,
|
||||||
|
stageDimensions,
|
||||||
|
stageScale,
|
||||||
|
} = state;
|
||||||
|
const scaledStageWidth = stageDimensions.width / stageScale;
|
||||||
|
const scaledStageHeight = stageDimensions.height / stageScale;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
boundingBoxCoordinates.x < 0 ||
|
boundingBoxCoordinates.x < 0 ||
|
||||||
boundingBoxCoordinates.x + boundingBoxDimensions.width >
|
boundingBoxCoordinates.x + boundingBoxDimensions.width >
|
||||||
stageDimensions.width ||
|
scaledStageWidth ||
|
||||||
boundingBoxCoordinates.y < 0 ||
|
boundingBoxCoordinates.y < 0 ||
|
||||||
boundingBoxCoordinates.y + boundingBoxDimensions.height >
|
boundingBoxCoordinates.y + boundingBoxDimensions.height >
|
||||||
stageDimensions.height
|
scaledStageHeight
|
||||||
) {
|
) {
|
||||||
const newBoundingBoxDimensions = {
|
const newBoundingBoxDimensions = {
|
||||||
width: roundDownToMultiple(
|
width: roundDownToMultiple(_.clamp(scaledStageWidth, 64, 512), 64),
|
||||||
_.clamp(stageDimensions.width, 64, 512),
|
height: roundDownToMultiple(_.clamp(scaledStageHeight, 64, 512), 64),
|
||||||
64
|
|
||||||
),
|
|
||||||
height: roundDownToMultiple(
|
|
||||||
_.clamp(stageDimensions.height, 64, 512),
|
|
||||||
64
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const newBoundingBoxCoordinates = {
|
const newBoundingBoxCoordinates = {
|
||||||
x: roundToMultiple(
|
x: roundToMultiple(
|
||||||
stageDimensions.width / 2 - newBoundingBoxDimensions.width / 2,
|
scaledStageWidth / 2 - newBoundingBoxDimensions.width / 2,
|
||||||
64
|
64
|
||||||
),
|
),
|
||||||
y: roundToMultiple(
|
y: roundToMultiple(
|
||||||
stageDimensions.height / 2 - newBoundingBoxDimensions.height / 2,
|
scaledStageHeight / 2 - newBoundingBoxDimensions.height / 2,
|
||||||
64
|
64
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@ -611,7 +585,6 @@ export const {
|
|||||||
prevStagingAreaImage,
|
prevStagingAreaImage,
|
||||||
commitStagingAreaImage,
|
commitStagingAreaImage,
|
||||||
discardStagedImages,
|
discardStagedImages,
|
||||||
setShouldLockToInitialImage,
|
|
||||||
resizeAndScaleCanvas,
|
resizeAndScaleCanvas,
|
||||||
resizeCanvas,
|
resizeCanvas,
|
||||||
resetCanvasView,
|
resetCanvasView,
|
||||||
|
@ -8,13 +8,6 @@ export type CanvasDrawingTool = 'brush' | 'eraser';
|
|||||||
|
|
||||||
export type CanvasTool = CanvasDrawingTool | 'move';
|
export type CanvasTool = CanvasDrawingTool | 'move';
|
||||||
|
|
||||||
export type ClipRect = {
|
|
||||||
clipX: number;
|
|
||||||
clipY: number;
|
|
||||||
clipWidth: number;
|
|
||||||
clipHeight: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Dimensions = {
|
export type Dimensions = {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
@ -25,7 +18,6 @@ export type CanvasAnyLine = {
|
|||||||
tool: CanvasDrawingTool;
|
tool: CanvasDrawingTool;
|
||||||
strokeWidth: number;
|
strokeWidth: number;
|
||||||
points: number[];
|
points: number[];
|
||||||
clipRect: ClipRect | undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CanvasImage = {
|
export type CanvasImage = {
|
||||||
@ -86,7 +78,6 @@ export interface CanvasState {
|
|||||||
doesCanvasNeedScaling: boolean;
|
doesCanvasNeedScaling: boolean;
|
||||||
eraserSize: number;
|
eraserSize: number;
|
||||||
futureLayerStates: CanvasLayerState[];
|
futureLayerStates: CanvasLayerState[];
|
||||||
initialCanvasImageClipRect?: ClipRect;
|
|
||||||
inpaintReplace: number;
|
inpaintReplace: number;
|
||||||
intermediateImage?: InvokeAI.Image;
|
intermediateImage?: InvokeAI.Image;
|
||||||
isCanvasInitialized: boolean;
|
isCanvasInitialized: boolean;
|
||||||
@ -107,7 +98,6 @@ export interface CanvasState {
|
|||||||
shouldAutoSave: boolean;
|
shouldAutoSave: boolean;
|
||||||
shouldDarkenOutsideBoundingBox: boolean;
|
shouldDarkenOutsideBoundingBox: boolean;
|
||||||
shouldLockBoundingBox: boolean;
|
shouldLockBoundingBox: boolean;
|
||||||
shouldLockToInitialImage: boolean;
|
|
||||||
shouldPreserveMaskedArea: boolean;
|
shouldPreserveMaskedArea: boolean;
|
||||||
shouldShowBoundingBox: boolean;
|
shouldShowBoundingBox: boolean;
|
||||||
shouldShowBrush: boolean;
|
shouldShowBrush: boolean;
|
||||||
|
@ -47,13 +47,6 @@ export const setInitialCanvasImage = (
|
|||||||
};
|
};
|
||||||
state.futureLayerStates = [];
|
state.futureLayerStates = [];
|
||||||
|
|
||||||
state.initialCanvasImageClipRect = {
|
|
||||||
clipX: 0,
|
|
||||||
clipY: 0,
|
|
||||||
clipWidth: image.width,
|
|
||||||
clipHeight: image.height,
|
|
||||||
};
|
|
||||||
|
|
||||||
state.isCanvasInitialized = false;
|
state.isCanvasInitialized = false;
|
||||||
state.doesCanvasNeedScaling = true;
|
state.doesCanvasNeedScaling = true;
|
||||||
};
|
};
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { IRect } from 'konva/lib/types';
|
|
||||||
|
|
||||||
const layerToDataURL = (
|
const layerToDataURL = (layer: Konva.Layer, stageScale: number) => {
|
||||||
layer: Konva.Layer,
|
|
||||||
stageScale: number,
|
|
||||||
boundingBox?: IRect
|
|
||||||
) => {
|
|
||||||
const tempScale = layer.scale();
|
const tempScale = layer.scale();
|
||||||
|
|
||||||
const relativeClientRect = layer.getClientRect({
|
const relativeClientRect = layer.getClientRect({
|
||||||
@ -20,21 +15,12 @@ const layerToDataURL = (
|
|||||||
|
|
||||||
const { x, y, width, height } = layer.getClientRect();
|
const { x, y, width, height } = layer.getClientRect();
|
||||||
|
|
||||||
const scaledBoundingBox = boundingBox
|
const dataURL = layer.toDataURL({
|
||||||
? {
|
|
||||||
x: Math.round(boundingBox.x / stageScale),
|
|
||||||
y: Math.round(boundingBox.y / stageScale),
|
|
||||||
width: Math.round(boundingBox.width / stageScale),
|
|
||||||
height: Math.round(boundingBox.height / stageScale),
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
x: Math.round(x),
|
x: Math.round(x),
|
||||||
y: Math.round(y),
|
y: Math.round(y),
|
||||||
width: Math.round(width),
|
width: Math.round(width),
|
||||||
height: Math.round(height),
|
height: Math.round(height),
|
||||||
};
|
});
|
||||||
|
|
||||||
const dataURL = layer.toDataURL(scaledBoundingBox);
|
|
||||||
|
|
||||||
// Unscale the canvas
|
// Unscale the canvas
|
||||||
layer.scale(tempScale);
|
layer.scale(tempScale);
|
||||||
|
@ -22,24 +22,12 @@ export const mergeAndUploadCanvas = createAsyncThunk(
|
|||||||
const state = getState() as RootState;
|
const state = getState() as RootState;
|
||||||
|
|
||||||
const stageScale = state.canvas.stageScale;
|
const stageScale = state.canvas.stageScale;
|
||||||
const clipRect = state.canvas.initialCanvasImageClipRect;
|
|
||||||
|
|
||||||
const boundingBox =
|
|
||||||
state.canvas.shouldLockToInitialImage && clipRect && saveToGallery
|
|
||||||
? {
|
|
||||||
x: clipRect.clipX,
|
|
||||||
y: clipRect.clipY,
|
|
||||||
width: clipRect.clipWidth,
|
|
||||||
height: clipRect.clipHeight,
|
|
||||||
}
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if (!canvasImageLayerRef.current) return;
|
if (!canvasImageLayerRef.current) return;
|
||||||
|
|
||||||
const { dataURL, boundingBox: originalBoundingBox } = layerToDataURL(
|
const { dataURL, boundingBox: originalBoundingBox } = layerToDataURL(
|
||||||
canvasImageLayerRef.current,
|
canvasImageLayerRef.current,
|
||||||
stageScale,
|
stageScale
|
||||||
boundingBox
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!dataURL) return;
|
if (!dataURL) return;
|
||||||
|
@ -38,7 +38,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
setDoesCanvasNeedScaling,
|
setDoesCanvasNeedScaling,
|
||||||
setInitialCanvasImage,
|
setInitialCanvasImage,
|
||||||
setShouldLockToInitialImage,
|
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { GalleryState } from './gallerySlice';
|
import { GalleryState } from './gallerySlice';
|
||||||
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
import { activeTabNameSelector } from 'features/options/optionsSelectors';
|
||||||
@ -317,31 +316,10 @@ const CurrentImageButtons = () => {
|
|||||||
const handleClickShowImageDetails = () =>
|
const handleClickShowImageDetails = () =>
|
||||||
dispatch(setShouldShowImageDetails(!shouldShowImageDetails));
|
dispatch(setShouldShowImageDetails(!shouldShowImageDetails));
|
||||||
|
|
||||||
const handleSendToInpainting = () => {
|
const handleSendToCanvas = () => {
|
||||||
if (!currentImage) return;
|
if (!currentImage) return;
|
||||||
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
||||||
|
|
||||||
dispatch(setShouldLockToInitialImage(true));
|
|
||||||
dispatch(setInitialCanvasImage(currentImage));
|
|
||||||
dispatch(setDoesCanvasNeedScaling(true));
|
|
||||||
|
|
||||||
if (activeTabName !== 'unifiedCanvas') {
|
|
||||||
dispatch(setActiveTab('unifiedCanvas'));
|
|
||||||
}
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: 'Sent to Unified Canvas',
|
|
||||||
status: 'success',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSendToOutpainting = () => {
|
|
||||||
if (!currentImage) return;
|
|
||||||
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
|
||||||
|
|
||||||
dispatch(setShouldLockToInitialImage(false));
|
|
||||||
dispatch(setInitialCanvasImage(currentImage));
|
dispatch(setInitialCanvasImage(currentImage));
|
||||||
dispatch(setDoesCanvasNeedScaling(true));
|
dispatch(setDoesCanvasNeedScaling(true));
|
||||||
|
|
||||||
@ -393,17 +371,10 @@ const CurrentImageButtons = () => {
|
|||||||
</IAIButton>
|
</IAIButton>
|
||||||
<IAIButton
|
<IAIButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
onClick={handleSendToInpainting}
|
onClick={handleSendToCanvas}
|
||||||
leftIcon={<FaShare />}
|
leftIcon={<FaShare />}
|
||||||
>
|
>
|
||||||
Send to Inpainting
|
Send to Unified Canvas
|
||||||
</IAIButton>
|
|
||||||
<IAIButton
|
|
||||||
size={'sm'}
|
|
||||||
onClick={handleSendToOutpainting}
|
|
||||||
leftIcon={<FaShare />}
|
|
||||||
>
|
|
||||||
Send to Outpainting
|
|
||||||
</IAIButton>
|
</IAIButton>
|
||||||
<IAIButton
|
<IAIButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
|
@ -25,7 +25,6 @@ import * as ContextMenu from '@radix-ui/react-context-menu';
|
|||||||
import {
|
import {
|
||||||
setDoesCanvasNeedScaling,
|
setDoesCanvasNeedScaling,
|
||||||
setInitialCanvasImage,
|
setInitialCanvasImage,
|
||||||
setShouldLockToInitialImage,
|
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { hoverableImageSelector } from './gallerySliceSelectors';
|
import { hoverableImageSelector } from './gallerySliceSelectors';
|
||||||
|
|
||||||
@ -96,10 +95,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSendToInpainting = () => {
|
const handleSendToCanvas = () => {
|
||||||
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
||||||
|
|
||||||
dispatch(setShouldLockToInitialImage(true));
|
|
||||||
dispatch(setInitialCanvasImage(image));
|
dispatch(setInitialCanvasImage(image));
|
||||||
dispatch(setDoesCanvasNeedScaling(true));
|
dispatch(setDoesCanvasNeedScaling(true));
|
||||||
|
|
||||||
@ -108,26 +106,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: 'Sent to Inpainting',
|
title: 'Sent to Unified Canvas',
|
||||||
status: 'success',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSendToOutpainting = () => {
|
|
||||||
if (isLightBoxOpen) dispatch(setIsLightBoxOpen(false));
|
|
||||||
|
|
||||||
dispatch(setShouldLockToInitialImage(false));
|
|
||||||
dispatch(setInitialCanvasImage(image));
|
|
||||||
dispatch(setDoesCanvasNeedScaling(true));
|
|
||||||
|
|
||||||
if (activeTabName !== 'unifiedCanvas') {
|
|
||||||
dispatch(setActiveTab('unifiedCanvas'));
|
|
||||||
}
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: 'Sent to Outpainting',
|
|
||||||
status: 'success',
|
status: 'success',
|
||||||
duration: 2500,
|
duration: 2500,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
@ -257,11 +236,8 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
<ContextMenu.Item onClickCapture={handleSendToImageToImage}>
|
<ContextMenu.Item onClickCapture={handleSendToImageToImage}>
|
||||||
Send to Image To Image
|
Send to Image To Image
|
||||||
</ContextMenu.Item>
|
</ContextMenu.Item>
|
||||||
<ContextMenu.Item onClickCapture={handleSendToInpainting}>
|
<ContextMenu.Item onClickCapture={handleSendToCanvas}>
|
||||||
Send to Inpainting
|
Send to Unified Canvas
|
||||||
</ContextMenu.Item>
|
|
||||||
<ContextMenu.Item onClickCapture={handleSendToOutpainting}>
|
|
||||||
Send to Outpainting
|
|
||||||
</ContextMenu.Item>
|
</ContextMenu.Item>
|
||||||
<DeleteImageModal image={image}>
|
<DeleteImageModal image={image}>
|
||||||
<ContextMenu.Item data-warning>Delete Image</ContextMenu.Item>
|
<ContextMenu.Item data-warning>Delete Image</ContextMenu.Item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user