fix(ui): fix canvas bbox interactions

This commit is contained in:
psychedelicious 2024-01-02 14:40:41 +11:00 committed by Kent Keirsey
parent 93880223e6
commit 61c10a7ca8
4 changed files with 67 additions and 76 deletions

View File

@ -1,7 +1,17 @@
export const roundDownToMultiple = (num: number, multiple: number): number => {
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 => {
return Math.round(num / multiple) * multiple;
};
export const roundToMultipleMin = (num: number, multiple: number): number => {
return Math.max(multiple, roundToMultiple(num, multiple));
};

View File

@ -154,13 +154,12 @@ const IAICanvas = () => {
if (!containerRef.current) {
return;
}
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.contentBoxSize) {
const { width, height } = entry.contentRect;
dispatch(canvasResized({ width, height }));
}
const resizeObserver = new ResizeObserver(() => {
if (!containerRef.current) {
return;
}
const { width, height } = containerRef.current.getBoundingClientRect();
dispatch(canvasResized({ width, height }));
});
resizeObserver.observe(containerRef.current);

View File

@ -3,10 +3,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { $shift } from 'common/hooks/useGlobalModifiers';
import {
roundDownToMultiple,
roundToMultiple,
} from 'common/util/roundDownToMultiple';
import { roundToMultiple, roundToMultipleMin } from 'common/util/roundDownToMultiple';
import {
$isDrawing,
$isMovingBoundingBox,
@ -16,6 +13,7 @@ import {
setIsTransformingBoundingBox,
} from 'features/canvas/store/canvasNanostore';
import {
aspectRatioChanged,
CANVAS_GRID_SIZE_COARSE,
CANVAS_GRID_SIZE_FINE,
setBoundingBoxCoordinates,
@ -34,17 +32,16 @@ const borderDash = [4, 4];
const boundingBoxPreviewSelector = createMemoizedSelector(
[stateSelector],
({ canvas, generation }) => {
({ canvas }) => {
const {
boundingBoxCoordinates,
boundingBoxDimensions,
stageScale,
tool,
shouldSnapToGrid,
aspectRatio,
} = canvas;
const { aspectRatio } = generation;
return {
boundingBoxCoordinates,
boundingBoxDimensions,
@ -98,9 +95,13 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
[gridSize, stageScale]
);
useHotkeys('N', () => {
dispatch(setShouldSnapToGrid(!shouldSnapToGrid));
});
useHotkeys(
'N',
() => {
dispatch(setShouldSnapToGrid(!shouldSnapToGrid));
},
[shouldSnapToGrid]
);
const handleOnDragMove = useCallback(
(e: KonvaEventObject<DragEvent>) => {
@ -144,7 +145,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
}
const rect = shapeRef.current;
const scaleX = rect.scaleX();
const scaleY = rect.scaleY();
@ -155,40 +156,52 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
const x = Math.round(rect.x());
const y = Math.round(rect.y());
if (aspectRatio) {
const newHeight = roundToMultiple(width / aspectRatio.value, gridSize);
if (aspectRatio.isLocked) {
const newHeight = roundToMultipleMin(width / aspectRatio.value, gridSize);
dispatch(
setBoundingBoxDimensions({
width: width,
width: roundToMultipleMin(width, gridSize),
height: newHeight,
})
);
} else {
dispatch(
setBoundingBoxDimensions({
width,
height,
width: roundToMultipleMin(width, gridSize),
height: roundToMultipleMin(height, gridSize),
})
);
dispatch(
aspectRatioChanged({
isLocked: false,
id: 'Free',
value: width / height,
})
);
}
dispatch(
setBoundingBoxCoordinates({
x: shouldSnapToGrid ? roundDownToMultiple(x, gridSize) : x,
y: shouldSnapToGrid ? roundDownToMultiple(y, gridSize) : y,
x: shouldSnapToGrid ? roundToMultiple(x, gridSize) : x,
y: shouldSnapToGrid ? roundToMultiple(y, gridSize) : y,
})
);
// Reset the scale now that the coords/dimensions have been un-scaled
rect.scaleX(1);
rect.scaleY(1);
}, [aspectRatio, dispatch, shouldSnapToGrid, gridSize]);
}, [
aspectRatio.isLocked,
aspectRatio.value,
dispatch,
shouldSnapToGrid,
gridSize,
]);
const anchorDragBoundFunc = useCallback(
(
oldPos: Vector2d, // old absolute position of anchor point
newPos: Vector2d, // new absolute position (potentially) of anchor point
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_e: MouseEvent
) => {
/**
@ -208,8 +221,8 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
const offsetY = oldPos.y % scaledStep;
const newCoordinates = {
x: roundDownToMultiple(newPos.x, scaledStep) + offsetX,
y: roundDownToMultiple(newPos.y, scaledStep) + offsetY,
x: roundToMultiple(newPos.x, scaledStep) + offsetX,
y: roundToMultiple(newPos.y, scaledStep) + offsetY,
};
return newCoordinates;
@ -287,9 +300,19 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
onTransformEnd={handleEndedTransforming}
ref={shapeRef}
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}
x={boundingBoxCoordinates.x}
y={boundingBoxCoordinates.y}
@ -316,6 +339,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
onTransformEnd={handleEndedTransforming}
ref={transformerRef}
rotateEnabled={false}
shiftBehavior="none"
/>
</Group>
);

View File

@ -29,11 +29,7 @@ import type {
CanvasTool,
Dimensions,
} from './canvasTypes';
import {
isCanvasAnyLine,
isCanvasBaseImage,
isCanvasMaskLine,
} from './canvasTypes';
import { isCanvasAnyLine, isCanvasMaskLine } from './canvasTypes';
/**
* The maximum history length to keep in the past/future layer states.
@ -482,48 +478,10 @@ export const canvasSlice = createSlice({
state,
action: PayloadAction<{ width: number; height: number }>
) => {
const { width, height } = action.payload;
const newStageDimensions = {
width: Math.floor(width),
height: Math.floor(height),
state.stageDimensions = {
width: Math.floor(action.payload.width),
height: Math.floor(action.payload.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: (
state,