diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts index 85fbac41ec..dcd3d7f70c 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts @@ -58,7 +58,7 @@ export const addStagingListeners = (startAppListening: AppStartListening) => { assert(stagingAreaImage, 'No staged image found to accept'); const { x, y } = state.canvasV2.bbox.rect; - api.dispatch(layerAddedFromStagingArea({ stagingAreaImage, pos: { x, y } })); + api.dispatch(layerAddedFromStagingArea({ stagingAreaImage, position: { x, y } })); api.dispatch(sessionStagingAreaReset()); }, }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HeadsUpDisplay.tsx b/invokeai/frontend/web/src/features/controlLayers/components/HeadsUpDisplay.tsx index 5f3bcab13c..abf799c893 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/HeadsUpDisplay.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/HeadsUpDisplay.tsx @@ -24,10 +24,10 @@ export const HeadsUpDisplay = memo(() => { return ( - + diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasControlAdapter.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasControlAdapter.ts index 8b4208a6a6..4c29b17eb0 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasControlAdapter.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasControlAdapter.ts @@ -42,12 +42,15 @@ export class CanvasControlAdapter { }); this.transformer.on('transformend', () => { this.manager.stateApi.onScaleChanged( - { id: this.id, scale: this.group.scaleX(), x: this.group.x(), y: this.group.y() }, + { id: this.id, scale: this.group.scaleX(), position: { x: this.group.x(), y: this.group.y() } }, 'control_adapter' ); }); this.transformer.on('dragend', () => { - this.manager.stateApi.onPosChanged({ id: this.id, x: this.group.x(), y: this.group.y() }, 'control_adapter'); + this.manager.stateApi.onPosChanged( + { id: this.id, position: { x: this.group.x(), y: this.group.y() } }, + 'control_adapter' + ); }); this.layer.add(this.transformer); @@ -60,8 +63,8 @@ export class CanvasControlAdapter { // Update the layer's position and listening state this.group.setAttrs({ - x: controlAdapterState.x, - y: controlAdapterState.y, + x: controlAdapterState.position.x, + y: controlAdapterState.position.y, scaleX: 1, scaleY: 1, }); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInitialImage.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInitialImage.ts index 868e8a9761..b3aaf24ef2 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInitialImage.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInitialImage.ts @@ -42,7 +42,7 @@ export class CanvasInitialImage { } if (!this.image) { - this.image = await new CanvasImage(this.initialImageState.imageObject, {}); + this.image = await new CanvasImage(this.initialImageState.imageObject); this.objectsGroup.add(this.image.konvaImageGroup); await this.image.update(this.initialImageState.imageObject, true); } else if (!this.image.isLoading && !this.image.isError) { diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMask.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMask.ts index b30dae3f5d..c470b87dd0 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMask.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMask.ts @@ -46,12 +46,15 @@ export class CanvasInpaintMask { }); this.transformer.on('transformend', () => { this.manager.stateApi.onScaleChanged( - { id: this.id, scale: this.group.scaleX(), x: this.group.x(), y: this.group.y() }, + { id: this.id, scale: this.group.scaleX(), position: { x: this.group.x(), y: this.group.y() } }, 'inpaint_mask' ); }); this.transformer.on('dragend', () => { - this.manager.stateApi.onPosChanged({ id: this.id, x: this.group.x(), y: this.group.y() }, 'inpaint_mask'); + this.manager.stateApi.onPosChanged( + { id: this.id, position: { x: this.group.x(), y: this.group.y() } }, + 'inpaint_mask' + ); }); this.layer.add(this.transformer); @@ -103,8 +106,8 @@ export class CanvasInpaintMask { // Update the layer's position and listening state this.group.setAttrs({ - x: inpaintMaskState.x, - y: inpaintMaskState.y, + x: inpaintMaskState.position.x, + y: inpaintMaskState.position.y, scaleX: 1, scaleY: 1, }); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasLayer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasLayer.ts index 49442876f4..d6cb942a18 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasLayer.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasLayer.ts @@ -48,12 +48,12 @@ export class CanvasLayer { }); this.transformer.on('transformend', () => { this.manager.stateApi.onScaleChanged( - { id: this.id, scale: this.group.scaleX(), x: this.group.x(), y: this.group.y() }, + { id: this.id, scale: this.group.scaleX(), position: { x: this.group.x(), y: this.group.y() } }, 'layer' ); }); this.transformer.on('dragend', () => { - this.manager.stateApi.onPosChanged({ id: this.id, x: this.group.x(), y: this.group.y() }, 'layer'); + this.manager.stateApi.onPosChanged({ id: this.id, position: { x: this.group.x(), y: this.group.y() } }, 'layer'); }); this.layer.add(this.transformer); @@ -99,8 +99,8 @@ export class CanvasLayer { // Update the layer's position and listening state this.group.setAttrs({ - x: layerState.x, - y: layerState.y, + x: layerState.position.x, + y: layerState.position.y, scaleX: 1, scaleY: 1, }); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts index 8d2d5b198c..81c0507601 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts @@ -212,10 +212,8 @@ export class CanvasManager { this.stage.width(this.container.offsetWidth); this.stage.height(this.container.offsetHeight); this.stateApi.setStageAttrs({ - x: this.stage.x(), - y: this.stage.y(), - width: this.stage.width(), - height: this.stage.height(), + position: { x: this.stage.x(), y: this.stage.y() }, + dimensions: { width: this.stage.width(), height: this.stage.height() }, scale: this.stage.scaleX(), }); this.background.render(); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegion.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegion.ts index 1a331e0827..2ecbf95978 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegion.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegion.ts @@ -47,12 +47,15 @@ export class CanvasRegion { }); this.transformer.on('transformend', () => { this.manager.stateApi.onScaleChanged( - { id: this.id, scale: this.group.scaleX(), x: this.group.x(), y: this.group.y() }, + { id: this.id, scale: this.group.scaleX(), position: { x: this.group.x(), y: this.group.y() } }, 'regional_guidance' ); }); this.transformer.on('dragend', () => { - this.manager.stateApi.onPosChanged({ id: this.id, x: this.group.x(), y: this.group.y() }, 'regional_guidance'); + this.manager.stateApi.onPosChanged( + { id: this.id, position: { x: this.group.x(), y: this.group.y() } }, + 'regional_guidance' + ); }); this.layer.add(this.transformer); @@ -104,8 +107,8 @@ export class CanvasRegion { // Update the layer's position and listening state this.group.setAttrs({ - x: regionState.x, - y: regionState.y, + x: regionState.position.x, + y: regionState.position.y, scaleX: 1, scaleY: 1, }); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApi.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApi.ts index d40a51c3a6..162789bd2e 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApi.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApi.ts @@ -47,7 +47,7 @@ import type { BrushLine, CanvasEntity, EraserLine, - PosChangedArg, + PositionChangedArg, RectShape, ScaleChangedArg, Tool, @@ -70,7 +70,7 @@ export class CanvasStateApi { return this.store.getState().canvasV2; }; - onPosChanged = (arg: PosChangedArg, entityType: CanvasEntity['type']) => { + onPosChanged = (arg: PositionChangedArg, entityType: CanvasEntity['type']) => { log.debug('onPosChanged'); if (entityType === 'layer') { this.store.dispatch(layerTranslated(arg)); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/events.ts b/invokeai/frontend/web/src/features/controlLayers/konva/events.ts index b6869c4318..a04242695f 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/events.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/events.ts @@ -2,9 +2,9 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getScaledCursorPosition } from 'features/controlLayers/konva/util'; import type { CanvasV2State, + Coordinate, InpaintMaskEntity, LayerEntity, - Coordinate, RegionEntity, Tool, } from 'features/controlLayers/store/types'; @@ -141,15 +141,15 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { if (settings.clipToBbox) { return { - x: bboxRect.x - entity.x, - y: bboxRect.y - entity.y, + x: bboxRect.x - entity.position.x, + y: bboxRect.y - entity.position.y, width: bboxRect.width, height: bboxRect.height, }; } else { return { - x: -stage.x() / stage.scaleX() - entity.x, - y: -stage.y() / stage.scaleY() - entity.y, + x: -stage.x() / stage.scaleX() - entity.position.x, + y: -stage.y() / stage.scaleY() - entity.position.y, width: stage.width() / stage.scaleX(), height: stage.height() / stage.scaleY(), }; @@ -194,8 +194,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { // The last point of the last line is already normalized to the entity's coordinates lastLinePoint.x, lastLinePoint.y, - pos.x - selectedEntity.x, - pos.y - selectedEntity.y, + pos.x - selectedEntity.position.x, + pos.y - selectedEntity.position.y, ], strokeWidth: toolState.brush.width, color: getCurrentFill(), @@ -208,7 +208,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { await selectedEntityAdapter.setDrawingBuffer({ id: getBrushLineId(selectedEntityAdapter.id, uuidv4()), type: 'brush_line', - points: [pos.x - selectedEntity.x, pos.y - selectedEntity.y], + points: [pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y], strokeWidth: toolState.brush.width, color: getCurrentFill(), clip: getClip(selectedEntity), @@ -231,8 +231,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { // The last point of the last line is already normalized to the entity's coordinates lastLinePoint.x, lastLinePoint.y, - pos.x - selectedEntity.x, - pos.y - selectedEntity.y, + pos.x - selectedEntity.position.x, + pos.y - selectedEntity.position.y, ], strokeWidth: toolState.eraser.width, clip: getClip(selectedEntity), @@ -244,7 +244,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { await selectedEntityAdapter.setDrawingBuffer({ id: getEraserLineId(selectedEntityAdapter.id, uuidv4()), type: 'eraser_line', - points: [pos.x - selectedEntity.x, pos.y - selectedEntity.y], + points: [pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y], strokeWidth: toolState.eraser.width, clip: getClip(selectedEntity), }); @@ -259,8 +259,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { await selectedEntityAdapter.setDrawingBuffer({ id: getRectShapeId(selectedEntityAdapter.id, uuidv4()), type: 'rect_shape', - x: pos.x - selectedEntity.x, - y: pos.y - selectedEntity.y, + x: pos.x - selectedEntity.position.x, + y: pos.y - selectedEntity.position.y, width: 0, height: 0, color: getCurrentFill(), @@ -342,7 +342,10 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { if (drawingBuffer?.type === 'brush_line') { const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points)); if (nextPoint) { - drawingBuffer.points.push(nextPoint.x - selectedEntity.x, nextPoint.y - selectedEntity.y); + drawingBuffer.points.push( + nextPoint.x - selectedEntity.position.x, + nextPoint.y - selectedEntity.position.y + ); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer); setLastAddedPoint(nextPoint); } @@ -356,7 +359,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { await selectedEntityAdapter.setDrawingBuffer({ id: getBrushLineId(selectedEntityAdapter.id, uuidv4()), type: 'brush_line', - points: [pos.x - selectedEntity.x, pos.y - selectedEntity.y], + points: [pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y], strokeWidth: toolState.brush.width, color: getCurrentFill(), clip: getClip(selectedEntity), @@ -371,7 +374,10 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { if (drawingBuffer.type === 'eraser_line') { const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points)); if (nextPoint) { - drawingBuffer.points.push(nextPoint.x - selectedEntity.x, nextPoint.y - selectedEntity.y); + drawingBuffer.points.push( + nextPoint.x - selectedEntity.position.x, + nextPoint.y - selectedEntity.position.y + ); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer); setLastAddedPoint(nextPoint); } @@ -385,7 +391,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { await selectedEntityAdapter.setDrawingBuffer({ id: getEraserLineId(selectedEntityAdapter.id, uuidv4()), type: 'eraser_line', - points: [pos.x - selectedEntity.x, pos.y - selectedEntity.y], + points: [pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y], strokeWidth: toolState.eraser.width, clip: getClip(selectedEntity), }); @@ -397,8 +403,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { 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; + drawingBuffer.width = pos.x - selectedEntity.position.x - drawingBuffer.x; + drawingBuffer.height = pos.y - selectedEntity.position.y - drawingBuffer.y; await selectedEntityAdapter.setDrawingBuffer(drawingBuffer); } else { await selectedEntityAdapter.setDrawingBuffer(null); @@ -429,16 +435,16 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { ) { const drawingBuffer = selectedEntityAdapter.getDrawingBuffer(); if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') { - drawingBuffer.points.push(pos.x - selectedEntity.x, pos.y - selectedEntity.y); + drawingBuffer.points.push(pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.y); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer); selectedEntityAdapter.finalizeDrawingBuffer(); } else if (toolState.selected === 'eraser' && drawingBuffer?.type === 'eraser_line') { - drawingBuffer.points.push(pos.x - selectedEntity.x, pos.y - selectedEntity.y); + drawingBuffer.points.push(pos.x - selectedEntity.position.x, pos.y - selectedEntity.position.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; + drawingBuffer.width = pos.x - selectedEntity.position.x - drawingBuffer.x; + drawingBuffer.height = pos.y - selectedEntity.position.y - drawingBuffer.y; await selectedEntityAdapter.setDrawingBuffer(drawingBuffer); selectedEntityAdapter.finalizeDrawingBuffer(); } @@ -484,7 +490,11 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { stage.scaleX(newScale); stage.scaleY(newScale); stage.position(newPos); - setStageAttrs({ ...newPos, width: stage.width(), height: stage.height(), scale: newScale }); + setStageAttrs({ + position: newPos, + dimensions: { width: stage.width(), height: stage.height() }, + scale: newScale, + }); manager.background.render(); } } @@ -494,10 +504,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { //#region dragmove stage.on('dragmove', () => { setStageAttrs({ - x: Math.floor(stage.x()), - y: Math.floor(stage.y()), - width: stage.width(), - height: stage.height(), + position: { x: Math.floor(stage.x()), y: Math.floor(stage.y()) }, + dimensions: { width: stage.width(), height: stage.height() }, scale: stage.scaleX(), }); manager.background.render(); @@ -508,10 +516,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { stage.on('dragend', () => { // Stage position should always be an integer, else we get fractional pixels which are blurry setStageAttrs({ - x: Math.floor(stage.x()), - y: Math.floor(stage.y()), - width: stage.width(), - height: stage.height(), + position: { x: Math.floor(stage.x()), y: Math.floor(stage.y()) }, + dimensions: { width: stage.width(), height: stage.height() }, scale: stage.scaleX(), }); manager.preview.tool.render(); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts index 621d1fda82..30e673cdc3 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasV2Slice.ts @@ -50,8 +50,10 @@ const initialState: CanvasV2State = { imageCache: null, isEnabled: true, objects: [], - x: 0, - y: 0, + position: { + x: 0, + y: 0, + }, }, tool: { selected: 'view', @@ -369,10 +371,8 @@ const migrate = (state: any): any => { // Ephemeral state that does not need to be in redux export const $isPreviewVisible = atom(true); export const $stageAttrs = atom({ - x: 0, - y: 0, - width: 0, - height: 0, + position: { x: 0, y: 0 }, + dimensions: { width: 0, height: 0 }, scale: 0, }); export const $shouldShowStagedImage = atom(true); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts index e82b7aa2ac..0283f4097c 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/controlAdaptersReducers.ts @@ -14,6 +14,7 @@ import type { ControlNetConfig, ControlNetData, Filter, + PositionChangedArg, ProcessorConfig, ScaleChangedArg, T2IAdapterConfig, @@ -35,8 +36,7 @@ export const controlAdaptersReducers = { state.controlAdapters.entities.push({ id, type: 'control_adapter', - x: 0, - y: 0, + position: { x: 0, y: 0 }, bbox: null, bboxNeedsUpdate: false, isEnabled: true, @@ -64,17 +64,16 @@ export const controlAdaptersReducers = { } ca.isEnabled = !ca.isEnabled; }, - caTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => { - const { id, x, y } = action.payload; + caTranslated: (state, action: PayloadAction) => { + const { id, position } = action.payload; const ca = selectCA(state, id); if (!ca) { return; } - ca.x = x; - ca.y = y; + ca.position = position; }, caScaled: (state, action: PayloadAction) => { - const { id, scale, x, y } = action.payload; + const { id, scale, position } = action.payload; const ca = selectCA(state, id); if (!ca) { return; @@ -92,8 +91,7 @@ export const controlAdaptersReducers = { ca.processedImageObject.height *= scale; ca.processedImageObject.width *= scale; } - ca.x = x; - ca.y = y; + ca.position = position; ca.bboxNeedsUpdate = true; state.layers.imageCache = null; }, diff --git a/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts index 0e4c6bfc40..e49552cf2a 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/inpaintMaskReducers.ts @@ -2,6 +2,7 @@ import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit'; import type { BrushLine, CanvasV2State, + Coordinate, EraserLine, InpaintMaskEntity, RectShape, @@ -28,13 +29,12 @@ export const inpaintMaskReducers = { 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; + imTranslated: (state, action: PayloadAction<{ position: Coordinate }>) => { + const { position } = action.payload; + state.inpaintMask.position = position; }, imScaled: (state, action: PayloadAction) => { - const { scale, x, y } = action.payload; + const { scale, position } = action.payload; for (const obj of state.inpaintMask.objects) { if (obj.type === 'brush_line') { obj.points = obj.points.map((point) => point * scale); @@ -49,8 +49,7 @@ export const inpaintMaskReducers = { obj.width *= scale; } } - state.inpaintMask.x = x; - state.inpaintMask.y = y; + state.inpaintMask.position = position; state.inpaintMask.bboxNeedsUpdate = true; state.inpaintMask.imageCache = null; }, diff --git a/invokeai/frontend/web/src/features/controlLayers/store/layersReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/layersReducers.ts index 52c7cf0849..0869db4286 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/layersReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/layersReducers.ts @@ -8,10 +8,11 @@ import { v4 as uuidv4 } from 'uuid'; import type { BrushLine, CanvasV2State, + Coordinate, EraserLine, ImageObjectAddedArg, LayerEntity, - Coordinate, + PositionChangedArg, RectShape, ScaleChangedArg, StagingAreaImage, @@ -37,8 +38,7 @@ export const layersReducers = { bboxNeedsUpdate: false, objects: [], opacity: 1, - x: 0, - y: 0, + position: { x: 0, y: 0 }, }); state.selectedEntityIdentifier = { type: 'layer', id }; state.layers.imageCache = null; @@ -48,9 +48,9 @@ export const layersReducers = { layerAddedFromStagingArea: { reducer: ( state, - action: PayloadAction<{ id: string; objectId: string; stagingAreaImage: StagingAreaImage; pos: Coordinate }> + action: PayloadAction<{ id: string; objectId: string; stagingAreaImage: StagingAreaImage; position: Coordinate }> ) => { - const { id, objectId, stagingAreaImage, pos } = action.payload; + const { id, objectId, stagingAreaImage, position } = action.payload; const { imageDTO, offsetX, offsetY } = stagingAreaImage; const imageObject = imageDTOToImageObject(id, objectId, imageDTO); state.layers.entities.push({ @@ -61,13 +61,12 @@ export const layersReducers = { bboxNeedsUpdate: false, objects: [imageObject], opacity: 1, - x: pos.x + offsetX, - y: pos.y + offsetY, + position: { x: position.x + offsetX, y: position.y + offsetY }, }); state.selectedEntityIdentifier = { type: 'layer', id }; state.layers.imageCache = null; }, - prepare: (payload: { stagingAreaImage: StagingAreaImage; pos: Coordinate }) => ({ + prepare: (payload: { stagingAreaImage: StagingAreaImage; position: Coordinate }) => ({ payload: { ...payload, id: uuidv4(), objectId: uuidv4() }, }), }, @@ -86,14 +85,13 @@ export const layersReducers = { layer.isEnabled = !layer.isEnabled; state.layers.imageCache = null; }, - layerTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => { - const { id, x, y } = action.payload; + layerTranslated: (state, action: PayloadAction) => { + const { id, position } = action.payload; const layer = selectLayer(state, id); if (!layer) { return; } - layer.x = x; - layer.y = y; + layer.position = position; state.layers.imageCache = null; }, layerBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => { @@ -121,8 +119,7 @@ export const layersReducers = { layer.bbox = null; layer.bboxNeedsUpdate = false; state.layers.imageCache = null; - layer.x = 0; - layer.y = 0; + layer.position = { x: 0, y: 0 }; }, layerDeleted: (state, action: PayloadAction<{ id: string }>) => { const { id } = action.payload; @@ -212,7 +209,7 @@ export const layersReducers = { state.layers.imageCache = null; }, layerScaled: (state, action: PayloadAction) => { - const { id, scale, x, y } = action.payload; + const { id, scale, position } = action.payload; const layer = selectLayer(state, id); if (!layer) { return; @@ -236,8 +233,7 @@ export const layersReducers = { obj.width *= scale; } } - layer.x = x; - layer.y = y; + layer.position = position; layer.bboxNeedsUpdate = true; state.layers.imageCache = null; }, diff --git a/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts b/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts index 1975d65681..5a93dcd8d4 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/regionsReducers.ts @@ -6,6 +6,7 @@ import type { CLIPVisionModelV2, EraserLine, IPMethodV2, + PositionChangedArg, RectShape, ScaleChangedArg, } from 'features/controlLayers/store/types'; @@ -61,8 +62,7 @@ export const regionsReducers = { bboxNeedsUpdate: false, objects: [], fill: getRGMaskFill(state), - x: 0, - y: 0, + position: { x: 0, y: 0 }, autoNegative: 'invert', positivePrompt: '', negativePrompt: null, @@ -97,16 +97,15 @@ export const regionsReducers = { rg.isEnabled = !rg.isEnabled; } }, - rgTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => { - const { id, x, y } = action.payload; + rgTranslated: (state, action: PayloadAction) => { + const { id, position } = action.payload; const rg = selectRG(state, id); if (rg) { - rg.x = x; - rg.y = y; + rg.position = position; } }, rgScaled: (state, action: PayloadAction) => { - const { id, scale, x, y } = action.payload; + const { id, scale, position } = action.payload; const rg = selectRG(state, id); if (!rg) { return; @@ -125,8 +124,7 @@ export const regionsReducers = { obj.width *= scale; } } - rg.x = x; - rg.y = y; + rg.position = position; rg.bboxNeedsUpdate = true; state.layers.imageCache = null; }, diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index 836c04459c..be4c3e1e9d 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -506,6 +506,18 @@ export const RGBA_RED: RgbaColor = { r: 255, g: 0, b: 0, a: 1 }; const zOpacity = z.number().gte(0).lte(1); +const zDimensions = z.object({ + width: z.number().int().positive(), + height: z.number().int().positive(), +}); +export type Dimensions = z.infer; + +const zCoordinate = z.object({ + x: z.number(), + y: z.number(), +}); +export type Coordinate = z.infer; + const zRect = z.object({ x: z.number(), y: z.number(), @@ -566,8 +578,7 @@ export const zLayerEntity = z.object({ id: zId, type: z.literal('layer'), isEnabled: z.boolean(), - x: z.number(), - y: z.number(), + position: zCoordinate, bbox: zRect.nullable(), bboxNeedsUpdate: z.boolean(), opacity: zOpacity, @@ -631,8 +642,7 @@ export const zRegionEntity = z.object({ id: zId, type: z.literal('regional_guidance'), isEnabled: z.boolean(), - x: z.number(), - y: z.number(), + position: zCoordinate, bbox: zRect.nullable(), bboxNeedsUpdate: z.boolean(), objects: z.array(zMaskObject), @@ -658,8 +668,7 @@ const zInpaintMaskEntity = z.object({ id: z.literal('inpaint_mask'), type: z.literal('inpaint_mask'), isEnabled: z.boolean(), - x: z.number(), - y: z.number(), + position: zCoordinate, bbox: zRect.nullable(), bboxNeedsUpdate: z.boolean(), objects: z.array(zMaskObject), @@ -682,8 +691,7 @@ const zControlAdapterEntityBase = z.object({ id: zId, type: z.literal('control_adapter'), isEnabled: z.boolean(), - x: z.number(), - y: z.number(), + position: zCoordinate, bbox: zRect.nullable(), bboxNeedsUpdate: z.boolean(), opacity: zOpacity, @@ -809,18 +817,6 @@ export type CanvasEntity = | InitialImageEntity; export type CanvasEntityIdentifier = Pick; -const zDimensions = z.object({ - width: z.number().int().positive(), - height: z.number().int().positive(), -}); -export type Dimensions = z.infer; - -const zCoordinate = z.object({ - x: z.number(), - y: z.number(), -}); -export type Coordinate = z.infer; - export type LoRA = { id: string; isEnabled: boolean; @@ -926,9 +922,9 @@ export type CanvasV2State = { }; }; -export type StageAttrs = { x: number; y: number; width: number; height: number; scale: number }; -export type PosChangedArg = { id: string; x: number; y: number }; -export type ScaleChangedArg = { id: string; scale: number; x: number; y: number }; +export type StageAttrs = { position: Coordinate; dimensions: Dimensions; scale: number }; +export type PositionChangedArg = { id: string; position: Coordinate }; +export type ScaleChangedArg = { id: string; scale: number; position: Coordinate }; export type BboxChangedArg = { id: string; bbox: Rect | null }; export type EraserLineAddedArg = { id: string; @@ -939,7 +935,7 @@ export type EraserLineAddedArg = { export type BrushLineAddedArg = EraserLineAddedArg & { color: RgbaColor }; export type PointAddedToLineArg = { id: string; point: [number, number] }; export type RectShapeAddedArg = { id: string; rect: IRect; color: RgbaColor }; -export type ImageObjectAddedArg = { id: string; imageDTO: ImageDTO; pos?: Coordinate }; +export type ImageObjectAddedArg = { id: string; imageDTO: ImageDTO; position?: Coordinate }; //#region Type guards export const isLine = (obj: RenderableObject): obj is BrushLine | EraserLine => {