mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): fix canvas bbox interactions
This commit is contained in:
parent
93880223e6
commit
61c10a7ca8
@ -1,7 +1,17 @@
|
|||||||
export const roundDownToMultiple = (num: number, multiple: number): number => {
|
export const roundDownToMultiple = (num: number, multiple: number): number => {
|
||||||
return Math.floor(num / multiple) * multiple;
|
return Math.floor(num / multiple) * multiple;
|
||||||
};
|
};
|
||||||
|
export const roundDownToMultipleMin = (
|
||||||
|
num: number,
|
||||||
|
multiple: number
|
||||||
|
): number => {
|
||||||
|
return Math.max(multiple, Math.floor(num / multiple) * multiple);
|
||||||
|
};
|
||||||
|
|
||||||
export const roundToMultiple = (num: number, multiple: number): number => {
|
export const roundToMultiple = (num: number, multiple: number): number => {
|
||||||
return Math.round(num / multiple) * multiple;
|
return Math.round(num / multiple) * multiple;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const roundToMultipleMin = (num: number, multiple: number): number => {
|
||||||
|
return Math.max(multiple, roundToMultiple(num, multiple));
|
||||||
|
};
|
||||||
|
@ -154,13 +154,12 @@ const IAICanvas = () => {
|
|||||||
if (!containerRef.current) {
|
if (!containerRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const resizeObserver = new ResizeObserver((entries) => {
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
for (const entry of entries) {
|
if (!containerRef.current) {
|
||||||
if (entry.contentBoxSize) {
|
return;
|
||||||
const { width, height } = entry.contentRect;
|
|
||||||
dispatch(canvasResized({ width, height }));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const { width, height } = containerRef.current.getBoundingClientRect();
|
||||||
|
dispatch(canvasResized({ width, height }));
|
||||||
});
|
});
|
||||||
|
|
||||||
resizeObserver.observe(containerRef.current);
|
resizeObserver.observe(containerRef.current);
|
||||||
|
@ -3,10 +3,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { $shift } from 'common/hooks/useGlobalModifiers';
|
import { $shift } from 'common/hooks/useGlobalModifiers';
|
||||||
import {
|
import { roundToMultiple, roundToMultipleMin } from 'common/util/roundDownToMultiple';
|
||||||
roundDownToMultiple,
|
|
||||||
roundToMultiple,
|
|
||||||
} from 'common/util/roundDownToMultiple';
|
|
||||||
import {
|
import {
|
||||||
$isDrawing,
|
$isDrawing,
|
||||||
$isMovingBoundingBox,
|
$isMovingBoundingBox,
|
||||||
@ -16,6 +13,7 @@ import {
|
|||||||
setIsTransformingBoundingBox,
|
setIsTransformingBoundingBox,
|
||||||
} from 'features/canvas/store/canvasNanostore';
|
} from 'features/canvas/store/canvasNanostore';
|
||||||
import {
|
import {
|
||||||
|
aspectRatioChanged,
|
||||||
CANVAS_GRID_SIZE_COARSE,
|
CANVAS_GRID_SIZE_COARSE,
|
||||||
CANVAS_GRID_SIZE_FINE,
|
CANVAS_GRID_SIZE_FINE,
|
||||||
setBoundingBoxCoordinates,
|
setBoundingBoxCoordinates,
|
||||||
@ -34,17 +32,16 @@ const borderDash = [4, 4];
|
|||||||
|
|
||||||
const boundingBoxPreviewSelector = createMemoizedSelector(
|
const boundingBoxPreviewSelector = createMemoizedSelector(
|
||||||
[stateSelector],
|
[stateSelector],
|
||||||
({ canvas, generation }) => {
|
({ canvas }) => {
|
||||||
const {
|
const {
|
||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
stageScale,
|
stageScale,
|
||||||
tool,
|
tool,
|
||||||
shouldSnapToGrid,
|
shouldSnapToGrid,
|
||||||
|
aspectRatio,
|
||||||
} = canvas;
|
} = canvas;
|
||||||
|
|
||||||
const { aspectRatio } = generation;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
@ -98,9 +95,13 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
[gridSize, stageScale]
|
[gridSize, stageScale]
|
||||||
);
|
);
|
||||||
|
|
||||||
useHotkeys('N', () => {
|
useHotkeys(
|
||||||
dispatch(setShouldSnapToGrid(!shouldSnapToGrid));
|
'N',
|
||||||
});
|
() => {
|
||||||
|
dispatch(setShouldSnapToGrid(!shouldSnapToGrid));
|
||||||
|
},
|
||||||
|
[shouldSnapToGrid]
|
||||||
|
);
|
||||||
|
|
||||||
const handleOnDragMove = useCallback(
|
const handleOnDragMove = useCallback(
|
||||||
(e: KonvaEventObject<DragEvent>) => {
|
(e: KonvaEventObject<DragEvent>) => {
|
||||||
@ -144,7 +145,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const rect = shapeRef.current;
|
const rect = shapeRef.current;
|
||||||
|
|
||||||
const scaleX = rect.scaleX();
|
const scaleX = rect.scaleX();
|
||||||
const scaleY = rect.scaleY();
|
const scaleY = rect.scaleY();
|
||||||
|
|
||||||
@ -155,40 +156,52 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
const x = Math.round(rect.x());
|
const x = Math.round(rect.x());
|
||||||
const y = Math.round(rect.y());
|
const y = Math.round(rect.y());
|
||||||
|
|
||||||
if (aspectRatio) {
|
if (aspectRatio.isLocked) {
|
||||||
const newHeight = roundToMultiple(width / aspectRatio.value, gridSize);
|
const newHeight = roundToMultipleMin(width / aspectRatio.value, gridSize);
|
||||||
dispatch(
|
dispatch(
|
||||||
setBoundingBoxDimensions({
|
setBoundingBoxDimensions({
|
||||||
width: width,
|
width: roundToMultipleMin(width, gridSize),
|
||||||
height: newHeight,
|
height: newHeight,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
dispatch(
|
dispatch(
|
||||||
setBoundingBoxDimensions({
|
setBoundingBoxDimensions({
|
||||||
width,
|
width: roundToMultipleMin(width, gridSize),
|
||||||
height,
|
height: roundToMultipleMin(height, gridSize),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
dispatch(
|
||||||
|
aspectRatioChanged({
|
||||||
|
isLocked: false,
|
||||||
|
id: 'Free',
|
||||||
|
value: width / height,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
setBoundingBoxCoordinates({
|
setBoundingBoxCoordinates({
|
||||||
x: shouldSnapToGrid ? roundDownToMultiple(x, gridSize) : x,
|
x: shouldSnapToGrid ? roundToMultiple(x, gridSize) : x,
|
||||||
y: shouldSnapToGrid ? roundDownToMultiple(y, gridSize) : y,
|
y: shouldSnapToGrid ? roundToMultiple(y, gridSize) : 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);
|
||||||
}, [aspectRatio, dispatch, shouldSnapToGrid, gridSize]);
|
}, [
|
||||||
|
aspectRatio.isLocked,
|
||||||
|
aspectRatio.value,
|
||||||
|
dispatch,
|
||||||
|
shouldSnapToGrid,
|
||||||
|
gridSize,
|
||||||
|
]);
|
||||||
|
|
||||||
const anchorDragBoundFunc = useCallback(
|
const anchorDragBoundFunc = useCallback(
|
||||||
(
|
(
|
||||||
oldPos: Vector2d, // old absolute position of anchor point
|
oldPos: Vector2d, // old absolute position of anchor point
|
||||||
newPos: Vector2d, // new absolute position (potentially) of anchor point
|
newPos: Vector2d, // new absolute position (potentially) of anchor point
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
_e: MouseEvent
|
_e: MouseEvent
|
||||||
) => {
|
) => {
|
||||||
/**
|
/**
|
||||||
@ -208,8 +221,8 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
const offsetY = oldPos.y % scaledStep;
|
const offsetY = oldPos.y % scaledStep;
|
||||||
|
|
||||||
const newCoordinates = {
|
const newCoordinates = {
|
||||||
x: roundDownToMultiple(newPos.x, scaledStep) + offsetX,
|
x: roundToMultiple(newPos.x, scaledStep) + offsetX,
|
||||||
y: roundDownToMultiple(newPos.y, scaledStep) + offsetY,
|
y: roundToMultiple(newPos.y, scaledStep) + offsetY,
|
||||||
};
|
};
|
||||||
|
|
||||||
return newCoordinates;
|
return newCoordinates;
|
||||||
@ -287,9 +300,19 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
onTransformEnd={handleEndedTransforming}
|
onTransformEnd={handleEndedTransforming}
|
||||||
ref={shapeRef}
|
ref={shapeRef}
|
||||||
stroke={
|
stroke={
|
||||||
isMouseOverBoundingBoxOutline ? 'rgba(255,255,255,0.7)' : 'white'
|
isMouseOverBoundingBoxOutline ||
|
||||||
|
isMovingBoundingBox ||
|
||||||
|
isTransformingBoundingBox
|
||||||
|
? 'rgba(255,255,255,0.5)'
|
||||||
|
: 'white'
|
||||||
|
}
|
||||||
|
strokeWidth={
|
||||||
|
isMouseOverBoundingBoxOutline ||
|
||||||
|
isMovingBoundingBox ||
|
||||||
|
isTransformingBoundingBox
|
||||||
|
? 6 / stageScale
|
||||||
|
: 1 / stageScale
|
||||||
}
|
}
|
||||||
strokeWidth={(isMouseOverBoundingBoxOutline ? 8 : 1) / stageScale}
|
|
||||||
width={boundingBoxDimensions.width}
|
width={boundingBoxDimensions.width}
|
||||||
x={boundingBoxCoordinates.x}
|
x={boundingBoxCoordinates.x}
|
||||||
y={boundingBoxCoordinates.y}
|
y={boundingBoxCoordinates.y}
|
||||||
@ -316,6 +339,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
onTransformEnd={handleEndedTransforming}
|
onTransformEnd={handleEndedTransforming}
|
||||||
ref={transformerRef}
|
ref={transformerRef}
|
||||||
rotateEnabled={false}
|
rotateEnabled={false}
|
||||||
|
shiftBehavior="none"
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
@ -29,11 +29,7 @@ import type {
|
|||||||
CanvasTool,
|
CanvasTool,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
} from './canvasTypes';
|
} from './canvasTypes';
|
||||||
import {
|
import { isCanvasAnyLine, isCanvasMaskLine } from './canvasTypes';
|
||||||
isCanvasAnyLine,
|
|
||||||
isCanvasBaseImage,
|
|
||||||
isCanvasMaskLine,
|
|
||||||
} from './canvasTypes';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum history length to keep in the past/future layer states.
|
* The maximum history length to keep in the past/future layer states.
|
||||||
@ -482,48 +478,10 @@ export const canvasSlice = createSlice({
|
|||||||
state,
|
state,
|
||||||
action: PayloadAction<{ width: number; height: number }>
|
action: PayloadAction<{ width: number; height: number }>
|
||||||
) => {
|
) => {
|
||||||
const { width, height } = action.payload;
|
state.stageDimensions = {
|
||||||
const newStageDimensions = {
|
width: Math.floor(action.payload.width),
|
||||||
width: Math.floor(width),
|
height: Math.floor(action.payload.height),
|
||||||
height: Math.floor(height),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
state.stageDimensions = newStageDimensions;
|
|
||||||
|
|
||||||
if (!state.layerState.objects.find(isCanvasBaseImage)) {
|
|
||||||
const newScale = calculateScale(
|
|
||||||
newStageDimensions.width,
|
|
||||||
newStageDimensions.height,
|
|
||||||
512,
|
|
||||||
512,
|
|
||||||
STAGE_PADDING_PERCENTAGE
|
|
||||||
);
|
|
||||||
|
|
||||||
const newCoordinates = calculateCoordinates(
|
|
||||||
newStageDimensions.width,
|
|
||||||
newStageDimensions.height,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
512,
|
|
||||||
512,
|
|
||||||
newScale
|
|
||||||
);
|
|
||||||
|
|
||||||
const newBoundingBoxDimensions = { width: 512, height: 512 };
|
|
||||||
|
|
||||||
state.stageScale = newScale;
|
|
||||||
|
|
||||||
state.stageCoordinates = newCoordinates;
|
|
||||||
state.boundingBoxCoordinates = { x: 0, y: 0 };
|
|
||||||
state.boundingBoxDimensions = newBoundingBoxDimensions;
|
|
||||||
|
|
||||||
if (state.boundingBoxScaleMethod === 'auto') {
|
|
||||||
const scaledDimensions = getScaledBoundingBoxDimensions(
|
|
||||||
newBoundingBoxDimensions
|
|
||||||
);
|
|
||||||
state.scaledBoundingBoxDimensions = scaledDimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
resetCanvasView: (
|
resetCanvasView: (
|
||||||
state,
|
state,
|
||||||
|
Loading…
Reference in New Issue
Block a user