feat: Add Aspect Ratio To Canvas Bounding Box

This commit is contained in:
blessedcoolant 2023-07-10 20:04:32 +12:00
parent b6fabe5146
commit 15175bb998
5 changed files with 139 additions and 19 deletions

View File

@ -7,7 +7,14 @@ import {
import { IRect, Vector2d } from 'konva/lib/types'; import { IRect, Vector2d } from 'konva/lib/types';
import { clamp, cloneDeep } from 'lodash-es'; import { clamp, cloneDeep } from 'lodash-es';
// //
import {
setActiveTab,
setAspectRatio,
setShouldUseCanvasBetaLayout,
} from 'features/ui/store/uiSlice';
import { RgbaColor } from 'react-colorful'; import { RgbaColor } from 'react-colorful';
import { sessionCanceled } from 'services/api/thunks/session';
import { ImageDTO } from 'services/api/types';
import calculateCoordinates from '../util/calculateCoordinates'; import calculateCoordinates from '../util/calculateCoordinates';
import calculateScale from '../util/calculateScale'; import calculateScale from '../util/calculateScale';
import { STAGE_PADDING_PERCENTAGE } from '../util/constants'; import { STAGE_PADDING_PERCENTAGE } from '../util/constants';
@ -28,13 +35,6 @@ import {
isCanvasBaseImage, isCanvasBaseImage,
isCanvasMaskLine, isCanvasMaskLine,
} from './canvasTypes'; } from './canvasTypes';
import { ImageDTO } from 'services/api/types';
import { sessionCanceled } from 'services/api/thunks/session';
import {
setActiveTab,
setShouldUseCanvasBetaLayout,
} from 'features/ui/store/uiSlice';
import { imageUrlsReceived } from 'services/api/thunks/image';
export const initialLayerState: CanvasLayerState = { export const initialLayerState: CanvasLayerState = {
objects: [], objects: [],
@ -240,6 +240,16 @@ export const canvasSlice = createSlice({
state.scaledBoundingBoxDimensions = scaledDimensions; state.scaledBoundingBoxDimensions = scaledDimensions;
} }
}, },
toggleBoundingBoxDimensions: (state) => {
const [currWidth, currHeight] = [
state.boundingBoxDimensions.width,
state.boundingBoxDimensions.height,
];
state.boundingBoxDimensions = {
width: currHeight,
height: currWidth,
};
},
setBoundingBoxCoordinates: (state, action: PayloadAction<Vector2d>) => { setBoundingBoxCoordinates: (state, action: PayloadAction<Vector2d>) => {
state.boundingBoxCoordinates = floorCoordinates(action.payload); state.boundingBoxCoordinates = floorCoordinates(action.payload);
}, },
@ -864,6 +874,15 @@ export const canvasSlice = createSlice({
builder.addCase(setActiveTab, (state, action) => { builder.addCase(setActiveTab, (state, action) => {
state.doesCanvasNeedScaling = true; state.doesCanvasNeedScaling = true;
}); });
builder.addCase(setAspectRatio, (state, action) => {
const ratio = action.payload;
if (ratio) {
state.boundingBoxDimensions.height = roundToMultiple(
state.boundingBoxDimensions.width / ratio,
8
);
}
});
// builder.addCase(imageUrlsReceived.fulfilled, (state, action) => { // builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
// const { image_name, image_url, thumbnail_url } = action.payload; // const { image_name, image_url, thumbnail_url } = action.payload;
@ -912,6 +931,7 @@ export const {
setBoundingBoxDimensions, setBoundingBoxDimensions,
setBoundingBoxPreviewFill, setBoundingBoxPreviewFill,
setBoundingBoxScaleMethod, setBoundingBoxScaleMethod,
toggleBoundingBoxDimensions,
setBrushColor, setBrushColor,
setBrushSize, setBrushSize,
setCanvasContainerDimensions, setCanvasContainerDimensions,

View File

@ -2,22 +2,26 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import { import {
canvasSelector, canvasSelector,
isStagingSelector, isStagingSelector,
} from 'features/canvas/store/canvasSelectors'; } from 'features/canvas/store/canvasSelectors';
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createSelector( const selector = createSelector(
[canvasSelector, isStagingSelector], [canvasSelector, isStagingSelector, uiSelector],
(canvas, isStaging) => { (canvas, isStaging, ui) => {
const { boundingBoxDimensions } = canvas; const { boundingBoxDimensions } = canvas;
const { aspectRatio } = ui;
return { return {
boundingBoxDimensions, boundingBoxDimensions,
isStaging, isStaging,
aspectRatio,
}; };
}, },
defaultSelectorOptions defaultSelectorOptions
@ -25,7 +29,8 @@ const selector = createSelector(
const ParamBoundingBoxWidth = () => { const ParamBoundingBoxWidth = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { boundingBoxDimensions, isStaging } = useAppSelector(selector); const { boundingBoxDimensions, isStaging, aspectRatio } =
useAppSelector(selector);
const { t } = useTranslation(); const { t } = useTranslation();
@ -36,6 +41,15 @@ const ParamBoundingBoxWidth = () => {
height: Math.floor(v), height: Math.floor(v),
}) })
); );
if (aspectRatio) {
const newWidth = roundToMultiple(v * aspectRatio, 8);
dispatch(
setBoundingBoxDimensions({
width: newWidth,
height: Math.floor(v),
})
);
}
}; };
const handleResetHeight = () => { const handleResetHeight = () => {
@ -45,6 +59,15 @@ const ParamBoundingBoxWidth = () => {
height: Math.floor(512), height: Math.floor(512),
}) })
); );
if (aspectRatio) {
const newWidth = roundToMultiple(512 * aspectRatio, 8);
dispatch(
setBoundingBoxDimensions({
width: newWidth,
height: Math.floor(512),
})
);
}
}; };
return ( return (

View File

@ -0,0 +1,57 @@
import { Flex, Spacer, Text } from '@chakra-ui/react';
import { useAppDispatch } from 'app/store/storeHooks';
import IAIIconButton from 'common/components/IAIIconButton';
import { toggleBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { useTranslation } from 'react-i18next';
import { MdOutlineSwapVert } from 'react-icons/md';
import ParamAspectRatio from '../../Core/ParamAspectRatio';
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
export default function ParamBoundingBoxSize() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
return (
<Flex
sx={{
gap: 2,
p: 4,
borderRadius: 4,
flexDirection: 'column',
w: 'full',
bg: 'base.150',
_dark: {
bg: 'base.750',
},
}}
>
<Flex alignItems="center" gap={2}>
<Text
sx={{
fontSize: 'sm',
width: 'full',
color: 'base.700',
_dark: {
color: 'base.300',
},
}}
>
{t('parameters.aspectRatio')}
</Text>
<Spacer />
<ParamAspectRatio />
<IAIIconButton
tooltip={t('ui.swapSizes')}
aria-label={t('ui.swapSizes')}
size="sm"
icon={<MdOutlineSwapVert />}
fontSize={20}
onClick={() => dispatch(toggleBoundingBoxDimensions())}
/>
</Flex>
<ParamBoundingBoxWidth />
<ParamBoundingBoxHeight />
</Flex>
);
}

View File

@ -2,22 +2,26 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import { import {
canvasSelector, canvasSelector,
isStagingSelector, isStagingSelector,
} from 'features/canvas/store/canvasSelectors'; } from 'features/canvas/store/canvasSelectors';
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { memo } from 'react'; import { memo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const selector = createSelector( const selector = createSelector(
[canvasSelector, isStagingSelector], [canvasSelector, isStagingSelector, uiSelector],
(canvas, isStaging) => { (canvas, isStaging, ui) => {
const { boundingBoxDimensions } = canvas; const { boundingBoxDimensions } = canvas;
const { aspectRatio } = ui;
return { return {
boundingBoxDimensions, boundingBoxDimensions,
isStaging, isStaging,
aspectRatio,
}; };
}, },
defaultSelectorOptions defaultSelectorOptions
@ -25,7 +29,8 @@ const selector = createSelector(
const ParamBoundingBoxWidth = () => { const ParamBoundingBoxWidth = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { boundingBoxDimensions, isStaging } = useAppSelector(selector); const { boundingBoxDimensions, isStaging, aspectRatio } =
useAppSelector(selector);
const { t } = useTranslation(); const { t } = useTranslation();
@ -36,6 +41,15 @@ const ParamBoundingBoxWidth = () => {
width: Math.floor(v), width: Math.floor(v),
}) })
); );
if (aspectRatio) {
const newHeight = roundToMultiple(v / aspectRatio, 8);
dispatch(
setBoundingBoxDimensions({
width: Math.floor(v),
height: newHeight,
})
);
}
}; };
const handleResetWidth = () => { const handleResetWidth = () => {
@ -45,6 +59,15 @@ const ParamBoundingBoxWidth = () => {
width: Math.floor(512), width: Math.floor(512),
}) })
); );
if (aspectRatio) {
const newHeight = roundToMultiple(512 / aspectRatio, 8);
dispatch(
setBoundingBoxDimensions({
width: Math.floor(512),
height: newHeight,
})
);
}
}; };
return ( return (

View File

@ -4,8 +4,7 @@ import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAICollapse from 'common/components/IAICollapse'; import IAICollapse from 'common/components/IAICollapse';
import ParamBoundingBoxHeight from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxHeight'; import ParamBoundingBoxSize from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxSize';
import ParamBoundingBoxWidth from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxWidth';
import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale'; import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale';
import ParamIterations from 'features/parameters/components/Parameters/Core/ParamIterations'; import ParamIterations from 'features/parameters/components/Parameters/Core/ParamIterations';
import ParamModelandVAEandScheduler from 'features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler'; import ParamModelandVAEandScheduler from 'features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler';
@ -51,8 +50,7 @@ const UnifiedCanvasCoreParameters = () => {
<Box pt={2}> <Box pt={2}>
<ParamSeedFull /> <ParamSeedFull />
</Box> </Box>
<ParamBoundingBoxWidth /> <ParamBoundingBoxSize />
<ParamBoundingBoxHeight />
</> </>
) : ( ) : (
<> <>
@ -65,8 +63,7 @@ const UnifiedCanvasCoreParameters = () => {
<Box pt={2}> <Box pt={2}>
<ParamSeedFull /> <ParamSeedFull />
</Box> </Box>
<ParamBoundingBoxWidth /> <ParamBoundingBoxSize />
<ParamBoundingBoxHeight />
</> </>
)} )}
<ImageToImageStrength /> <ImageToImageStrength />