mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
refactor(ui): merge compositing, params into canvasV2 slice
This commit is contained in:
parent
84fde74331
commit
83a5c87f5e
@ -15,10 +15,8 @@ import { modelManagerV2PersistConfig, modelManagerV2Slice } from 'features/model
|
||||
import { nodesPersistConfig, nodesSlice, nodesUndoableConfig } from 'features/nodes/store/nodesSlice';
|
||||
import { workflowSettingsPersistConfig, workflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice';
|
||||
import { workflowPersistConfig, workflowSlice } from 'features/nodes/store/workflowSlice';
|
||||
import { generationPersistConfig, generationSlice } from 'features/parameters/store/generationSlice';
|
||||
import { upscalePersistConfig, upscaleSlice } from 'features/parameters/store/upscaleSlice';
|
||||
import { queueSlice } from 'features/queue/store/queueSlice';
|
||||
import { sdxlPersistConfig, sdxlSlice } from 'features/sdxl/store/sdxlSlice';
|
||||
import { stylePresetPersistConfig, stylePresetSlice } from 'features/stylePresets/store/stylePresetSlice';
|
||||
import { configSlice } from 'features/system/store/configSlice';
|
||||
import { systemPersistConfig, systemSlice } from 'features/system/store/systemSlice';
|
||||
@ -42,7 +40,7 @@ import { listenerMiddleware } from './middleware/listenerMiddleware';
|
||||
const allReducers = {
|
||||
[api.reducerPath]: api.reducer,
|
||||
[gallerySlice.name]: gallerySlice.reducer,
|
||||
[generationSlice.name]: generationSlice.reducer,
|
||||
// [generationSlice.name]: generationSlice.reducer,
|
||||
[nodesSlice.name]: undoable(nodesSlice.reducer, nodesUndoableConfig),
|
||||
[systemSlice.name]: systemSlice.reducer,
|
||||
[configSlice.name]: configSlice.reducer,
|
||||
@ -52,7 +50,7 @@ const allReducers = {
|
||||
[changeBoardModalSlice.name]: changeBoardModalSlice.reducer,
|
||||
[loraSlice.name]: loraSlice.reducer,
|
||||
[modelManagerV2Slice.name]: modelManagerV2Slice.reducer,
|
||||
[sdxlSlice.name]: sdxlSlice.reducer,
|
||||
// [sdxlSlice.name]: sdxlSlice.reducer,
|
||||
[queueSlice.name]: queueSlice.reducer,
|
||||
[workflowSlice.name]: workflowSlice.reducer,
|
||||
[hrfSlice.name]: hrfSlice.reducer,
|
||||
@ -90,13 +88,13 @@ export type PersistConfig<T = any> = {
|
||||
|
||||
const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
|
||||
[galleryPersistConfig.name]: galleryPersistConfig,
|
||||
[generationPersistConfig.name]: generationPersistConfig,
|
||||
// [generationPersistConfig.name]: generationPersistConfig,
|
||||
[nodesPersistConfig.name]: nodesPersistConfig,
|
||||
[systemPersistConfig.name]: systemPersistConfig,
|
||||
[workflowPersistConfig.name]: workflowPersistConfig,
|
||||
[uiPersistConfig.name]: uiPersistConfig,
|
||||
[dynamicPromptsPersistConfig.name]: dynamicPromptsPersistConfig,
|
||||
[sdxlPersistConfig.name]: sdxlPersistConfig,
|
||||
// [sdxlPersistConfig.name]: sdxlPersistConfig,
|
||||
[loraPersistConfig.name]: loraPersistConfig,
|
||||
[modelManagerV2PersistConfig.name]: modelManagerV2PersistConfig,
|
||||
[hrfPersistConfig.name]: hrfPersistConfig,
|
||||
|
@ -3,15 +3,14 @@ import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { PersistConfig, RootState } from 'app/store/store';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { compositingReducers } from 'features/controlLayers/store/compositingReducers';
|
||||
import { controlAdaptersReducers } from 'features/controlLayers/store/controlAdaptersReducers';
|
||||
import { ipAdaptersReducers } from 'features/controlLayers/store/ipAdaptersReducers';
|
||||
import { layersReducers } from 'features/controlLayers/store/layersReducers';
|
||||
import { paramsReducers } from 'features/controlLayers/store/paramsReducers';
|
||||
import { regionsReducers } from 'features/controlLayers/store/regionsReducers';
|
||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||
import { modelChanged } from 'features/parameters/store/generationSlice';
|
||||
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import type { IRect, Vector2d } from 'konva/lib/types';
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
@ -54,6 +53,46 @@ const initialState: CanvasV2State = {
|
||||
regions: [],
|
||||
layers: [],
|
||||
maskFillOpacity: 0.3,
|
||||
compositing: {
|
||||
maskBlur: 16,
|
||||
maskBlurMethod: 'box',
|
||||
canvasCoherenceMode: 'Gaussian Blur',
|
||||
canvasCoherenceMinDenoise: 0,
|
||||
canvasCoherenceEdgeSize: 16,
|
||||
infillMethod: 'patchmatch',
|
||||
infillTileSize: 32,
|
||||
infillPatchmatchDownscaleSize: 1,
|
||||
infillColorValue: { r: 0, g: 0, b: 0, a: 1 },
|
||||
},
|
||||
params: {
|
||||
cfgScale: 7.5,
|
||||
cfgRescaleMultiplier: 0,
|
||||
img2imgStrength: 0.75,
|
||||
iterations: 1,
|
||||
scheduler: 'euler',
|
||||
seed: 0,
|
||||
shouldRandomizeSeed: true,
|
||||
steps: 50,
|
||||
model: null,
|
||||
vae: null,
|
||||
vaePrecision: 'fp32',
|
||||
seamlessXAxis: false,
|
||||
seamlessYAxis: false,
|
||||
clipSkip: 0,
|
||||
shouldUseCpuNoise: true,
|
||||
positivePrompt: '',
|
||||
negativePrompt: '',
|
||||
positivePrompt2: '',
|
||||
negativePrompt2: '',
|
||||
shouldConcatPrompts: true,
|
||||
refinerModel: null,
|
||||
refinerSteps: 20,
|
||||
refinerCFGScale: 7.5,
|
||||
refinerScheduler: 'euler',
|
||||
refinerPositiveAestheticScore: 6,
|
||||
refinerNegativeAestheticScore: 2.5,
|
||||
refinerStart: 0.8,
|
||||
},
|
||||
};
|
||||
|
||||
export const canvasV2Slice = createSlice({
|
||||
@ -64,6 +103,8 @@ export const canvasV2Slice = createSlice({
|
||||
...ipAdaptersReducers,
|
||||
...controlAdaptersReducers,
|
||||
...regionsReducers,
|
||||
...paramsReducers,
|
||||
...compositingReducers,
|
||||
widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
|
||||
const { width, updateAspectRatio, clamp } = action.payload;
|
||||
state.document.width = clamp ? Math.max(roundDownToMultiple(width, 8), 64) : width;
|
||||
@ -119,22 +160,6 @@ export const canvasV2Slice = createSlice({
|
||||
state.controlAdapters = [];
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder.addCase(modelChanged, (state, action) => {
|
||||
const newModel = action.payload;
|
||||
if (!newModel || action.meta.previousModel?.base === newModel.base) {
|
||||
// Model was cleared or the base didn't change
|
||||
return;
|
||||
}
|
||||
const optimalDimension = getOptimalDimension(newModel);
|
||||
if (getIsSizeOptimal(state.document.width, state.document.height, optimalDimension)) {
|
||||
return;
|
||||
}
|
||||
const { width, height } = calculateNewSize(state.document.aspectRatio.value, optimalDimension * optimalDimension);
|
||||
state.document.width = width;
|
||||
state.document.height = height;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
@ -153,6 +178,7 @@ export const {
|
||||
allEntitiesDeleted,
|
||||
// layers
|
||||
layerAdded,
|
||||
layerRecalled,
|
||||
layerDeleted,
|
||||
layerReset,
|
||||
layerMovedForwardOne,
|
||||
@ -230,6 +256,43 @@ export const {
|
||||
rgEraserLineAdded,
|
||||
rgLinePointAdded,
|
||||
rgRectAdded,
|
||||
// Compositing
|
||||
setInfillMethod,
|
||||
setInfillTileSize,
|
||||
setInfillPatchmatchDownscaleSize,
|
||||
setInfillColorValue,
|
||||
setMaskBlur,
|
||||
setCanvasCoherenceMode,
|
||||
setCanvasCoherenceEdgeSize,
|
||||
setCanvasCoherenceMinDenoise,
|
||||
// Parameters
|
||||
setIterations,
|
||||
setSteps,
|
||||
setCfgScale,
|
||||
setCfgRescaleMultiplier,
|
||||
setScheduler,
|
||||
setSeed,
|
||||
setImg2imgStrength,
|
||||
setSeamlessXAxis,
|
||||
setSeamlessYAxis,
|
||||
setShouldRandomizeSeed,
|
||||
vaeSelected,
|
||||
vaePrecisionChanged,
|
||||
setClipSkip,
|
||||
shouldUseCpuNoiseChanged,
|
||||
positivePromptChanged,
|
||||
negativePromptChanged,
|
||||
positivePrompt2Changed,
|
||||
negativePrompt2Changed,
|
||||
shouldConcatPromptsChanged,
|
||||
refinerModelChanged,
|
||||
setRefinerSteps,
|
||||
setRefinerCFGScale,
|
||||
setRefinerScheduler,
|
||||
setRefinerPositiveAestheticScore,
|
||||
setRefinerNegativeAestheticScore,
|
||||
setRefinerStart,
|
||||
modelChanged,
|
||||
} = canvasV2Slice.actions;
|
||||
|
||||
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;
|
||||
|
@ -0,0 +1,30 @@
|
||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||
import type { CanvasV2State, RgbaColor } from 'features/controlLayers/store/types';
|
||||
import type { ParameterCanvasCoherenceMode } from 'features/parameters/types/parameterSchemas';
|
||||
|
||||
export const compositingReducers = {
|
||||
setInfillMethod: (state, action: PayloadAction<string>) => {
|
||||
state.compositing.infillMethod = action.payload;
|
||||
},
|
||||
setInfillTileSize: (state, action: PayloadAction<number>) => {
|
||||
state.compositing.infillTileSize = action.payload;
|
||||
},
|
||||
setInfillPatchmatchDownscaleSize: (state, action: PayloadAction<number>) => {
|
||||
state.compositing.infillPatchmatchDownscaleSize = action.payload;
|
||||
},
|
||||
setInfillColorValue: (state, action: PayloadAction<RgbaColor>) => {
|
||||
state.compositing.infillColorValue = action.payload;
|
||||
},
|
||||
setMaskBlur: (state, action: PayloadAction<number>) => {
|
||||
state.compositing.maskBlur = action.payload;
|
||||
},
|
||||
setCanvasCoherenceMode: (state, action: PayloadAction<ParameterCanvasCoherenceMode>) => {
|
||||
state.compositing.canvasCoherenceMode = action.payload;
|
||||
},
|
||||
setCanvasCoherenceEdgeSize: (state, action: PayloadAction<number>) => {
|
||||
state.compositing.canvasCoherenceEdgeSize = action.payload;
|
||||
},
|
||||
setCanvasCoherenceMinDenoise: (state, action: PayloadAction<number>) => {
|
||||
state.compositing.canvasCoherenceMinDenoise = action.payload;
|
||||
},
|
||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
@ -0,0 +1,132 @@
|
||||
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
||||
import getScaledBoundingBoxDimensions from 'features/canvas/util/getScaledBoundingBoxDimensions';
|
||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
|
||||
import type {
|
||||
ParameterCFGRescaleMultiplier,
|
||||
ParameterCFGScale,
|
||||
ParameterModel,
|
||||
ParameterPrecision,
|
||||
ParameterScheduler,
|
||||
ParameterSDXLRefinerModel,
|
||||
ParameterVAEModel,
|
||||
} from 'features/parameters/types/parameterSchemas';
|
||||
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import { clamp } from 'lodash-es';
|
||||
|
||||
export const paramsReducers = {
|
||||
setIterations: (state, action: PayloadAction<number>) => {
|
||||
state.params.iterations = action.payload;
|
||||
},
|
||||
setSteps: (state, action: PayloadAction<number>) => {
|
||||
state.params.steps = action.payload;
|
||||
},
|
||||
setCfgScale: (state, action: PayloadAction<ParameterCFGScale>) => {
|
||||
state.params.cfgScale = action.payload;
|
||||
},
|
||||
setCfgRescaleMultiplier: (state, action: PayloadAction<ParameterCFGRescaleMultiplier>) => {
|
||||
state.params.cfgRescaleMultiplier = action.payload;
|
||||
},
|
||||
setScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
|
||||
state.params.scheduler = action.payload;
|
||||
},
|
||||
setSeed: (state, action: PayloadAction<number>) => {
|
||||
state.params.seed = action.payload;
|
||||
state.params.shouldRandomizeSeed = false;
|
||||
},
|
||||
setImg2imgStrength: (state, action: PayloadAction<number>) => {
|
||||
state.params.img2imgStrength = action.payload;
|
||||
},
|
||||
setSeamlessXAxis: (state, action: PayloadAction<boolean>) => {
|
||||
state.params.seamlessXAxis = action.payload;
|
||||
},
|
||||
setSeamlessYAxis: (state, action: PayloadAction<boolean>) => {
|
||||
state.params.seamlessYAxis = action.payload;
|
||||
},
|
||||
setShouldRandomizeSeed: (state, action: PayloadAction<boolean>) => {
|
||||
state.params.shouldRandomizeSeed = action.payload;
|
||||
},
|
||||
modelChanged: (state, action: PayloadAction<{ model: ParameterModel | null; previousModel?: ParameterModel }>) => {
|
||||
const { model, previousModel } = action.payload;
|
||||
state.params.model = model;
|
||||
|
||||
// If the model base changes (e.g. SD1.5 -> SDXL), we need to change a few things
|
||||
if (model === null || previousModel?.base === model.base) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the bbox size to match the new model's optimal size
|
||||
// TODO(psyche): Should we change the document size too?
|
||||
const optimalDimension = getOptimalDimension(model);
|
||||
if (!getIsSizeOptimal(state.document.width, state.document.height, optimalDimension)) {
|
||||
const bboxDims = calculateNewSize(state.document.aspectRatio.value, optimalDimension * optimalDimension);
|
||||
state.bbox.width = bboxDims.width;
|
||||
state.bbox.height = bboxDims.height;
|
||||
|
||||
if (state.scaledBbox.scaleMethod === 'auto') {
|
||||
const scaledBboxDims = getScaledBoundingBoxDimensions(bboxDims, optimalDimension);
|
||||
state.scaledBbox.width = scaledBboxDims.width;
|
||||
state.scaledBbox.height = scaledBboxDims.height;
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp CLIP skip layer count to the bounds of the new model
|
||||
if (model.base === 'sdxl') {
|
||||
// We don't support user-defined CLIP skip for SDXL because it doesn't do anything useful
|
||||
state.params.clipSkip = 0;
|
||||
} else {
|
||||
const { maxClip } = CLIP_SKIP_MAP[model.base];
|
||||
state.params.clipSkip = clamp(state.params.clipSkip, 0, maxClip);
|
||||
}
|
||||
},
|
||||
vaeSelected: (state, action: PayloadAction<ParameterVAEModel | null>) => {
|
||||
// null is a valid VAE!
|
||||
state.params.vae = action.payload;
|
||||
},
|
||||
vaePrecisionChanged: (state, action: PayloadAction<ParameterPrecision>) => {
|
||||
state.params.vaePrecision = action.payload;
|
||||
},
|
||||
setClipSkip: (state, action: PayloadAction<number>) => {
|
||||
state.params.clipSkip = action.payload;
|
||||
},
|
||||
shouldUseCpuNoiseChanged: (state, action: PayloadAction<boolean>) => {
|
||||
state.params.shouldUseCpuNoise = action.payload;
|
||||
},
|
||||
positivePromptChanged: (state, action: PayloadAction<string>) => {
|
||||
state.params.positivePrompt = action.payload;
|
||||
},
|
||||
negativePromptChanged: (state, action: PayloadAction<string>) => {
|
||||
state.params.negativePrompt = action.payload;
|
||||
},
|
||||
positivePrompt2Changed: (state, action: PayloadAction<string>) => {
|
||||
state.params.positivePrompt2 = action.payload;
|
||||
},
|
||||
negativePrompt2Changed: (state, action: PayloadAction<string>) => {
|
||||
state.params.negativePrompt2 = action.payload;
|
||||
},
|
||||
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
|
||||
state.params.shouldConcatPrompts = action.payload;
|
||||
},
|
||||
refinerModelChanged: (state, action: PayloadAction<ParameterSDXLRefinerModel | null>) => {
|
||||
state.params.refinerModel = action.payload;
|
||||
},
|
||||
setRefinerSteps: (state, action: PayloadAction<number>) => {
|
||||
state.params.refinerSteps = action.payload;
|
||||
},
|
||||
setRefinerCFGScale: (state, action: PayloadAction<number>) => {
|
||||
state.params.refinerCFGScale = action.payload;
|
||||
},
|
||||
setRefinerScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
|
||||
state.params.refinerScheduler = action.payload;
|
||||
},
|
||||
setRefinerPositiveAestheticScore: (state, action: PayloadAction<number>) => {
|
||||
state.params.refinerPositiveAestheticScore = action.payload;
|
||||
},
|
||||
setRefinerNegativeAestheticScore: (state, action: PayloadAction<number>) => {
|
||||
state.params.refinerNegativeAestheticScore = action.payload;
|
||||
},
|
||||
setRefinerStart: (state, action: PayloadAction<number>) => {
|
||||
state.params.refinerStart = action.payload;
|
||||
},
|
||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
@ -1,7 +1,26 @@
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||
import type { ParameterHeight, ParameterWidth } from 'features/parameters/types/parameterSchemas';
|
||||
import type {
|
||||
ParameterCanvasCoherenceMode,
|
||||
ParameterCFGRescaleMultiplier,
|
||||
ParameterCFGScale,
|
||||
ParameterHeight,
|
||||
ParameterMaskBlurMethod,
|
||||
ParameterModel,
|
||||
ParameterNegativePrompt,
|
||||
ParameterNegativeStylePromptSDXL,
|
||||
ParameterPositivePrompt,
|
||||
ParameterPositiveStylePromptSDXL,
|
||||
ParameterPrecision,
|
||||
ParameterScheduler,
|
||||
ParameterSDXLRefinerModel,
|
||||
ParameterSeed,
|
||||
ParameterSteps,
|
||||
ParameterStrength,
|
||||
ParameterVAEModel,
|
||||
ParameterWidth,
|
||||
} from 'features/parameters/types/parameterSchemas';
|
||||
import {
|
||||
zAutoNegative,
|
||||
zParameterNegativePrompt,
|
||||
@ -785,6 +804,46 @@ export type CanvasV2State = {
|
||||
ipAdapters: IPAdapterData[];
|
||||
regions: RegionalGuidanceData[];
|
||||
maskFillOpacity: number;
|
||||
compositing: {
|
||||
maskBlur: number;
|
||||
maskBlurMethod: ParameterMaskBlurMethod;
|
||||
canvasCoherenceMode: ParameterCanvasCoherenceMode;
|
||||
canvasCoherenceMinDenoise: ParameterStrength;
|
||||
canvasCoherenceEdgeSize: number;
|
||||
infillMethod: string;
|
||||
infillTileSize: number;
|
||||
infillPatchmatchDownscaleSize: number;
|
||||
infillColorValue: RgbaColor;
|
||||
};
|
||||
params: {
|
||||
cfgScale: ParameterCFGScale;
|
||||
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
|
||||
img2imgStrength: ParameterStrength;
|
||||
iterations: number;
|
||||
scheduler: ParameterScheduler;
|
||||
seed: ParameterSeed;
|
||||
shouldRandomizeSeed: boolean;
|
||||
steps: ParameterSteps;
|
||||
model: ParameterModel | null;
|
||||
vae: ParameterVAEModel | null;
|
||||
vaePrecision: ParameterPrecision;
|
||||
seamlessXAxis: boolean;
|
||||
seamlessYAxis: boolean;
|
||||
clipSkip: number;
|
||||
shouldUseCpuNoise: boolean;
|
||||
positivePrompt: ParameterPositivePrompt;
|
||||
negativePrompt: ParameterNegativePrompt;
|
||||
positivePrompt2: ParameterPositiveStylePromptSDXL;
|
||||
negativePrompt2: ParameterNegativeStylePromptSDXL;
|
||||
shouldConcatPrompts: boolean;
|
||||
refinerModel: ParameterSDXLRefinerModel | null;
|
||||
refinerSteps: number;
|
||||
refinerCFGScale: number;
|
||||
refinerScheduler: ParameterScheduler;
|
||||
refinerPositiveAestheticScore: number;
|
||||
refinerNegativeAestheticScore: number;
|
||||
refinerStart: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type StageAttrs = { x: number; y: number; width: number; height: number; scale: number };
|
||||
|
Loading…
Reference in New Issue
Block a user