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 => { 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));
};

View File

@ -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);

View File

@ -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>
); );

View File

@ -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,