feat(ui): use position and dimensions instead of separate x,y,width,height attrs

This commit is contained in:
psychedelicious 2024-07-17 11:08:19 +10:00
parent 56237328f1
commit 44f91026e1
16 changed files with 130 additions and 130 deletions

View File

@ -58,7 +58,7 @@ export const addStagingListeners = (startAppListening: AppStartListening) => {
assert(stagingAreaImage, 'No staged image found to accept'); assert(stagingAreaImage, 'No staged image found to accept');
const { x, y } = state.canvasV2.bbox.rect; const { x, y } = state.canvasV2.bbox.rect;
api.dispatch(layerAddedFromStagingArea({ stagingAreaImage, pos: { x, y } })); api.dispatch(layerAddedFromStagingArea({ stagingAreaImage, position: { x, y } }));
api.dispatch(sessionStagingAreaReset()); api.dispatch(sessionStagingAreaReset());
}, },
}); });

View File

@ -24,10 +24,10 @@ export const HeadsUpDisplay = memo(() => {
return ( return (
<Flex flexDir="column" bg="blackAlpha.400" borderBottomEndRadius="base" p={2} minW={64} gap={2}> <Flex flexDir="column" bg="blackAlpha.400" borderBottomEndRadius="base" p={2} minW={64} gap={2}>
<HUDItem label="Zoom" value={`${round(stageAttrs.scale * 100, 2)}%`} /> <HUDItem label="Zoom" value={`${round(stageAttrs.scale * 100, 2)}%`} />
<HUDItem label="Stage Pos" value={`${round(stageAttrs.x, 3)}, ${round(stageAttrs.y, 3)}`} /> <HUDItem label="Stage Pos" value={`${round(stageAttrs.position.x, 3)}, ${round(stageAttrs.position.y, 3)}`} />
<HUDItem <HUDItem
label="Stage Size" label="Stage Size"
value={`${round(stageAttrs.width / stageAttrs.scale, 2)}×${round(stageAttrs.height / stageAttrs.scale, 2)} px`} value={`${round(stageAttrs.dimensions.width / stageAttrs.scale, 2)}×${round(stageAttrs.dimensions.height / stageAttrs.scale, 2)} px`}
/> />
<HUDItem label="BBox Size" value={`${bbox.rect.width}×${bbox.rect.height} px`} /> <HUDItem label="BBox Size" value={`${bbox.rect.width}×${bbox.rect.height} px`} />
<HUDItem label="BBox Position" value={`${bbox.rect.x}, ${bbox.rect.y}`} /> <HUDItem label="BBox Position" value={`${bbox.rect.x}, ${bbox.rect.y}`} />

View File

@ -42,12 +42,15 @@ export class CanvasControlAdapter {
}); });
this.transformer.on('transformend', () => { this.transformer.on('transformend', () => {
this.manager.stateApi.onScaleChanged( 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' 'control_adapter'
); );
}); });
this.transformer.on('dragend', () => { 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); this.layer.add(this.transformer);
@ -60,8 +63,8 @@ export class CanvasControlAdapter {
// Update the layer's position and listening state // Update the layer's position and listening state
this.group.setAttrs({ this.group.setAttrs({
x: controlAdapterState.x, x: controlAdapterState.position.x,
y: controlAdapterState.y, y: controlAdapterState.position.y,
scaleX: 1, scaleX: 1,
scaleY: 1, scaleY: 1,
}); });

View File

@ -42,7 +42,7 @@ export class CanvasInitialImage {
} }
if (!this.image) { 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); this.objectsGroup.add(this.image.konvaImageGroup);
await this.image.update(this.initialImageState.imageObject, true); await this.image.update(this.initialImageState.imageObject, true);
} else if (!this.image.isLoading && !this.image.isError) { } else if (!this.image.isLoading && !this.image.isError) {

View File

@ -46,12 +46,15 @@ export class CanvasInpaintMask {
}); });
this.transformer.on('transformend', () => { this.transformer.on('transformend', () => {
this.manager.stateApi.onScaleChanged( 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' 'inpaint_mask'
); );
}); });
this.transformer.on('dragend', () => { 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); this.layer.add(this.transformer);
@ -103,8 +106,8 @@ export class CanvasInpaintMask {
// Update the layer's position and listening state // Update the layer's position and listening state
this.group.setAttrs({ this.group.setAttrs({
x: inpaintMaskState.x, x: inpaintMaskState.position.x,
y: inpaintMaskState.y, y: inpaintMaskState.position.y,
scaleX: 1, scaleX: 1,
scaleY: 1, scaleY: 1,
}); });

View File

@ -48,12 +48,12 @@ export class CanvasLayer {
}); });
this.transformer.on('transformend', () => { this.transformer.on('transformend', () => {
this.manager.stateApi.onScaleChanged( 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' 'layer'
); );
}); });
this.transformer.on('dragend', () => { 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); this.layer.add(this.transformer);
@ -99,8 +99,8 @@ export class CanvasLayer {
// Update the layer's position and listening state // Update the layer's position and listening state
this.group.setAttrs({ this.group.setAttrs({
x: layerState.x, x: layerState.position.x,
y: layerState.y, y: layerState.position.y,
scaleX: 1, scaleX: 1,
scaleY: 1, scaleY: 1,
}); });

View File

@ -212,10 +212,8 @@ export class CanvasManager {
this.stage.width(this.container.offsetWidth); this.stage.width(this.container.offsetWidth);
this.stage.height(this.container.offsetHeight); this.stage.height(this.container.offsetHeight);
this.stateApi.setStageAttrs({ this.stateApi.setStageAttrs({
x: this.stage.x(), position: { x: this.stage.x(), y: this.stage.y() },
y: this.stage.y(), dimensions: { width: this.stage.width(), height: this.stage.height() },
width: this.stage.width(),
height: this.stage.height(),
scale: this.stage.scaleX(), scale: this.stage.scaleX(),
}); });
this.background.render(); this.background.render();

View File

@ -47,12 +47,15 @@ export class CanvasRegion {
}); });
this.transformer.on('transformend', () => { this.transformer.on('transformend', () => {
this.manager.stateApi.onScaleChanged( 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' 'regional_guidance'
); );
}); });
this.transformer.on('dragend', () => { 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); this.layer.add(this.transformer);
@ -104,8 +107,8 @@ export class CanvasRegion {
// Update the layer's position and listening state // Update the layer's position and listening state
this.group.setAttrs({ this.group.setAttrs({
x: regionState.x, x: regionState.position.x,
y: regionState.y, y: regionState.position.y,
scaleX: 1, scaleX: 1,
scaleY: 1, scaleY: 1,
}); });

View File

@ -47,7 +47,7 @@ import type {
BrushLine, BrushLine,
CanvasEntity, CanvasEntity,
EraserLine, EraserLine,
PosChangedArg, PositionChangedArg,
RectShape, RectShape,
ScaleChangedArg, ScaleChangedArg,
Tool, Tool,
@ -70,7 +70,7 @@ export class CanvasStateApi {
return this.store.getState().canvasV2; return this.store.getState().canvasV2;
}; };
onPosChanged = (arg: PosChangedArg, entityType: CanvasEntity['type']) => { onPosChanged = (arg: PositionChangedArg, entityType: CanvasEntity['type']) => {
log.debug('onPosChanged'); log.debug('onPosChanged');
if (entityType === 'layer') { if (entityType === 'layer') {
this.store.dispatch(layerTranslated(arg)); this.store.dispatch(layerTranslated(arg));

View File

@ -2,9 +2,9 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { getScaledCursorPosition } from 'features/controlLayers/konva/util'; import { getScaledCursorPosition } from 'features/controlLayers/konva/util';
import type { import type {
CanvasV2State, CanvasV2State,
Coordinate,
InpaintMaskEntity, InpaintMaskEntity,
LayerEntity, LayerEntity,
Coordinate,
RegionEntity, RegionEntity,
Tool, Tool,
} from 'features/controlLayers/store/types'; } from 'features/controlLayers/store/types';
@ -141,15 +141,15 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
if (settings.clipToBbox) { if (settings.clipToBbox) {
return { return {
x: bboxRect.x - entity.x, x: bboxRect.x - entity.position.x,
y: bboxRect.y - entity.y, y: bboxRect.y - entity.position.y,
width: bboxRect.width, width: bboxRect.width,
height: bboxRect.height, height: bboxRect.height,
}; };
} else { } else {
return { return {
x: -stage.x() / stage.scaleX() - entity.x, x: -stage.x() / stage.scaleX() - entity.position.x,
y: -stage.y() / stage.scaleY() - entity.y, y: -stage.y() / stage.scaleY() - entity.position.y,
width: stage.width() / stage.scaleX(), width: stage.width() / stage.scaleX(),
height: stage.height() / stage.scaleY(), 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 // The last point of the last line is already normalized to the entity's coordinates
lastLinePoint.x, lastLinePoint.x,
lastLinePoint.y, lastLinePoint.y,
pos.x - selectedEntity.x, pos.x - selectedEntity.position.x,
pos.y - selectedEntity.y, pos.y - selectedEntity.position.y,
], ],
strokeWidth: toolState.brush.width, strokeWidth: toolState.brush.width,
color: getCurrentFill(), color: getCurrentFill(),
@ -208,7 +208,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
await selectedEntityAdapter.setDrawingBuffer({ await selectedEntityAdapter.setDrawingBuffer({
id: getBrushLineId(selectedEntityAdapter.id, uuidv4()), id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
type: 'brush_line', 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, strokeWidth: toolState.brush.width,
color: getCurrentFill(), color: getCurrentFill(),
clip: getClip(selectedEntity), 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 // The last point of the last line is already normalized to the entity's coordinates
lastLinePoint.x, lastLinePoint.x,
lastLinePoint.y, lastLinePoint.y,
pos.x - selectedEntity.x, pos.x - selectedEntity.position.x,
pos.y - selectedEntity.y, pos.y - selectedEntity.position.y,
], ],
strokeWidth: toolState.eraser.width, strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity), clip: getClip(selectedEntity),
@ -244,7 +244,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
await selectedEntityAdapter.setDrawingBuffer({ await selectedEntityAdapter.setDrawingBuffer({
id: getEraserLineId(selectedEntityAdapter.id, uuidv4()), id: getEraserLineId(selectedEntityAdapter.id, uuidv4()),
type: 'eraser_line', 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, strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity), clip: getClip(selectedEntity),
}); });
@ -259,8 +259,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
await selectedEntityAdapter.setDrawingBuffer({ await selectedEntityAdapter.setDrawingBuffer({
id: getRectShapeId(selectedEntityAdapter.id, uuidv4()), id: getRectShapeId(selectedEntityAdapter.id, uuidv4()),
type: 'rect_shape', type: 'rect_shape',
x: pos.x - selectedEntity.x, x: pos.x - selectedEntity.position.x,
y: pos.y - selectedEntity.y, y: pos.y - selectedEntity.position.y,
width: 0, width: 0,
height: 0, height: 0,
color: getCurrentFill(), color: getCurrentFill(),
@ -342,7 +342,10 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
if (drawingBuffer?.type === 'brush_line') { if (drawingBuffer?.type === 'brush_line') {
const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points)); const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points));
if (nextPoint) { 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); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
setLastAddedPoint(nextPoint); setLastAddedPoint(nextPoint);
} }
@ -356,7 +359,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
await selectedEntityAdapter.setDrawingBuffer({ await selectedEntityAdapter.setDrawingBuffer({
id: getBrushLineId(selectedEntityAdapter.id, uuidv4()), id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
type: 'brush_line', 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, strokeWidth: toolState.brush.width,
color: getCurrentFill(), color: getCurrentFill(),
clip: getClip(selectedEntity), clip: getClip(selectedEntity),
@ -371,7 +374,10 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
if (drawingBuffer.type === 'eraser_line') { if (drawingBuffer.type === 'eraser_line') {
const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points)); const nextPoint = getNextPoint(pos, toolState, getLastPointOfLine(drawingBuffer.points));
if (nextPoint) { 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); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
setLastAddedPoint(nextPoint); setLastAddedPoint(nextPoint);
} }
@ -385,7 +391,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
await selectedEntityAdapter.setDrawingBuffer({ await selectedEntityAdapter.setDrawingBuffer({
id: getEraserLineId(selectedEntityAdapter.id, uuidv4()), id: getEraserLineId(selectedEntityAdapter.id, uuidv4()),
type: 'eraser_line', 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, strokeWidth: toolState.eraser.width,
clip: getClip(selectedEntity), clip: getClip(selectedEntity),
}); });
@ -397,8 +403,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer(); const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
if (drawingBuffer) { if (drawingBuffer) {
if (drawingBuffer.type === 'rect_shape') { if (drawingBuffer.type === 'rect_shape') {
drawingBuffer.width = pos.x - selectedEntity.x - drawingBuffer.x; drawingBuffer.width = pos.x - selectedEntity.position.x - drawingBuffer.x;
drawingBuffer.height = pos.y - selectedEntity.y - drawingBuffer.y; drawingBuffer.height = pos.y - selectedEntity.position.y - drawingBuffer.y;
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
} else { } else {
await selectedEntityAdapter.setDrawingBuffer(null); await selectedEntityAdapter.setDrawingBuffer(null);
@ -429,16 +435,16 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
) { ) {
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer(); const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') { 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); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
selectedEntityAdapter.finalizeDrawingBuffer(); selectedEntityAdapter.finalizeDrawingBuffer();
} else if (toolState.selected === 'eraser' && drawingBuffer?.type === 'eraser_line') { } 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); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
selectedEntityAdapter.finalizeDrawingBuffer(); selectedEntityAdapter.finalizeDrawingBuffer();
} else if (toolState.selected === 'rect' && drawingBuffer?.type === 'rect_shape') { } else if (toolState.selected === 'rect' && drawingBuffer?.type === 'rect_shape') {
drawingBuffer.width = pos.x - selectedEntity.x - drawingBuffer.x; drawingBuffer.width = pos.x - selectedEntity.position.x - drawingBuffer.x;
drawingBuffer.height = pos.y - selectedEntity.y - drawingBuffer.y; drawingBuffer.height = pos.y - selectedEntity.position.y - drawingBuffer.y;
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer); await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
selectedEntityAdapter.finalizeDrawingBuffer(); selectedEntityAdapter.finalizeDrawingBuffer();
} }
@ -484,7 +490,11 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
stage.scaleX(newScale); stage.scaleX(newScale);
stage.scaleY(newScale); stage.scaleY(newScale);
stage.position(newPos); 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(); manager.background.render();
} }
} }
@ -494,10 +504,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
//#region dragmove //#region dragmove
stage.on('dragmove', () => { stage.on('dragmove', () => {
setStageAttrs({ setStageAttrs({
x: Math.floor(stage.x()), position: { x: Math.floor(stage.x()), y: Math.floor(stage.y()) },
y: Math.floor(stage.y()), dimensions: { width: stage.width(), height: stage.height() },
width: stage.width(),
height: stage.height(),
scale: stage.scaleX(), scale: stage.scaleX(),
}); });
manager.background.render(); manager.background.render();
@ -508,10 +516,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
stage.on('dragend', () => { stage.on('dragend', () => {
// Stage position should always be an integer, else we get fractional pixels which are blurry // Stage position should always be an integer, else we get fractional pixels which are blurry
setStageAttrs({ setStageAttrs({
x: Math.floor(stage.x()), position: { x: Math.floor(stage.x()), y: Math.floor(stage.y()) },
y: Math.floor(stage.y()), dimensions: { width: stage.width(), height: stage.height() },
width: stage.width(),
height: stage.height(),
scale: stage.scaleX(), scale: stage.scaleX(),
}); });
manager.preview.tool.render(); manager.preview.tool.render();

View File

@ -50,8 +50,10 @@ const initialState: CanvasV2State = {
imageCache: null, imageCache: null,
isEnabled: true, isEnabled: true,
objects: [], objects: [],
x: 0, position: {
y: 0, x: 0,
y: 0,
},
}, },
tool: { tool: {
selected: 'view', selected: 'view',
@ -369,10 +371,8 @@ const migrate = (state: any): any => {
// Ephemeral state that does not need to be in redux // Ephemeral state that does not need to be in redux
export const $isPreviewVisible = atom(true); export const $isPreviewVisible = atom(true);
export const $stageAttrs = atom<StageAttrs>({ export const $stageAttrs = atom<StageAttrs>({
x: 0, position: { x: 0, y: 0 },
y: 0, dimensions: { width: 0, height: 0 },
width: 0,
height: 0,
scale: 0, scale: 0,
}); });
export const $shouldShowStagedImage = atom(true); export const $shouldShowStagedImage = atom(true);

View File

@ -14,6 +14,7 @@ import type {
ControlNetConfig, ControlNetConfig,
ControlNetData, ControlNetData,
Filter, Filter,
PositionChangedArg,
ProcessorConfig, ProcessorConfig,
ScaleChangedArg, ScaleChangedArg,
T2IAdapterConfig, T2IAdapterConfig,
@ -35,8 +36,7 @@ export const controlAdaptersReducers = {
state.controlAdapters.entities.push({ state.controlAdapters.entities.push({
id, id,
type: 'control_adapter', type: 'control_adapter',
x: 0, position: { x: 0, y: 0 },
y: 0,
bbox: null, bbox: null,
bboxNeedsUpdate: false, bboxNeedsUpdate: false,
isEnabled: true, isEnabled: true,
@ -64,17 +64,16 @@ export const controlAdaptersReducers = {
} }
ca.isEnabled = !ca.isEnabled; ca.isEnabled = !ca.isEnabled;
}, },
caTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => { caTranslated: (state, action: PayloadAction<PositionChangedArg>) => {
const { id, x, y } = action.payload; const { id, position } = action.payload;
const ca = selectCA(state, id); const ca = selectCA(state, id);
if (!ca) { if (!ca) {
return; return;
} }
ca.x = x; ca.position = position;
ca.y = y;
}, },
caScaled: (state, action: PayloadAction<ScaleChangedArg>) => { caScaled: (state, action: PayloadAction<ScaleChangedArg>) => {
const { id, scale, x, y } = action.payload; const { id, scale, position } = action.payload;
const ca = selectCA(state, id); const ca = selectCA(state, id);
if (!ca) { if (!ca) {
return; return;
@ -92,8 +91,7 @@ export const controlAdaptersReducers = {
ca.processedImageObject.height *= scale; ca.processedImageObject.height *= scale;
ca.processedImageObject.width *= scale; ca.processedImageObject.width *= scale;
} }
ca.x = x; ca.position = position;
ca.y = y;
ca.bboxNeedsUpdate = true; ca.bboxNeedsUpdate = true;
state.layers.imageCache = null; state.layers.imageCache = null;
}, },

View File

@ -2,6 +2,7 @@ import type { PayloadAction, SliceCaseReducers } from '@reduxjs/toolkit';
import type { import type {
BrushLine, BrushLine,
CanvasV2State, CanvasV2State,
Coordinate,
EraserLine, EraserLine,
InpaintMaskEntity, InpaintMaskEntity,
RectShape, RectShape,
@ -28,13 +29,12 @@ export const inpaintMaskReducers = {
imIsEnabledToggled: (state) => { imIsEnabledToggled: (state) => {
state.inpaintMask.isEnabled = !state.inpaintMask.isEnabled; state.inpaintMask.isEnabled = !state.inpaintMask.isEnabled;
}, },
imTranslated: (state, action: PayloadAction<{ x: number; y: number }>) => { imTranslated: (state, action: PayloadAction<{ position: Coordinate }>) => {
const { x, y } = action.payload; const { position } = action.payload;
state.inpaintMask.x = x; state.inpaintMask.position = position;
state.inpaintMask.y = y;
}, },
imScaled: (state, action: PayloadAction<ScaleChangedArg>) => { imScaled: (state, action: PayloadAction<ScaleChangedArg>) => {
const { scale, x, y } = action.payload; const { scale, position } = action.payload;
for (const obj of state.inpaintMask.objects) { for (const obj of state.inpaintMask.objects) {
if (obj.type === 'brush_line') { if (obj.type === 'brush_line') {
obj.points = obj.points.map((point) => point * scale); obj.points = obj.points.map((point) => point * scale);
@ -49,8 +49,7 @@ export const inpaintMaskReducers = {
obj.width *= scale; obj.width *= scale;
} }
} }
state.inpaintMask.x = x; state.inpaintMask.position = position;
state.inpaintMask.y = y;
state.inpaintMask.bboxNeedsUpdate = true; state.inpaintMask.bboxNeedsUpdate = true;
state.inpaintMask.imageCache = null; state.inpaintMask.imageCache = null;
}, },

View File

@ -8,10 +8,11 @@ import { v4 as uuidv4 } from 'uuid';
import type { import type {
BrushLine, BrushLine,
CanvasV2State, CanvasV2State,
Coordinate,
EraserLine, EraserLine,
ImageObjectAddedArg, ImageObjectAddedArg,
LayerEntity, LayerEntity,
Coordinate, PositionChangedArg,
RectShape, RectShape,
ScaleChangedArg, ScaleChangedArg,
StagingAreaImage, StagingAreaImage,
@ -37,8 +38,7 @@ export const layersReducers = {
bboxNeedsUpdate: false, bboxNeedsUpdate: false,
objects: [], objects: [],
opacity: 1, opacity: 1,
x: 0, position: { x: 0, y: 0 },
y: 0,
}); });
state.selectedEntityIdentifier = { type: 'layer', id }; state.selectedEntityIdentifier = { type: 'layer', id };
state.layers.imageCache = null; state.layers.imageCache = null;
@ -48,9 +48,9 @@ export const layersReducers = {
layerAddedFromStagingArea: { layerAddedFromStagingArea: {
reducer: ( reducer: (
state, 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 { imageDTO, offsetX, offsetY } = stagingAreaImage;
const imageObject = imageDTOToImageObject(id, objectId, imageDTO); const imageObject = imageDTOToImageObject(id, objectId, imageDTO);
state.layers.entities.push({ state.layers.entities.push({
@ -61,13 +61,12 @@ export const layersReducers = {
bboxNeedsUpdate: false, bboxNeedsUpdate: false,
objects: [imageObject], objects: [imageObject],
opacity: 1, opacity: 1,
x: pos.x + offsetX, position: { x: position.x + offsetX, y: position.y + offsetY },
y: pos.y + offsetY,
}); });
state.selectedEntityIdentifier = { type: 'layer', id }; state.selectedEntityIdentifier = { type: 'layer', id };
state.layers.imageCache = null; state.layers.imageCache = null;
}, },
prepare: (payload: { stagingAreaImage: StagingAreaImage; pos: Coordinate }) => ({ prepare: (payload: { stagingAreaImage: StagingAreaImage; position: Coordinate }) => ({
payload: { ...payload, id: uuidv4(), objectId: uuidv4() }, payload: { ...payload, id: uuidv4(), objectId: uuidv4() },
}), }),
}, },
@ -86,14 +85,13 @@ export const layersReducers = {
layer.isEnabled = !layer.isEnabled; layer.isEnabled = !layer.isEnabled;
state.layers.imageCache = null; state.layers.imageCache = null;
}, },
layerTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => { layerTranslated: (state, action: PayloadAction<PositionChangedArg>) => {
const { id, x, y } = action.payload; const { id, position } = action.payload;
const layer = selectLayer(state, id); const layer = selectLayer(state, id);
if (!layer) { if (!layer) {
return; return;
} }
layer.x = x; layer.position = position;
layer.y = y;
state.layers.imageCache = null; state.layers.imageCache = null;
}, },
layerBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => { layerBboxChanged: (state, action: PayloadAction<{ id: string; bbox: IRect | null }>) => {
@ -121,8 +119,7 @@ export const layersReducers = {
layer.bbox = null; layer.bbox = null;
layer.bboxNeedsUpdate = false; layer.bboxNeedsUpdate = false;
state.layers.imageCache = null; state.layers.imageCache = null;
layer.x = 0; layer.position = { x: 0, y: 0 };
layer.y = 0;
}, },
layerDeleted: (state, action: PayloadAction<{ id: string }>) => { layerDeleted: (state, action: PayloadAction<{ id: string }>) => {
const { id } = action.payload; const { id } = action.payload;
@ -212,7 +209,7 @@ export const layersReducers = {
state.layers.imageCache = null; state.layers.imageCache = null;
}, },
layerScaled: (state, action: PayloadAction<ScaleChangedArg>) => { layerScaled: (state, action: PayloadAction<ScaleChangedArg>) => {
const { id, scale, x, y } = action.payload; const { id, scale, position } = action.payload;
const layer = selectLayer(state, id); const layer = selectLayer(state, id);
if (!layer) { if (!layer) {
return; return;
@ -236,8 +233,7 @@ export const layersReducers = {
obj.width *= scale; obj.width *= scale;
} }
} }
layer.x = x; layer.position = position;
layer.y = y;
layer.bboxNeedsUpdate = true; layer.bboxNeedsUpdate = true;
state.layers.imageCache = null; state.layers.imageCache = null;
}, },

View File

@ -6,6 +6,7 @@ import type {
CLIPVisionModelV2, CLIPVisionModelV2,
EraserLine, EraserLine,
IPMethodV2, IPMethodV2,
PositionChangedArg,
RectShape, RectShape,
ScaleChangedArg, ScaleChangedArg,
} from 'features/controlLayers/store/types'; } from 'features/controlLayers/store/types';
@ -61,8 +62,7 @@ export const regionsReducers = {
bboxNeedsUpdate: false, bboxNeedsUpdate: false,
objects: [], objects: [],
fill: getRGMaskFill(state), fill: getRGMaskFill(state),
x: 0, position: { x: 0, y: 0 },
y: 0,
autoNegative: 'invert', autoNegative: 'invert',
positivePrompt: '', positivePrompt: '',
negativePrompt: null, negativePrompt: null,
@ -97,16 +97,15 @@ export const regionsReducers = {
rg.isEnabled = !rg.isEnabled; rg.isEnabled = !rg.isEnabled;
} }
}, },
rgTranslated: (state, action: PayloadAction<{ id: string; x: number; y: number }>) => { rgTranslated: (state, action: PayloadAction<PositionChangedArg>) => {
const { id, x, y } = action.payload; const { id, position } = action.payload;
const rg = selectRG(state, id); const rg = selectRG(state, id);
if (rg) { if (rg) {
rg.x = x; rg.position = position;
rg.y = y;
} }
}, },
rgScaled: (state, action: PayloadAction<ScaleChangedArg>) => { rgScaled: (state, action: PayloadAction<ScaleChangedArg>) => {
const { id, scale, x, y } = action.payload; const { id, scale, position } = action.payload;
const rg = selectRG(state, id); const rg = selectRG(state, id);
if (!rg) { if (!rg) {
return; return;
@ -125,8 +124,7 @@ export const regionsReducers = {
obj.width *= scale; obj.width *= scale;
} }
} }
rg.x = x; rg.position = position;
rg.y = y;
rg.bboxNeedsUpdate = true; rg.bboxNeedsUpdate = true;
state.layers.imageCache = null; state.layers.imageCache = null;
}, },

View File

@ -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 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<typeof zDimensions>;
const zCoordinate = z.object({
x: z.number(),
y: z.number(),
});
export type Coordinate = z.infer<typeof zCoordinate>;
const zRect = z.object({ const zRect = z.object({
x: z.number(), x: z.number(),
y: z.number(), y: z.number(),
@ -566,8 +578,7 @@ export const zLayerEntity = z.object({
id: zId, id: zId,
type: z.literal('layer'), type: z.literal('layer'),
isEnabled: z.boolean(), isEnabled: z.boolean(),
x: z.number(), position: zCoordinate,
y: z.number(),
bbox: zRect.nullable(), bbox: zRect.nullable(),
bboxNeedsUpdate: z.boolean(), bboxNeedsUpdate: z.boolean(),
opacity: zOpacity, opacity: zOpacity,
@ -631,8 +642,7 @@ export const zRegionEntity = z.object({
id: zId, id: zId,
type: z.literal('regional_guidance'), type: z.literal('regional_guidance'),
isEnabled: z.boolean(), isEnabled: z.boolean(),
x: z.number(), position: zCoordinate,
y: z.number(),
bbox: zRect.nullable(), bbox: zRect.nullable(),
bboxNeedsUpdate: z.boolean(), bboxNeedsUpdate: z.boolean(),
objects: z.array(zMaskObject), objects: z.array(zMaskObject),
@ -658,8 +668,7 @@ const zInpaintMaskEntity = z.object({
id: z.literal('inpaint_mask'), id: z.literal('inpaint_mask'),
type: z.literal('inpaint_mask'), type: z.literal('inpaint_mask'),
isEnabled: z.boolean(), isEnabled: z.boolean(),
x: z.number(), position: zCoordinate,
y: z.number(),
bbox: zRect.nullable(), bbox: zRect.nullable(),
bboxNeedsUpdate: z.boolean(), bboxNeedsUpdate: z.boolean(),
objects: z.array(zMaskObject), objects: z.array(zMaskObject),
@ -682,8 +691,7 @@ const zControlAdapterEntityBase = z.object({
id: zId, id: zId,
type: z.literal('control_adapter'), type: z.literal('control_adapter'),
isEnabled: z.boolean(), isEnabled: z.boolean(),
x: z.number(), position: zCoordinate,
y: z.number(),
bbox: zRect.nullable(), bbox: zRect.nullable(),
bboxNeedsUpdate: z.boolean(), bboxNeedsUpdate: z.boolean(),
opacity: zOpacity, opacity: zOpacity,
@ -809,18 +817,6 @@ export type CanvasEntity =
| InitialImageEntity; | InitialImageEntity;
export type CanvasEntityIdentifier = Pick<CanvasEntity, 'id' | 'type'>; export type CanvasEntityIdentifier = Pick<CanvasEntity, 'id' | 'type'>;
const zDimensions = z.object({
width: z.number().int().positive(),
height: z.number().int().positive(),
});
export type Dimensions = z.infer<typeof zDimensions>;
const zCoordinate = z.object({
x: z.number(),
y: z.number(),
});
export type Coordinate = z.infer<typeof zCoordinate>;
export type LoRA = { export type LoRA = {
id: string; id: string;
isEnabled: boolean; isEnabled: boolean;
@ -926,9 +922,9 @@ export type CanvasV2State = {
}; };
}; };
export type StageAttrs = { x: number; y: number; width: number; height: number; scale: number }; export type StageAttrs = { position: Coordinate; dimensions: Dimensions; scale: number };
export type PosChangedArg = { id: string; x: number; y: number }; export type PositionChangedArg = { id: string; position: Coordinate };
export type ScaleChangedArg = { id: string; scale: number; x: number; y: number }; export type ScaleChangedArg = { id: string; scale: number; position: Coordinate };
export type BboxChangedArg = { id: string; bbox: Rect | null }; export type BboxChangedArg = { id: string; bbox: Rect | null };
export type EraserLineAddedArg = { export type EraserLineAddedArg = {
id: string; id: string;
@ -939,7 +935,7 @@ export type EraserLineAddedArg = {
export type BrushLineAddedArg = EraserLineAddedArg & { color: RgbaColor }; export type BrushLineAddedArg = EraserLineAddedArg & { color: RgbaColor };
export type PointAddedToLineArg = { id: string; point: [number, number] }; export type PointAddedToLineArg = { id: string; point: [number, number] };
export type RectShapeAddedArg = { id: string; rect: IRect; color: RgbaColor }; 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 //#region Type guards
export const isLine = (obj: RenderableObject): obj is BrushLine | EraserLine => { export const isLine = (obj: RenderableObject): obj is BrushLine | EraserLine => {