diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/renderers/objects.ts b/invokeai/frontend/web/src/features/controlLayers/konva/renderers/objects.ts index 9c3ab76cad..5563b7e4d2 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/renderers/objects.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/renderers/objects.ts @@ -19,18 +19,23 @@ import { v4 as uuidv4 } from 'uuid'; * @param name The konva name for the line */ export const createBrushLine = (brushLine: BrushLine, layerObjectGroup: Konva.Group, name: string): Konva.Line => { + const { id, strokeWidth, color, x, y, scaleX, scaleY, rotation } = brushLine; const konvaLine = new Konva.Line({ - id: brushLine.id, - key: brushLine.id, + id, name, - strokeWidth: brushLine.strokeWidth, + strokeWidth, tension: 0, lineCap: 'round', lineJoin: 'round', shadowForStrokeEnabled: false, globalCompositeOperation: 'source-over', listening: false, - stroke: rgbaColorToString(brushLine.color), + stroke: rgbaColorToString(color), + x, + y, + scaleX, + scaleY, + rotation, }); layerObjectGroup.add(konvaLine); return konvaLine; @@ -43,11 +48,11 @@ export const createBrushLine = (brushLine: BrushLine, layerObjectGroup: Konva.Gr * @param name The konva name for the line */ export const createEraserLine = (eraserLine: EraserLine, layerObjectGroup: Konva.Group, name: string): Konva.Line => { + const { id, strokeWidth, x, y, scaleX, scaleY, rotation } = eraserLine; const konvaLine = new Konva.Line({ - id: eraserLine.id, - key: eraserLine.id, + id, name, - strokeWidth: eraserLine.strokeWidth, + strokeWidth, tension: 0, lineCap: 'round', lineJoin: 'round', @@ -55,6 +60,11 @@ export const createEraserLine = (eraserLine: EraserLine, layerObjectGroup: Konva globalCompositeOperation: 'destination-out', listening: false, stroke: rgbaColorToString(DEFAULT_RGBA_COLOR), + x, + y, + scaleX, + scaleY, + rotation, }); layerObjectGroup.add(konvaLine); return konvaLine; @@ -67,14 +77,18 @@ export const createEraserLine = (eraserLine: EraserLine, layerObjectGroup: Konva * @param name The konva name for the rect */ export const createRectShape = (rectShape: RectShape, layerObjectGroup: Konva.Group, name: string): Konva.Rect => { + const { id, x, y, width, height, scaleX, scaleY, rotation } = rectShape; + const konvaRect = new Konva.Rect({ - id: rectShape.id, - key: rectShape.id, + id, name, - x: rectShape.x, - y: rectShape.y, - width: rectShape.width, - height: rectShape.height, + x, + y, + width, + height, + scaleX, + scaleY, + rotation, listening: false, fill: rgbaColorToString(rectShape.color), }); @@ -125,7 +139,20 @@ export const createImageObjectGroup = async ( layerObjectGroup: Konva.Group, name: string ): Promise => { - const konvaImageGroup = new Konva.Group({ id: imageObject.id, name, listening: false }); + const { id, x, y, width, height, scaleX, scaleY, rotation } = imageObject; + + const konvaImageGroup = new Konva.Group({ + id, + x, + y, + width, + height, + scaleX, + scaleY, + rotation, + name, + listening: false, + }); const placeholder = createImagePlaceholderGroup(imageObject); konvaImageGroup.add(placeholder.konvaPlaceholderGroup); layerObjectGroup.add(konvaImageGroup); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts index 25be668117..ec214fd5ce 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts @@ -656,6 +656,11 @@ export const controlLayersSlice = createSlice({ layer.objects.push({ id: getBrushLineId(layer.id, lineUuid), type: 'brush_line', + x: 0, + y: 0, + scaleX: 1, + scaleY: 1, + rotation: 0, // Points must be offset by the layer's x and y coordinates // TODO: Handle this in the event listener? points: [points[0] - layer.x, points[1] - layer.y, points[2] - layer.x, points[3] - layer.y], @@ -685,6 +690,11 @@ export const controlLayersSlice = createSlice({ layer.objects.push({ id: getEraserLineId(layer.id, lineUuid), type: 'eraser_line', + x: 0, + y: 0, + scaleX: 1, + scaleY: 1, + rotation: 0, // Points must be offset by the layer's x and y coordinates // TODO: Handle this in the event listener? points: [points[0] - layer.x, points[1] - layer.y, points[2] - layer.x, points[3] - layer.y], @@ -728,6 +738,9 @@ export const controlLayersSlice = createSlice({ id, x: rect.x - layer.x, y: rect.y - layer.y, + scaleX: 1, + scaleY: 1, + rotation: 0, width: rect.width, height: rect.height, color, @@ -750,6 +763,9 @@ export const controlLayersSlice = createSlice({ id, x: 0, y: 0, + scaleX: 1, + scaleY: 1, + rotation: 0, width, height, image: { width, height, name }, diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index 4e161069d9..73654288da 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -60,8 +60,16 @@ export const DEFAULT_RGBA_COLOR: RgbaColor = { r: 255, g: 255, b: 255, a: 1 }; const zOpacity = z.number().gte(0).lte(1); -const zBrushLine = z.object({ +const zObjectBase = z.object({ id: z.string(), + x: z.number().catch(0), + y: z.number().catch(0), + scaleX: z.number().catch(1), + scaleY: z.number().catch(1), + rotation: z.number().catch(0), +}); + +const zBrushLine = zObjectBase.extend({ type: z.literal('brush_line'), strokeWidth: z.number().min(1), points: zPoints, @@ -69,50 +77,39 @@ const zBrushLine = z.object({ }); export type BrushLine = z.infer; -const zEraserline = z.object({ - id: z.string(), +const zEraserline = zObjectBase.extend({ type: z.literal('eraser_line'), strokeWidth: z.number().min(1), points: zPoints, }); export type EraserLine = z.infer; -const zRectShape = z.object({ - id: z.string(), +const zRectShape = zObjectBase.extend({ type: z.literal('rect_shape'), - x: z.number(), - y: z.number(), width: z.number().min(1), height: z.number().min(1), color: zRgbaColor, }); export type RectShape = z.infer; -const zEllipseShape = z.object({ - id: z.string(), +const zEllipseShape = zObjectBase.extend({ type: z.literal('ellipse_shape'), - x: z.number(), - y: z.number(), width: z.number().min(1), height: z.number().min(1), color: zRgbaColor, }); export type EllipseShape = z.infer; -const zPolygonShape = z.object({ - id: z.string(), +const zPolygonShape = zObjectBase.extend({ type: z.literal('polygon_shape'), points: zPoints, color: zRgbaColor, }); export type PolygonShape = z.infer; -const zImageObject = z.object({ - id: z.string(), +const zImageObject = zObjectBase.extend({ type: z.literal('image'), image: zImageWithDims, - x: z.number(), - y: z.number(), width: z.number().min(1), height: z.number().min(1), }); @@ -179,12 +176,22 @@ const zMaskObject = z ...rest, type: 'brush_line', color: { r: 255, g: 255, b: 255, a: 1 }, + x: 0, + y: 0, + scaleX: 1, + scaleY: 1, + rotation: 0, }; return asBrushline; } else if (tool === 'eraser') { const asEraserLine: EraserLine = { ...rest, type: 'eraser_line', + x: 0, + y: 0, + scaleX: 1, + scaleY: 1, + rotation: 0, }; return asEraserLine; } @@ -193,6 +200,11 @@ const zMaskObject = z ...val, type: 'rect_shape', color: { r: 255, g: 255, b: 255, a: 1 }, + x: 0, + y: 0, + scaleX: 1, + scaleY: 1, + rotation: 0, }; return asRectShape; } else {