mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): add dimensions to params slice
This commit is contained in:
@@ -1,9 +1,22 @@
|
||||
import type { PayloadAction, Selector } from '@reduxjs/toolkit';
|
||||
import { createSelector, createSlice } from '@reduxjs/toolkit';
|
||||
import type { PersistConfig, RootState } from 'app/store/store';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { clamp } from 'es-toolkit/compat';
|
||||
import type { ParamsState, RgbaColor } from 'features/controlLayers/store/types';
|
||||
import { getInitialParamsState } from 'features/controlLayers/store/types';
|
||||
import type { AspectRatioID, ParamsState, RgbaColor } from 'features/controlLayers/store/types';
|
||||
import {
|
||||
ASPECT_RATIO_MAP,
|
||||
CHATGPT_ASPECT_RATIOS,
|
||||
DEFAULT_ASPECT_RATIO_CONFIG,
|
||||
FLUX_KONTEXT_ASPECT_RATIOS,
|
||||
getInitialParamsState,
|
||||
IMAGEN_ASPECT_RATIOS,
|
||||
isChatGPT4oAspectRatioID,
|
||||
isFluxKontextAspectRatioID,
|
||||
isImagenAspectRatioID,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { calculateNewSize } from 'features/controlLayers/util/getScaledBoundingBoxDimensions';
|
||||
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
|
||||
import type {
|
||||
ParameterCanvasCoherenceMode,
|
||||
@@ -23,6 +36,7 @@ import type {
|
||||
ParameterT5EncoderModel,
|
||||
ParameterVAEModel,
|
||||
} from 'features/parameters/types/parameterSchemas';
|
||||
import { getGridSize, getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models';
|
||||
import { isNonRefinerMainModelConfig } from 'services/api/types';
|
||||
|
||||
@@ -186,6 +200,129 @@ export const paramsSlice = createSlice({
|
||||
setCanvasCoherenceMinDenoise: (state, action: PayloadAction<number>) => {
|
||||
state.canvasCoherenceMinDenoise = action.payload;
|
||||
},
|
||||
|
||||
//#region Dimensions
|
||||
widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
|
||||
const { width, updateAspectRatio, clamp } = action.payload;
|
||||
const gridSize = getGridSize(state.model?.base);
|
||||
state.dimensions.rect.width = clamp ? Math.max(roundDownToMultiple(width, gridSize), 64) : width;
|
||||
|
||||
if (state.dimensions.aspectRatio.isLocked) {
|
||||
state.dimensions.rect.height = roundToMultiple(
|
||||
state.dimensions.rect.width / state.dimensions.aspectRatio.value,
|
||||
gridSize
|
||||
);
|
||||
}
|
||||
|
||||
if (updateAspectRatio || !state.dimensions.aspectRatio.isLocked) {
|
||||
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
|
||||
state.dimensions.aspectRatio.id = 'Free';
|
||||
state.dimensions.aspectRatio.isLocked = false;
|
||||
}
|
||||
},
|
||||
heightChanged: (state, action: PayloadAction<{ height: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
|
||||
const { height, updateAspectRatio, clamp } = action.payload;
|
||||
const gridSize = getGridSize(state.model?.base);
|
||||
state.dimensions.rect.height = clamp ? Math.max(roundDownToMultiple(height, gridSize), 64) : height;
|
||||
|
||||
if (state.dimensions.aspectRatio.isLocked) {
|
||||
state.dimensions.rect.width = roundToMultiple(
|
||||
state.dimensions.rect.height * state.dimensions.aspectRatio.value,
|
||||
gridSize
|
||||
);
|
||||
}
|
||||
|
||||
if (updateAspectRatio || !state.dimensions.aspectRatio.isLocked) {
|
||||
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
|
||||
state.dimensions.aspectRatio.id = 'Free';
|
||||
state.dimensions.aspectRatio.isLocked = false;
|
||||
}
|
||||
},
|
||||
aspectRatioLockToggled: (state) => {
|
||||
state.dimensions.aspectRatio.isLocked = !state.dimensions.aspectRatio.isLocked;
|
||||
},
|
||||
aspectRatioIdChanged: (state, action: PayloadAction<{ id: AspectRatioID }>) => {
|
||||
const { id } = action.payload;
|
||||
state.dimensions.aspectRatio.id = id;
|
||||
if (id === 'Free') {
|
||||
state.dimensions.aspectRatio.isLocked = false;
|
||||
} else if ((state.model?.base === 'imagen3' || state.model?.base === 'imagen4') && isImagenAspectRatioID(id)) {
|
||||
const { width, height } = IMAGEN_ASPECT_RATIOS[id];
|
||||
state.dimensions.rect.width = width;
|
||||
state.dimensions.rect.height = height;
|
||||
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
|
||||
state.dimensions.aspectRatio.isLocked = true;
|
||||
} else if (state.model?.base === 'chatgpt-4o' && isChatGPT4oAspectRatioID(id)) {
|
||||
const { width, height } = CHATGPT_ASPECT_RATIOS[id];
|
||||
state.dimensions.rect.width = width;
|
||||
state.dimensions.rect.height = height;
|
||||
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
|
||||
state.dimensions.aspectRatio.isLocked = true;
|
||||
} else if (state.model?.base === 'flux-kontext' && isFluxKontextAspectRatioID(id)) {
|
||||
const { width, height } = FLUX_KONTEXT_ASPECT_RATIOS[id];
|
||||
state.dimensions.rect.width = width;
|
||||
state.dimensions.rect.height = height;
|
||||
state.dimensions.aspectRatio.value = state.dimensions.rect.width / state.dimensions.rect.height;
|
||||
state.dimensions.aspectRatio.isLocked = true;
|
||||
} else {
|
||||
state.dimensions.aspectRatio.isLocked = true;
|
||||
state.dimensions.aspectRatio.value = ASPECT_RATIO_MAP[id].ratio;
|
||||
const { width, height } = calculateNewSize(
|
||||
state.dimensions.aspectRatio.value,
|
||||
state.dimensions.rect.width * state.dimensions.rect.height,
|
||||
state.model?.base
|
||||
);
|
||||
state.dimensions.rect.width = width;
|
||||
state.dimensions.rect.height = height;
|
||||
}
|
||||
},
|
||||
dimensionsSwapped: (state) => {
|
||||
state.dimensions.aspectRatio.value = 1 / state.dimensions.aspectRatio.value;
|
||||
if (state.dimensions.aspectRatio.id === 'Free') {
|
||||
const newWidth = state.dimensions.rect.height;
|
||||
const newHeight = state.dimensions.rect.width;
|
||||
state.dimensions.rect.width = newWidth;
|
||||
state.dimensions.rect.height = newHeight;
|
||||
} else {
|
||||
const { width, height } = calculateNewSize(
|
||||
state.dimensions.aspectRatio.value,
|
||||
state.dimensions.rect.width * state.dimensions.rect.height,
|
||||
state.model?.base
|
||||
);
|
||||
state.dimensions.rect.width = width;
|
||||
state.dimensions.rect.height = height;
|
||||
state.dimensions.aspectRatio.id = ASPECT_RATIO_MAP[state.dimensions.aspectRatio.id].inverseID;
|
||||
}
|
||||
},
|
||||
sizeOptimized: (state) => {
|
||||
const optimalDimension = getOptimalDimension(state.model?.base);
|
||||
if (state.dimensions.aspectRatio.isLocked) {
|
||||
const { width, height } = calculateNewSize(
|
||||
state.dimensions.aspectRatio.value,
|
||||
optimalDimension * optimalDimension,
|
||||
state.model?.base
|
||||
);
|
||||
state.dimensions.rect.width = width;
|
||||
state.dimensions.rect.height = height;
|
||||
} else {
|
||||
state.dimensions.aspectRatio = deepClone(DEFAULT_ASPECT_RATIO_CONFIG);
|
||||
state.dimensions.rect.width = optimalDimension;
|
||||
state.dimensions.rect.height = optimalDimension;
|
||||
}
|
||||
},
|
||||
syncedToOptimalDimension: (state) => {
|
||||
const optimalDimension = getOptimalDimension(state.model?.base);
|
||||
|
||||
if (!getIsSizeOptimal(state.dimensions.rect.width, state.dimensions.rect.height, state.model?.base)) {
|
||||
const bboxDims = calculateNewSize(
|
||||
state.dimensions.aspectRatio.value,
|
||||
optimalDimension * optimalDimension,
|
||||
state.model?.base
|
||||
);
|
||||
state.dimensions.rect.width = bboxDims.width;
|
||||
state.dimensions.rect.height = bboxDims.height;
|
||||
}
|
||||
},
|
||||
paramsReset: (state) => resetState(state),
|
||||
},
|
||||
});
|
||||
@@ -249,6 +386,16 @@ export const {
|
||||
setRefinerNegativeAestheticScore,
|
||||
setRefinerStart,
|
||||
modelChanged,
|
||||
|
||||
// Dimensions
|
||||
widthChanged,
|
||||
heightChanged,
|
||||
aspectRatioLockToggled,
|
||||
aspectRatioIdChanged,
|
||||
dimensionsSwapped,
|
||||
sizeOptimized,
|
||||
syncedToOptimalDimension,
|
||||
|
||||
paramsReset,
|
||||
} = paramsSlice.actions;
|
||||
|
||||
|
||||
@@ -512,6 +512,16 @@ const zBboxState = z.object({
|
||||
modelBase: zMainModelBase,
|
||||
});
|
||||
|
||||
const zDimensionsState = z.object({
|
||||
rect: z.object({
|
||||
x: z.number().int(),
|
||||
y: z.number().int(),
|
||||
width: zParameterImageDimension,
|
||||
height: zParameterImageDimension,
|
||||
}),
|
||||
aspectRatio: zAspectRatioConfig,
|
||||
});
|
||||
|
||||
const zParamsState = z.object({
|
||||
maskBlur: z.number().default(16),
|
||||
maskBlurMethod: zParameterMaskBlurMethod.default('box'),
|
||||
@@ -560,6 +570,10 @@ const zParamsState = z.object({
|
||||
clipLEmbedModel: zParameterCLIPLEmbedModel.nullable().default(null),
|
||||
clipGEmbedModel: zParameterCLIPGEmbedModel.nullable().default(null),
|
||||
controlLora: zParameterControlLoRAModel.nullable().default(null),
|
||||
dimensions: zDimensionsState.default({
|
||||
rect: { x: 0, y: 0, width: 512, height: 512 },
|
||||
aspectRatio: DEFAULT_ASPECT_RATIO_CONFIG,
|
||||
}),
|
||||
});
|
||||
export type ParamsState = z.infer<typeof zParamsState>;
|
||||
const INITIAL_PARAMS_STATE = zParamsState.parse({});
|
||||
|
||||
Reference in New Issue
Block a user