mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): buffered rect drawing
This commit is contained in:
parent
908e504a6f
commit
3f6ee1b7a4
@ -6,7 +6,7 @@ import { CanvasRect } from 'features/controlLayers/konva/CanvasRect';
|
||||
import { getNodeBboxFast } from 'features/controlLayers/konva/entityBbox';
|
||||
import { getObjectGroupId, INPAINT_MASK_LAYER_ID } from 'features/controlLayers/konva/naming';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import type { BrushLine, EraserLine, InpaintMaskEntity } from 'features/controlLayers/store/types';
|
||||
import type { BrushLine, EraserLine, InpaintMaskEntity, RectShape } from 'features/controlLayers/store/types';
|
||||
import { isDrawingTool, RGBA_RED } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import { assert } from 'tsafe';
|
||||
@ -21,7 +21,7 @@ export class CanvasInpaintMask {
|
||||
compositingRect: Konva.Rect;
|
||||
transformer: Konva.Transformer;
|
||||
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect>;
|
||||
private drawingBuffer: BrushLine | EraserLine | null;
|
||||
private drawingBuffer: BrushLine | EraserLine | RectShape | null;
|
||||
private inpaintMaskState: InpaintMaskEntity;
|
||||
|
||||
constructor(entity: InpaintMaskEntity, manager: CanvasManager) {
|
||||
@ -71,7 +71,7 @@ export class CanvasInpaintMask {
|
||||
return this.drawingBuffer;
|
||||
}
|
||||
|
||||
async setDrawingBuffer(obj: BrushLine | EraserLine | null) {
|
||||
async setDrawingBuffer(obj: BrushLine | EraserLine | RectShape | null) {
|
||||
this.drawingBuffer = obj;
|
||||
if (this.drawingBuffer) {
|
||||
if (this.drawingBuffer.type === 'brush_line') {
|
||||
@ -91,6 +91,8 @@ export class CanvasInpaintMask {
|
||||
this.manager.stateApi.onBrushLineAdded2({ id: this.id, brushLine: this.drawingBuffer }, 'inpaint_mask');
|
||||
} else if (this.drawingBuffer.type === 'eraser_line') {
|
||||
this.manager.stateApi.onEraserLineAdded2({ id: this.id, eraserLine: this.drawingBuffer }, 'inpaint_mask');
|
||||
} else if (this.drawingBuffer.type === 'rect_shape') {
|
||||
this.manager.stateApi.onRectShapeAdded2({ id: this.id, rectShape: this.drawingBuffer }, 'layer');
|
||||
}
|
||||
this.setDrawingBuffer(null);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasRect } from 'features/controlLayers/konva/CanvasRect';
|
||||
import { getObjectGroupId } from 'features/controlLayers/konva/naming';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import type { BrushLine, EraserLine, LayerEntity } from 'features/controlLayers/store/types';
|
||||
import type { BrushLine, EraserLine, LayerEntity, RectShape } from 'features/controlLayers/store/types';
|
||||
import { isDrawingTool } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import { assert } from 'tsafe';
|
||||
@ -19,7 +19,7 @@ export class CanvasLayer {
|
||||
objectsGroup: Konva.Group;
|
||||
transformer: Konva.Transformer;
|
||||
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect | CanvasImage>;
|
||||
private drawingBuffer: BrushLine | EraserLine | null;
|
||||
private drawingBuffer: BrushLine | EraserLine | RectShape | null;
|
||||
private layerState: LayerEntity;
|
||||
|
||||
constructor(entity: LayerEntity, manager: CanvasManager) {
|
||||
@ -70,7 +70,7 @@ export class CanvasLayer {
|
||||
return this.drawingBuffer;
|
||||
}
|
||||
|
||||
async setDrawingBuffer(obj: BrushLine | EraserLine | null) {
|
||||
async setDrawingBuffer(obj: BrushLine | EraserLine | RectShape | null) {
|
||||
if (obj) {
|
||||
this.drawingBuffer = obj;
|
||||
await this.renderObject(this.drawingBuffer, true);
|
||||
@ -88,6 +88,8 @@ export class CanvasLayer {
|
||||
this.manager.stateApi.onBrushLineAdded2({ id: this.id, brushLine: this.drawingBuffer }, 'layer');
|
||||
} else if (this.drawingBuffer.type === 'eraser_line') {
|
||||
this.manager.stateApi.onEraserLineAdded2({ id: this.id, eraserLine: this.drawingBuffer }, 'layer');
|
||||
} else if (this.drawingBuffer.type === 'rect_shape') {
|
||||
this.manager.stateApi.onRectShapeAdded2({ id: this.id, rectShape: this.drawingBuffer }, 'layer');
|
||||
}
|
||||
this.setDrawingBuffer(null);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { CanvasRect } from 'features/controlLayers/konva/CanvasRect';
|
||||
import { getNodeBboxFast } from 'features/controlLayers/konva/entityBbox';
|
||||
import { getObjectGroupId } from 'features/controlLayers/konva/naming';
|
||||
import { mapId } from 'features/controlLayers/konva/util';
|
||||
import type { BrushLine, EraserLine, RegionEntity } from 'features/controlLayers/store/types';
|
||||
import type { BrushLine, EraserLine, RectShape, RegionEntity } from 'features/controlLayers/store/types';
|
||||
import { isDrawingTool, RGBA_RED } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import { assert } from 'tsafe';
|
||||
@ -21,7 +21,7 @@ export class CanvasRegion {
|
||||
compositingRect: Konva.Rect;
|
||||
transformer: Konva.Transformer;
|
||||
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect>;
|
||||
private drawingBuffer: BrushLine | EraserLine | null;
|
||||
private drawingBuffer: BrushLine | EraserLine | RectShape | null;
|
||||
private regionState: RegionEntity;
|
||||
|
||||
constructor(entity: RegionEntity, manager: CanvasManager) {
|
||||
@ -71,7 +71,7 @@ export class CanvasRegion {
|
||||
return this.drawingBuffer;
|
||||
}
|
||||
|
||||
async setDrawingBuffer(obj: BrushLine | EraserLine | null) {
|
||||
async setDrawingBuffer(obj: BrushLine | EraserLine | RectShape | null) {
|
||||
this.drawingBuffer = obj;
|
||||
if (this.drawingBuffer) {
|
||||
if (this.drawingBuffer.type === 'brush_line') {
|
||||
@ -90,6 +90,8 @@ export class CanvasRegion {
|
||||
this.manager.stateApi.onBrushLineAdded2({ id: this.id, brushLine: this.drawingBuffer }, 'regional_guidance');
|
||||
} else if (this.drawingBuffer.type === 'eraser_line') {
|
||||
this.manager.stateApi.onEraserLineAdded2({ id: this.id, eraserLine: this.drawingBuffer }, 'regional_guidance');
|
||||
} else if (this.drawingBuffer.type === 'rect_shape') {
|
||||
this.manager.stateApi.onRectShapeAdded2({ id: this.id, rectShape: this.drawingBuffer }, 'layer');
|
||||
}
|
||||
this.setDrawingBuffer(null);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import {
|
||||
imImageCacheChanged,
|
||||
imLinePointAdded,
|
||||
imRectAdded,
|
||||
imRectShapeAdded2,
|
||||
imScaled,
|
||||
imTranslated,
|
||||
layerBboxChanged,
|
||||
@ -35,6 +36,7 @@ import {
|
||||
layerImageCacheChanged,
|
||||
layerLinePointAdded,
|
||||
layerRectAdded,
|
||||
layerRectShapeAdded2,
|
||||
layerScaled,
|
||||
layerTranslated,
|
||||
rgBboxChanged,
|
||||
@ -45,6 +47,7 @@ import {
|
||||
rgImageCacheChanged,
|
||||
rgLinePointAdded,
|
||||
rgRectAdded,
|
||||
rgRectShapeAdded2,
|
||||
rgScaled,
|
||||
rgTranslated,
|
||||
toolBufferChanged,
|
||||
@ -59,6 +62,7 @@ import type {
|
||||
EraserLineAddedArg,
|
||||
PointAddedToLineArg,
|
||||
PosChangedArg,
|
||||
RectShape,
|
||||
RectShapeAddedArg,
|
||||
ScaleChangedArg,
|
||||
Tool,
|
||||
@ -175,6 +179,16 @@ export class CanvasStateApi {
|
||||
this.store.dispatch(imRectAdded(arg));
|
||||
}
|
||||
};
|
||||
onRectShapeAdded2 = (arg: { id: string; rectShape: RectShape }, entityType: CanvasEntity['type']) => {
|
||||
log.debug('Rect shape added');
|
||||
if (entityType === 'layer') {
|
||||
this.store.dispatch(layerRectShapeAdded2(arg));
|
||||
} else if (entityType === 'regional_guidance') {
|
||||
this.store.dispatch(rgRectShapeAdded2(arg));
|
||||
} else if (entityType === 'inpaint_mask') {
|
||||
this.store.dispatch(imRectShapeAdded2(arg));
|
||||
}
|
||||
};
|
||||
onBboxTransformed = (bbox: IRect) => {
|
||||
log.debug('Generation bbox transformed');
|
||||
this.store.dispatch(bboxChanged(bbox));
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
BRUSH_BORDER_OUTER_COLOR,
|
||||
BRUSH_ERASER_BORDER_WIDTH,
|
||||
} from 'features/controlLayers/konva/constants';
|
||||
import { PREVIEW_RECT_ID } from 'features/controlLayers/konva/naming';
|
||||
import Konva from 'konva';
|
||||
|
||||
export class CanvasTool {
|
||||
@ -23,10 +22,10 @@ export class CanvasTool {
|
||||
innerBorderCircle: Konva.Circle;
|
||||
outerBorderCircle: Konva.Circle;
|
||||
};
|
||||
rect: {
|
||||
group: Konva.Group;
|
||||
fillRect: Konva.Rect;
|
||||
};
|
||||
// rect: {
|
||||
// group: Konva.Group;
|
||||
// fillRect: Konva.Rect;
|
||||
// };
|
||||
|
||||
constructor(manager: CanvasManager) {
|
||||
this.manager = manager;
|
||||
@ -83,17 +82,17 @@ export class CanvasTool {
|
||||
this.eraser.group.add(this.eraser.outerBorderCircle);
|
||||
this.group.add(this.eraser.group);
|
||||
|
||||
// Create the rect preview - this is a rectangle drawn from the last mouse down position to the current cursor position
|
||||
this.rect = {
|
||||
group: new Konva.Group(),
|
||||
fillRect: new Konva.Rect({
|
||||
id: PREVIEW_RECT_ID,
|
||||
listening: false,
|
||||
strokeEnabled: false,
|
||||
}),
|
||||
};
|
||||
this.rect.group.add(this.rect.fillRect);
|
||||
this.group.add(this.rect.group);
|
||||
// // Create the rect preview - this is a rectangle drawn from the last mouse down position to the current cursor position
|
||||
// this.rect = {
|
||||
// group: new Konva.Group(),
|
||||
// fillRect: new Konva.Rect({
|
||||
// id: PREVIEW_RECT_ID,
|
||||
// listening: false,
|
||||
// strokeEnabled: false,
|
||||
// }),
|
||||
// };
|
||||
// this.rect.group.add(this.rect.fillRect);
|
||||
// this.group.add(this.rect.group);
|
||||
}
|
||||
|
||||
scaleTool = () => {
|
||||
@ -189,7 +188,7 @@ export class CanvasTool {
|
||||
|
||||
this.brush.group.visible(true);
|
||||
this.eraser.group.visible(false);
|
||||
this.rect.group.visible(false);
|
||||
// this.rect.group.visible(false);
|
||||
} else if (cursorPos && tool === 'eraser') {
|
||||
const scale = stage.scaleX();
|
||||
// Update the fill circle
|
||||
@ -215,23 +214,23 @@ export class CanvasTool {
|
||||
|
||||
this.brush.group.visible(false);
|
||||
this.eraser.group.visible(true);
|
||||
this.rect.group.visible(false);
|
||||
} else if (cursorPos && lastMouseDownPos && tool === 'rect') {
|
||||
this.rect.fillRect.setAttrs({
|
||||
x: Math.min(cursorPos.x, lastMouseDownPos.x),
|
||||
y: Math.min(cursorPos.y, lastMouseDownPos.y),
|
||||
width: Math.abs(cursorPos.x - lastMouseDownPos.x),
|
||||
height: Math.abs(cursorPos.y - lastMouseDownPos.y),
|
||||
fill: rgbaColorToString(currentFill),
|
||||
visible: true,
|
||||
});
|
||||
this.brush.group.visible(false);
|
||||
this.eraser.group.visible(false);
|
||||
this.rect.group.visible(true);
|
||||
// this.rect.group.visible(false);
|
||||
// } else if (cursorPos && lastMouseDownPos && tool === 'rect') {
|
||||
// this.rect.fillRect.setAttrs({
|
||||
// x: Math.min(cursorPos.x, lastMouseDownPos.x),
|
||||
// y: Math.min(cursorPos.y, lastMouseDownPos.y),
|
||||
// width: Math.abs(cursorPos.x - lastMouseDownPos.x),
|
||||
// height: Math.abs(cursorPos.y - lastMouseDownPos.y),
|
||||
// fill: rgbaColorToString(currentFill),
|
||||
// visible: true,
|
||||
// });
|
||||
// this.brush.group.visible(false);
|
||||
// this.eraser.group.visible(false);
|
||||
// this.rect.group.visible(true);
|
||||
} else {
|
||||
this.brush.group.visible(false);
|
||||
this.eraser.group.visible(false);
|
||||
this.rect.group.visible(false);
|
||||
// this.rect.group.visible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import { clamp } from 'lodash-es';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { BRUSH_SPACING_TARGET_SCALE, CANVAS_SCALE_BY, MAX_CANVAS_SCALE, MIN_CANVAS_SCALE } from './constants';
|
||||
import { getBrushLineId, getEraserLineId } from './naming';
|
||||
import { getBrushLineId, getEraserLineId, getRectShapeId } from './naming';
|
||||
|
||||
/**
|
||||
* Updates the last cursor position atom with the current cursor position, returning the new position or `null` if the
|
||||
@ -267,6 +267,21 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
}
|
||||
setLastAddedPoint(pos);
|
||||
}
|
||||
|
||||
if (toolState.selected === 'rect') {
|
||||
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||
}
|
||||
await selectedEntityAdapter.setDrawingBuffer({
|
||||
id: getRectShapeId(selectedEntityAdapter.id, uuidv4()),
|
||||
type: 'rect_shape',
|
||||
x: pos.x - selectedEntity.x,
|
||||
y: pos.y - selectedEntity.y,
|
||||
width: 0,
|
||||
height: 0,
|
||||
color: getCurrentFill(),
|
||||
});
|
||||
}
|
||||
}
|
||||
manager.preview.tool.render();
|
||||
});
|
||||
@ -284,7 +299,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
isDrawableEntity(selectedEntity) &&
|
||||
selectedEntityAdapter &&
|
||||
isDrawableEntityAdapter(selectedEntityAdapter) &&
|
||||
!getSpaceKey()
|
||||
!getSpaceKey() &&
|
||||
getIsPrimaryMouseDown(e)
|
||||
) {
|
||||
const toolState = getToolState();
|
||||
|
||||
@ -307,22 +323,28 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
}
|
||||
|
||||
if (toolState.selected === 'rect') {
|
||||
const lastMouseDownPos = getLastMouseDownPos();
|
||||
if (lastMouseDownPos) {
|
||||
onRectShapeAdded(
|
||||
{
|
||||
id: selectedEntity.id,
|
||||
rect: {
|
||||
x: Math.min(pos.x - selectedEntity.x, lastMouseDownPos.x - selectedEntity.x),
|
||||
y: Math.min(pos.y - selectedEntity.y, lastMouseDownPos.y - selectedEntity.y),
|
||||
width: Math.abs(pos.x - lastMouseDownPos.x),
|
||||
height: Math.abs(pos.y - lastMouseDownPos.y),
|
||||
},
|
||||
color: getCurrentFill(),
|
||||
},
|
||||
selectedEntity.type
|
||||
);
|
||||
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
||||
if (drawingBuffer?.type === 'rect_shape') {
|
||||
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||
} else {
|
||||
await selectedEntityAdapter.setDrawingBuffer(null);
|
||||
}
|
||||
// const lastMouseDownPos = getLastMouseDownPos();
|
||||
// if (lastMouseDownPos) {
|
||||
// onRectShapeAdded(
|
||||
// {
|
||||
// id: selectedEntity.id,
|
||||
// rect: {
|
||||
// x: Math.min(pos.x - selectedEntity.x, lastMouseDownPos.x - selectedEntity.x),
|
||||
// y: Math.min(pos.y - selectedEntity.y, lastMouseDownPos.y - selectedEntity.y),
|
||||
// width: Math.abs(pos.x - lastMouseDownPos.x),
|
||||
// height: Math.abs(pos.y - lastMouseDownPos.y),
|
||||
// },
|
||||
// color: getCurrentFill(),
|
||||
// },
|
||||
// selectedEntity.type
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
setLastMouseDownPos(null);
|
||||
@ -415,6 +437,19 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
setLastAddedPoint(pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (toolState.selected === 'rect') {
|
||||
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
||||
if (drawingBuffer) {
|
||||
if (drawingBuffer.type === 'rect_shape') {
|
||||
drawingBuffer.width = pos.x - selectedEntity.x - drawingBuffer.x;
|
||||
drawingBuffer.height = pos.y - selectedEntity.y - drawingBuffer.y;
|
||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||
} else {
|
||||
await selectedEntityAdapter.setDrawingBuffer(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.preview.tool.render();
|
||||
});
|
||||
@ -446,6 +481,11 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
drawingBuffer.points.push(pos.x - selectedEntity.x, pos.y - selectedEntity.y);
|
||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||
} else if (toolState.selected === 'rect' && drawingBuffer?.type === 'rect_shape') {
|
||||
drawingBuffer.width = pos.x - selectedEntity.x - drawingBuffer.x;
|
||||
drawingBuffer.height = pos.y - selectedEntity.y - drawingBuffer.y;
|
||||
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,10 +349,13 @@ export const {
|
||||
stagingAreaPreviousImageSelected,
|
||||
layerBrushLineAdded2,
|
||||
layerEraserLineAdded2,
|
||||
layerRectShapeAdded2,
|
||||
rgBrushLineAdded2,
|
||||
rgEraserLineAdded2,
|
||||
rgRectShapeAdded2,
|
||||
imBrushLineAdded2,
|
||||
imEraserLineAdded2,
|
||||
imRectShapeAdded2,
|
||||
} = canvasV2Slice.actions;
|
||||
|
||||
export const selectCanvasV2Slice = (state: RootState) => state.canvasV2;
|
||||
|
@ -5,6 +5,7 @@ import type {
|
||||
CanvasV2State,
|
||||
EraserLine,
|
||||
InpaintMaskEntity,
|
||||
RectShape,
|
||||
ScaleChangedArg,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { imageDTOToImageWithDims, RGBA_RED } from 'features/controlLayers/store/types';
|
||||
@ -99,6 +100,12 @@ export const inpaintMaskReducers = {
|
||||
state.inpaintMask.bboxNeedsUpdate = true;
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
imRectShapeAdded2: (state, action: PayloadAction<{ rectShape: RectShape }>) => {
|
||||
const { rectShape } = action.payload;
|
||||
state.inpaintMask.objects.push(rectShape);
|
||||
state.inpaintMask.bboxNeedsUpdate = true;
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
imEraserLineAdded: {
|
||||
reducer: (state, action: PayloadAction<Omit<EraserLineAddedArg, 'id'> & { lineId: string }>) => {
|
||||
const { points, lineId, width, clip } = action.payload;
|
||||
|
@ -15,6 +15,7 @@ import type {
|
||||
ImageObjectAddedArg,
|
||||
LayerEntity,
|
||||
PointAddedToLineArg,
|
||||
RectShape,
|
||||
RectShapeAddedArg,
|
||||
ScaleChangedArg,
|
||||
} from './types';
|
||||
@ -176,6 +177,17 @@ export const layersReducers = {
|
||||
layer.bboxNeedsUpdate = true;
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
layerRectShapeAdded2: (state, action: PayloadAction<{ id: string; rectShape: RectShape }>) => {
|
||||
const { id, rectShape } = action.payload;
|
||||
const layer = selectLayer(state, id);
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
layer.objects.push(rectShape);
|
||||
layer.bboxNeedsUpdate = true;
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
layerBrushLineAdded: {
|
||||
reducer: (state, action: PayloadAction<BrushLineAddedArg & { lineId: string }>) => {
|
||||
const { id, points, lineId, color, width, clip } = action.payload;
|
||||
|
@ -7,6 +7,7 @@ import type {
|
||||
CLIPVisionModelV2,
|
||||
EraserLine,
|
||||
IPMethodV2,
|
||||
RectShape,
|
||||
ScaleChangedArg,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { imageDTOToImageObject, imageDTOToImageWithDims, RGBA_RED } from 'features/controlLayers/store/types';
|
||||
@ -383,6 +384,17 @@ export const regionsReducers = {
|
||||
rg.bboxNeedsUpdate = true;
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
rgRectShapeAdded2: (state, action: PayloadAction<{ id: string; rectShape: RectShape }>) => {
|
||||
const { id, rectShape } = action.payload;
|
||||
const rg = selectRG(state, id);
|
||||
if (!rg) {
|
||||
return;
|
||||
}
|
||||
|
||||
rg.objects.push(rectShape);
|
||||
rg.bboxNeedsUpdate = true;
|
||||
state.layers.imageCache = null;
|
||||
},
|
||||
rgEraserLineAdded: {
|
||||
reducer: (state, action: PayloadAction<EraserLineAddedArg & { lineId: string }>) => {
|
||||
const { id, points, lineId, width, clip } = action.payload;
|
||||
|
Loading…
Reference in New Issue
Block a user