mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): hallelujah (???)
This commit is contained in:
parent
ae3e473024
commit
b1c7236117
@ -1,4 +1,5 @@
|
|||||||
import { getStore } from 'app/store/nanostores/store';
|
import { getStore } from 'app/store/nanostores/store';
|
||||||
|
import { deepClone } from 'common/util/deepClone';
|
||||||
import { CanvasBrushLine } from 'features/controlLayers/konva/CanvasBrushLine';
|
import { CanvasBrushLine } from 'features/controlLayers/konva/CanvasBrushLine';
|
||||||
import { CanvasEraserLine } from 'features/controlLayers/konva/CanvasEraserLine';
|
import { CanvasEraserLine } from 'features/controlLayers/konva/CanvasEraserLine';
|
||||||
import { CanvasImage } from 'features/controlLayers/konva/CanvasImage';
|
import { CanvasImage } from 'features/controlLayers/konva/CanvasImage';
|
||||||
@ -13,6 +14,7 @@ import type {
|
|||||||
Coordinate,
|
Coordinate,
|
||||||
EraserLine,
|
EraserLine,
|
||||||
LayerEntity,
|
LayerEntity,
|
||||||
|
Rect,
|
||||||
RectShape,
|
RectShape,
|
||||||
} from 'features/controlLayers/store/types';
|
} from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
@ -46,15 +48,14 @@ export class CanvasLayer {
|
|||||||
};
|
};
|
||||||
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect | CanvasImage>;
|
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect | CanvasImage>;
|
||||||
|
|
||||||
offsetX: number;
|
|
||||||
offsetY: number;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
log: Logger;
|
log: Logger;
|
||||||
bboxNeedsUpdate: boolean;
|
bboxNeedsUpdate: boolean;
|
||||||
isTransforming: boolean;
|
isTransforming: boolean;
|
||||||
isFirstRender: boolean;
|
isFirstRender: boolean;
|
||||||
|
|
||||||
|
rect: Rect;
|
||||||
|
bbox: Rect;
|
||||||
|
|
||||||
constructor(state: LayerEntity, manager: CanvasManager) {
|
constructor(state: LayerEntity, manager: CanvasManager) {
|
||||||
this.id = state.id;
|
this.id = state.id;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
@ -146,8 +147,8 @@ export class CanvasLayer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.konva.objectGroup.setAttrs({
|
this.konva.objectGroup.setAttrs({
|
||||||
x: this.konva.interactionRect.x() - this.offsetX * this.konva.interactionRect.scaleX(),
|
x: this.konva.interactionRect.x(),
|
||||||
y: this.konva.interactionRect.y() - this.offsetY * this.konva.interactionRect.scaleY(),
|
y: this.konva.interactionRect.y(),
|
||||||
scaleX: this.konva.interactionRect.scaleX(),
|
scaleX: this.konva.interactionRect.scaleX(),
|
||||||
scaleY: this.konva.interactionRect.scaleY(),
|
scaleY: this.konva.interactionRect.scaleY(),
|
||||||
rotation: this.konva.interactionRect.rotation(),
|
rotation: this.konva.interactionRect.rotation(),
|
||||||
@ -158,8 +159,8 @@ export class CanvasLayer {
|
|||||||
y: this.konva.objectGroup.y(),
|
y: this.konva.objectGroup.y(),
|
||||||
scaleX: this.konva.objectGroup.scaleX(),
|
scaleX: this.konva.objectGroup.scaleX(),
|
||||||
scaleY: this.konva.objectGroup.scaleY(),
|
scaleY: this.konva.objectGroup.scaleY(),
|
||||||
offsetX: this.offsetX,
|
offsetX: this.konva.objectGroup.offsetX(),
|
||||||
offsetY: this.offsetY,
|
offsetY: this.konva.objectGroup.offsetY(),
|
||||||
width: this.konva.objectGroup.width(),
|
width: this.konva.objectGroup.width(),
|
||||||
height: this.konva.objectGroup.height(),
|
height: this.konva.objectGroup.height(),
|
||||||
rotation: this.konva.objectGroup.rotation(),
|
rotation: this.konva.objectGroup.rotation(),
|
||||||
@ -196,8 +197,8 @@ export class CanvasLayer {
|
|||||||
// The object group is translated by the difference between the interaction rect's new and old positions (which is
|
// The object group is translated by the difference between the interaction rect's new and old positions (which is
|
||||||
// stored as this.bbox)
|
// stored as this.bbox)
|
||||||
this.konva.objectGroup.setAttrs({
|
this.konva.objectGroup.setAttrs({
|
||||||
x: this.konva.interactionRect.x() - this.offsetX * this.konva.interactionRect.scaleX(),
|
x: this.konva.interactionRect.x(),
|
||||||
y: this.konva.interactionRect.y() - this.offsetY * this.konva.interactionRect.scaleY(),
|
y: this.konva.interactionRect.y(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.konva.interactionRect.on('dragend', () => {
|
this.konva.interactionRect.on('dragend', () => {
|
||||||
@ -213,8 +214,8 @@ export class CanvasLayer {
|
|||||||
{
|
{
|
||||||
id: this.id,
|
id: this.id,
|
||||||
position: {
|
position: {
|
||||||
x: this.konva.interactionRect.x() - this.offsetX * this.konva.interactionRect.scaleX(),
|
x: this.konva.interactionRect.x() - this.bbox.x,
|
||||||
y: this.konva.interactionRect.y() - this.offsetY * this.konva.interactionRect.scaleY(),
|
y: this.konva.interactionRect.y() - this.bbox.y,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'layer'
|
'layer'
|
||||||
@ -224,16 +225,12 @@ export class CanvasLayer {
|
|||||||
this.objects = new Map();
|
this.objects = new Map();
|
||||||
this.drawingBuffer = null;
|
this.drawingBuffer = null;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.offsetX = 0;
|
this.rect = this.getDefaultRect();
|
||||||
this.offsetY = 0;
|
this.bbox = this.getDefaultRect();
|
||||||
this.width = 0;
|
|
||||||
this.height = 0;
|
|
||||||
this.bboxNeedsUpdate = true;
|
this.bboxNeedsUpdate = true;
|
||||||
this.isTransforming = false;
|
this.isTransforming = false;
|
||||||
this.isFirstRender = true;
|
this.isFirstRender = true;
|
||||||
this.log = this.manager.getLogger(`layer_${this.id}`);
|
this.log = this.manager.getLogger(`layer_${this.id}`);
|
||||||
|
|
||||||
console.log(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
@ -317,16 +314,18 @@ export class CanvasLayer {
|
|||||||
const bboxPadding = this.manager.getScaledBboxPadding();
|
const bboxPadding = this.manager.getScaledBboxPadding();
|
||||||
|
|
||||||
this.konva.objectGroup.setAttrs({
|
this.konva.objectGroup.setAttrs({
|
||||||
x: position.x,
|
x: position.x + this.bbox.x,
|
||||||
y: position.y,
|
y: position.y + this.bbox.y,
|
||||||
|
offsetX: this.bbox.x,
|
||||||
|
offsetY: this.bbox.y,
|
||||||
});
|
});
|
||||||
this.konva.bbox.setAttrs({
|
this.konva.bbox.setAttrs({
|
||||||
x: position.x + this.offsetX * this.konva.interactionRect.scaleX() - bboxPadding,
|
x: position.x + this.bbox.x - bboxPadding,
|
||||||
y: position.y + this.offsetY * this.konva.interactionRect.scaleY() - bboxPadding,
|
y: position.y + this.bbox.y - bboxPadding,
|
||||||
});
|
});
|
||||||
this.konva.interactionRect.setAttrs({
|
this.konva.interactionRect.setAttrs({
|
||||||
x: position.x + this.offsetX * this.konva.interactionRect.scaleX(),
|
x: position.x + this.bbox.x * this.konva.interactionRect.scaleX(),
|
||||||
y: position.y + this.offsetY * this.konva.interactionRect.scaleY(),
|
y: position.y + this.bbox.y * this.konva.interactionRect.scaleY(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,7 +427,7 @@ export class CanvasLayer {
|
|||||||
// If the bbox has no width or height, that means the layer is fully transparent. This can happen if it is only
|
// If the bbox has no width or height, that means the layer is fully transparent. This can happen if it is only
|
||||||
// eraser lines, fully clipped brush lines or if it has been fully erased. In this case, we should reset the layer
|
// eraser lines, fully clipped brush lines or if it has been fully erased. In this case, we should reset the layer
|
||||||
// so we aren't drawing shapes that do not render anything.
|
// so we aren't drawing shapes that do not render anything.
|
||||||
if (this.width === 0 || this.height === 0) {
|
if (this.bbox.width === 0 || this.bbox.height === 0) {
|
||||||
this.manager.stateApi.onEntityReset({ id: this.id }, 'layer');
|
this.manager.stateApi.onEntityReset({ id: this.id }, 'layer');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -437,17 +436,23 @@ export class CanvasLayer {
|
|||||||
const bboxPadding = this.manager.getScaledBboxPadding();
|
const bboxPadding = this.manager.getScaledBboxPadding();
|
||||||
|
|
||||||
this.konva.bbox.setAttrs({
|
this.konva.bbox.setAttrs({
|
||||||
x: this.state.position.x + this.offsetX * this.konva.interactionRect.scaleX() - bboxPadding,
|
x: this.state.position.x + this.bbox.x - bboxPadding,
|
||||||
y: this.state.position.y + this.offsetY * this.konva.interactionRect.scaleY() - bboxPadding,
|
y: this.state.position.y + this.bbox.y - bboxPadding,
|
||||||
width: this.width + bboxPadding * 2,
|
width: this.bbox.width + bboxPadding * 2,
|
||||||
height: this.height + bboxPadding * 2,
|
height: this.bbox.height + bboxPadding * 2,
|
||||||
strokeWidth: onePixel,
|
strokeWidth: onePixel,
|
||||||
});
|
});
|
||||||
this.konva.interactionRect.setAttrs({
|
this.konva.interactionRect.setAttrs({
|
||||||
x: this.state.position.x + this.offsetX * this.konva.interactionRect.scaleX(),
|
x: this.state.position.x + this.bbox.x,
|
||||||
y: this.state.position.y + this.offsetY * this.konva.interactionRect.scaleY(),
|
y: this.state.position.y + this.bbox.y,
|
||||||
width: this.width,
|
width: this.bbox.width,
|
||||||
height: this.height,
|
height: this.bbox.height,
|
||||||
|
});
|
||||||
|
this.konva.objectGroup.setAttrs({
|
||||||
|
x: this.state.position.x + this.bbox.x,
|
||||||
|
y: this.state.position.y + this.bbox.y,
|
||||||
|
offsetX: this.bbox.x,
|
||||||
|
offsetY: this.bbox.y,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,11 +555,14 @@ export class CanvasLayer {
|
|||||||
|
|
||||||
async resetScale() {
|
async resetScale() {
|
||||||
this.konva.objectGroup.scaleX(1);
|
this.konva.objectGroup.scaleX(1);
|
||||||
this.konva.objectGroup.scaleY(1);
|
this.konva.objectGroup.scaleX(1);
|
||||||
|
this.konva.objectGroup.rotation(0);
|
||||||
this.konva.bbox.scaleX(1);
|
this.konva.bbox.scaleX(1);
|
||||||
this.konva.bbox.scaleY(1);
|
this.konva.bbox.scaleX(1);
|
||||||
|
this.konva.bbox.rotation(0);
|
||||||
this.konva.interactionRect.scaleX(1);
|
this.konva.interactionRect.scaleX(1);
|
||||||
this.konva.interactionRect.scaleY(1);
|
this.konva.interactionRect.scaleX(1);
|
||||||
|
this.konva.interactionRect.rotation(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyTransform() {
|
async applyTransform() {
|
||||||
@ -562,13 +570,7 @@ export class CanvasLayer {
|
|||||||
|
|
||||||
this.isTransforming = false;
|
this.isTransforming = false;
|
||||||
const objectGroupClone = this.konva.objectGroup.clone();
|
const objectGroupClone = this.konva.objectGroup.clone();
|
||||||
const rect = {
|
const blob = await konvaNodeToBlob(objectGroupClone, objectGroupClone.getClientRect());
|
||||||
x: this.konva.interactionRect.x(),
|
|
||||||
y: this.konva.interactionRect.y(),
|
|
||||||
width: this.konva.interactionRect.width() * this.konva.interactionRect.scaleX(),
|
|
||||||
height: this.konva.interactionRect.height() * this.konva.interactionRect.scaleY(),
|
|
||||||
};
|
|
||||||
const blob = await konvaNodeToBlob(objectGroupClone, rect);
|
|
||||||
previewBlob(blob, 'transformed layer');
|
previewBlob(blob, 'transformed layer');
|
||||||
const imageDTO = await uploadImage(blob, `${this.id}_transform.png`, 'other', true, true);
|
const imageDTO = await uploadImage(blob, `${this.id}_transform.png`, 'other', true, true);
|
||||||
const { dispatch } = getStore();
|
const { dispatch } = getStore();
|
||||||
@ -590,14 +592,16 @@ export class CanvasLayer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefaultRect(): Rect {
|
||||||
|
return { x: 0, y: 0, width: 0, height: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
calculateBbox = debounce(() => {
|
calculateBbox = debounce(() => {
|
||||||
this.log.debug('Calculating bbox');
|
this.log.debug('Calculating bbox');
|
||||||
|
|
||||||
if (this.objects.size === 0) {
|
if (this.objects.size === 0) {
|
||||||
this.offsetX = 0;
|
this.rect = this.getDefaultRect();
|
||||||
this.offsetY = 0;
|
this.bbox = this.getDefaultRect();
|
||||||
this.width = 0;
|
|
||||||
this.height = 0;
|
|
||||||
this.updateBbox();
|
this.updateBbox();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -605,8 +609,6 @@ export class CanvasLayer {
|
|||||||
let needsPixelBbox = false;
|
let needsPixelBbox = false;
|
||||||
const rect = this.konva.objectGroup.getClientRect({ skipTransform: true });
|
const rect = this.konva.objectGroup.getClientRect({ skipTransform: true });
|
||||||
|
|
||||||
console.log('getBbox rect', rect);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In some cases, we can use konva's getClientRect as the bbox, but there are some cases where we need to calculate
|
* In some cases, we can use konva's getClientRect as the bbox, but there are some cases where we need to calculate
|
||||||
* the bbox using pixel data:
|
* the bbox using pixel data:
|
||||||
@ -628,11 +630,9 @@ export class CanvasLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!needsPixelBbox) {
|
if (!needsPixelBbox) {
|
||||||
this.offsetX = rect.x;
|
this.rect = deepClone(rect);
|
||||||
this.offsetY = rect.y;
|
this.bbox = deepClone(rect);
|
||||||
this.width = rect.width;
|
this.log.trace({ bbox: this.bbox, rect: this.rect }, 'Got bbox from client rect');
|
||||||
this.height = rect.height;
|
|
||||||
this.logBbox('new bbox from client rect');
|
|
||||||
this.updateBbox();
|
this.updateBbox();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -649,20 +649,19 @@ export class CanvasLayer {
|
|||||||
this.manager.requestBbox(
|
this.manager.requestBbox(
|
||||||
{ buffer: imageData.data.buffer, width: imageData.width, height: imageData.height },
|
{ buffer: imageData.data.buffer, width: imageData.width, height: imageData.height },
|
||||||
(extents) => {
|
(extents) => {
|
||||||
console.log('extents', extents);
|
this.rect = deepClone(rect);
|
||||||
if (extents) {
|
if (extents) {
|
||||||
const { minX, minY, maxX, maxY } = extents;
|
const { minX, minY, maxX, maxY } = extents;
|
||||||
this.offsetX = minX + rect.x;
|
this.bbox = {
|
||||||
this.offsetY = minY + rect.y;
|
x: rect.x + minX,
|
||||||
this.width = maxX - minX;
|
y: rect.y + minY,
|
||||||
this.height = maxY - minY;
|
width: maxX - minX,
|
||||||
|
height: maxY - minY,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.offsetX = 0;
|
this.bbox = deepClone(rect);
|
||||||
this.offsetY = 0;
|
|
||||||
this.width = 0;
|
|
||||||
this.height = 0;
|
|
||||||
}
|
}
|
||||||
this.logBbox('new bbox from worker');
|
this.log.trace({ bbox: this.bbox, rect: this.rect, extents }, `Got bbox from worker`);
|
||||||
this.updateBbox();
|
this.updateBbox();
|
||||||
clone.destroy();
|
clone.destroy();
|
||||||
}
|
}
|
||||||
@ -673,19 +672,8 @@ export class CanvasLayer {
|
|||||||
console.log(msg, {
|
console.log(msg, {
|
||||||
x: this.state.position.x,
|
x: this.state.position.x,
|
||||||
y: this.state.position.y,
|
y: this.state.position.y,
|
||||||
offsetX: this.offsetX,
|
rect: deepClone(this.rect),
|
||||||
offsetY: this.offsetY,
|
bbox: deepClone(this.bbox),
|
||||||
width: this.width,
|
|
||||||
height: this.height,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getLayerRect() {
|
|
||||||
return {
|
|
||||||
x: this.state.position.x + this.offsetX,
|
|
||||||
y: this.state.position.y + this.offsetY,
|
|
||||||
width: this.width,
|
|
||||||
height: this.height,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user