feat(ui): internal state for inpaint mask

This commit is contained in:
psychedelicious 2024-06-20 21:29:17 +10:00
parent 8fc2a1d1cf
commit 712e090134
3 changed files with 126 additions and 9 deletions

View File

@ -6,6 +6,7 @@ import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
import { bboxReducers } from 'features/controlLayers/store/bboxReducers';
import { compositingReducers } from 'features/controlLayers/store/compositingReducers';
import { controlAdaptersReducers } from 'features/controlLayers/store/controlAdaptersReducers';
import { inpaintMaskReducers } from 'features/controlLayers/store/inpaintMaskReducers';
import { ipAdaptersReducers } from 'features/controlLayers/store/ipAdaptersReducers';
import { layersReducers } from 'features/controlLayers/store/layersReducers';
import { lorasReducers } from 'features/controlLayers/store/lorasReducers';
@ -29,17 +30,14 @@ const initialState: CanvasV2State = {
regions: { entities: [] },
loras: [],
inpaintMask: {
id: 'inpaint_mask',
type: 'inpaint_mask',
bbox: null,
bboxNeedsUpdate: false,
fill: {
type: 'color_fill',
color: DEFAULT_RGBA_COLOR,
},
id: 'inpaint_mask',
fill: DEFAULT_RGBA_COLOR,
imageCache: null,
isEnabled: false,
maskObjects: [],
type: 'inpaint_mask',
objects: [],
x: 0,
y: 0,
},
@ -135,6 +133,7 @@ export const canvasV2Slice = createSlice({
...settingsReducers,
...toolReducers,
...bboxReducers,
...inpaintMaskReducers,
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;
@ -314,6 +313,17 @@ export const {
loraWeightChanged,
loraIsEnabledChanged,
loraAllDeleted,
// Inpaint mask
imReset,
imRecalled,
imIsEnabledToggled,
imTranslated,
imBboxChanged,
imImageCacheChanged,
imBrushLineAdded,
imEraserLineAdded,
imLinePointAdded,
imRectAdded,
} = canvasV2Slice.actions;
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;

View File

@ -1 +1,108 @@
import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
import { getBrushLineId, getEraserLineId, getRectShapeId } from 'features/controlLayers/konva/naming';
import type { CanvasV2State, InpaintMaskEntity } from 'features/controlLayers/store/types';
import { imageDTOToImageWithDims } from 'features/controlLayers/store/types';
import type { IRect } from 'konva/lib/types';
import type { ImageDTO } from 'services/api/types';
import { v4 as uuidv4 } from 'uuid';
import type { BrushLineAddedArg, EraserLineAddedArg, PointAddedToLineArg, RectShapeAddedArg, RgbColor } from './types';
import { isLine } from './types';
export const inpaintMaskReducers = {
imReset: (state) => {
state.inpaintMask.objects = [];
state.inpaintMask.bbox = null;
state.inpaintMask.bboxNeedsUpdate = false;
state.inpaintMask.imageCache = null;
},
imRecalled: (state, action: PayloadAction<{ data: InpaintMaskEntity }>) => {
const { data } = action.payload;
state.inpaintMask = data;
state.selectedEntityIdentifier = { type: 'inpaint_mask', id: data.id };
},
imIsEnabledToggled: (state) => {
state.inpaintMask.isEnabled = !state.inpaintMask.isEnabled;
},
imTranslated: (state, action: PayloadAction<{ x: number; y: number }>) => {
const { x, y } = action.payload;
state.inpaintMask.x = x;
state.inpaintMask.y = y;
},
imBboxChanged: (state, action: PayloadAction<{ bbox: IRect | null }>) => {
const { bbox } = action.payload;
state.inpaintMask.bbox = bbox;
state.inpaintMask.bboxNeedsUpdate = false;
},
inpaintMaskFillChanged: (state, action: PayloadAction<{ fill: RgbColor }>) => {
const { fill } = action.payload;
state.inpaintMask.fill = fill;
},
imImageCacheChanged: (state, action: PayloadAction<{ imageDTO: ImageDTO | null }>) => {
const { imageDTO } = action.payload;
state.inpaintMask.imageCache = imageDTO ? imageDTOToImageWithDims(imageDTO) : null;
},
imBrushLineAdded: {
reducer: (state, action: PayloadAction<Omit<BrushLineAddedArg, 'id'> & { lineId: string }>) => {
const { points, lineId, color, width, clip } = action.payload;
state.inpaintMask.objects.push({
id: getBrushLineId(state.inpaintMask.id, lineId),
type: 'brush_line',
points,
strokeWidth: width,
color,
clip,
});
state.inpaintMask.bboxNeedsUpdate = true;
state.inpaintMask.imageCache = null;
},
prepare: (payload: Omit<BrushLineAddedArg, 'id'>) => ({
payload: { ...payload, lineId: uuidv4() },
}),
},
imEraserLineAdded: {
reducer: (state, action: PayloadAction<Omit<EraserLineAddedArg, 'id'> & { lineId: string }>) => {
const { points, lineId, width, clip } = action.payload;
state.inpaintMask.objects.push({
id: getEraserLineId(state.inpaintMask.id, lineId),
type: 'eraser_line',
points,
strokeWidth: width,
clip,
});
state.inpaintMask.bboxNeedsUpdate = true;
state.inpaintMask.imageCache = null;
},
prepare: (payload: Omit<EraserLineAddedArg, 'id'>) => ({
payload: { ...payload, lineId: uuidv4() },
}),
},
imLinePointAdded: (state, action: PayloadAction<Omit<PointAddedToLineArg, 'id'>>) => {
const { point } = action.payload;
const lastObject = state.inpaintMask.objects[state.inpaintMask.objects.length - 1];
if (!lastObject || !isLine(lastObject)) {
return;
}
lastObject.points.push(...point);
state.inpaintMask.bboxNeedsUpdate = true;
state.inpaintMask.imageCache = null;
},
imRectAdded: {
reducer: (state, action: PayloadAction<Omit<RectShapeAddedArg, 'id'> & { rectId: string }>) => {
const { rect, rectId, color } = action.payload;
if (rect.height === 0 || rect.width === 0) {
// Ignore zero-area rectangles
return;
}
state.inpaintMask.objects.push({
type: 'rect_shape',
id: getRectShapeId(state.inpaintMask.id, rectId),
...rect,
color,
});
state.inpaintMask.bboxNeedsUpdate = true;
state.inpaintMask.imageCache = null;
},
prepare: (payload: Omit<RectShapeAddedArg, 'id'>) => ({ payload: { ...payload, rectId: uuidv4() } }),
},
} satisfies SliceCaseReducers<CanvasV2State>;

View File

@ -655,8 +655,8 @@ const zInpaintMaskEntity = z.object({
y: z.number(),
bbox: zRect.nullable(),
bboxNeedsUpdate: z.boolean(),
maskObjects: z.array(zMaskObject),
fill: zFill,
objects: z.array(zMaskObject),
fill: zRgbColor,
imageCache: zImageWithDims.nullable(),
});
export type InpaintMaskEntity = z.infer<typeof zInpaintMaskEntity>;