mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): staging area works
This commit is contained in:
parent
30d318d021
commit
4668ea449b
@ -5,9 +5,10 @@ import { parseify } from 'common/util/serialize';
|
|||||||
import {
|
import {
|
||||||
caImageChanged,
|
caImageChanged,
|
||||||
ipaImageChanged,
|
ipaImageChanged,
|
||||||
layerAddedFromImage,
|
layerAdded,
|
||||||
rgIPAdapterImageChanged,
|
rgIPAdapterImageChanged,
|
||||||
} from 'features/controlLayers/store/canvasV2Slice';
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
|
import type { CanvasLayerState } from 'features/controlLayers/store/types';
|
||||||
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
import { isValidDrop } from 'features/dnd/util/isValidDrop';
|
||||||
@ -106,7 +107,13 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
activeData.payloadType === 'IMAGE_DTO' &&
|
activeData.payloadType === 'IMAGE_DTO' &&
|
||||||
activeData.payload.imageDTO
|
activeData.payload.imageDTO
|
||||||
) {
|
) {
|
||||||
dispatch(layerAddedFromImage({ imageObject: imageDTOToImageObject(activeData.payload.imageDTO) }));
|
const imageObject = imageDTOToImageObject(activeData.payload.imageDTO);
|
||||||
|
const { x, y } = getState().canvasV2.bbox.rect;
|
||||||
|
const overrides: Partial<CanvasLayerState> = {
|
||||||
|
objects: [imageObject],
|
||||||
|
position: { x, y },
|
||||||
|
};
|
||||||
|
dispatch(layerAdded({ overrides, isSelected: true }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ export const AddLayerButton = memo(() => {
|
|||||||
dispatch(rgAdded());
|
dispatch(rgAdded());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
const addRasterLayer = useCallback(() => {
|
const addRasterLayer = useCallback(() => {
|
||||||
dispatch(layerAdded());
|
dispatch(layerAdded({ isSelected: true }));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { roundToMultiple, roundToMultipleMin } from 'common/util/roundDownToMultiple';
|
import { roundToMultiple, roundToMultipleMin } from 'common/util/roundDownToMultiple';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import type { CanvasPreview } from 'features/controlLayers/konva/CanvasPreview';
|
||||||
import type { Rect } from 'features/controlLayers/store/types';
|
import type { Rect } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
@ -23,6 +24,7 @@ export class CanvasBbox {
|
|||||||
static CORNER_ANCHORS: string[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
static CORNER_ANCHORS: string[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
||||||
static NO_ANCHORS: string[] = [];
|
static NO_ANCHORS: string[] = [];
|
||||||
|
|
||||||
|
parent: CanvasPreview;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
|
||||||
konva: {
|
konva: {
|
||||||
@ -31,8 +33,9 @@ export class CanvasBbox {
|
|||||||
transformer: Konva.Transformer;
|
transformer: Konva.Transformer;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(parent: CanvasPreview) {
|
||||||
this.manager = manager;
|
this.parent = parent;
|
||||||
|
this.manager = this.parent.manager;
|
||||||
// Create a stash to hold onto the last aspect ratio of the bbox - this allows for locking the aspect ratio when
|
// Create a stash to hold onto the last aspect ratio of the bbox - this allows for locking the aspect ratio when
|
||||||
// transforming the bbox.
|
// transforming the bbox.
|
||||||
const bbox = this.manager.stateApi.getBbox();
|
const bbox = this.manager.stateApi.getBbox();
|
||||||
|
@ -2,6 +2,7 @@ import { Mutex } from 'async-mutex';
|
|||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
|
import type { CanvasStagingArea } from 'features/controlLayers/konva/CanvasStagingArea';
|
||||||
import { FILTER_MAP } from 'features/controlLayers/konva/filters';
|
import { FILTER_MAP } from 'features/controlLayers/konva/filters';
|
||||||
import { loadImage } from 'features/controlLayers/konva/util';
|
import { loadImage } from 'features/controlLayers/konva/util';
|
||||||
import type { CanvasImageState, GetLoggingContext } from 'features/controlLayers/store/types';
|
import type { CanvasImageState, GetLoggingContext } from 'features/controlLayers/store/types';
|
||||||
@ -19,7 +20,7 @@ export class CanvasImageRenderer {
|
|||||||
static PLACEHOLDER_TEXT_NAME = `${CanvasImageRenderer.TYPE}_placeholder-text`;
|
static PLACEHOLDER_TEXT_NAME = `${CanvasImageRenderer.TYPE}_placeholder-text`;
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
parent: CanvasObjectRenderer;
|
parent: CanvasObjectRenderer | CanvasStagingArea;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
getLoggingContext: GetLoggingContext;
|
getLoggingContext: GetLoggingContext;
|
||||||
@ -36,7 +37,7 @@ export class CanvasImageRenderer {
|
|||||||
isError: boolean = false;
|
isError: boolean = false;
|
||||||
mutex = new Mutex();
|
mutex = new Mutex();
|
||||||
|
|
||||||
constructor(state: CanvasImageState, parent: CanvasObjectRenderer) {
|
constructor(state: CanvasImageState, parent: CanvasObjectRenderer | CanvasStagingArea) {
|
||||||
const { id, image } = state;
|
const { id, image } = state;
|
||||||
const { width, height } = image;
|
const { width, height } = image;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -97,18 +98,16 @@ export class CanvasImageRenderer {
|
|||||||
this.onFailedToLoadImage();
|
this.onFailedToLoadImage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Load the thumbnail first, but let the image load in parallel
|
||||||
loadImage(imageDTO.thumbnail_url)
|
loadImage(imageDTO.thumbnail_url)
|
||||||
.then((thumbnailElement) => {
|
.then((thumbnailElement) => {
|
||||||
this.thumbnailElement = thumbnailElement;
|
this.thumbnailElement = thumbnailElement;
|
||||||
this.mutex.runExclusive(this.updateImageElement);
|
this.updateImageElement();
|
||||||
})
|
|
||||||
.catch(this.onFailedToLoadImage);
|
|
||||||
loadImage(imageDTO.image_url)
|
|
||||||
.then((imageElement) => {
|
|
||||||
this.imageElement = imageElement;
|
|
||||||
this.mutex.runExclusive(this.updateImageElement);
|
|
||||||
})
|
})
|
||||||
.catch(this.onFailedToLoadImage);
|
.catch(this.onFailedToLoadImage);
|
||||||
|
|
||||||
|
this.imageElement = await loadImage(imageDTO.image_url);
|
||||||
|
await this.updateImageElement();
|
||||||
} catch {
|
} catch {
|
||||||
this.onFailedToLoadImage();
|
this.onFailedToLoadImage();
|
||||||
}
|
}
|
||||||
@ -123,21 +122,29 @@ export class CanvasImageRenderer {
|
|||||||
this.konva.placeholder.group.visible(true);
|
this.konva.placeholder.group.visible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateImageElement = () => {
|
updateImageElement = async () => {
|
||||||
|
const release = await this.mutex.acquire();
|
||||||
|
|
||||||
|
try {
|
||||||
const element = this.imageElement ?? this.thumbnailElement;
|
const element = this.imageElement ?? this.thumbnailElement;
|
||||||
|
const { width, height } = this.state.image;
|
||||||
|
|
||||||
if (element) {
|
if (element) {
|
||||||
if (this.konva.image && this.konva.image.image() !== element) {
|
if (this.konva.image) {
|
||||||
|
this.log.trace('Updating Konva image attrs');
|
||||||
this.konva.image.setAttrs({
|
this.konva.image.setAttrs({
|
||||||
image: element,
|
image: element,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
this.log.trace('Creating new Konva image');
|
||||||
this.konva.image = new Konva.Image({
|
this.konva.image = new Konva.Image({
|
||||||
name: CanvasImageRenderer.IMAGE_NAME,
|
name: CanvasImageRenderer.IMAGE_NAME,
|
||||||
listening: false,
|
listening: false,
|
||||||
image: element,
|
image: element,
|
||||||
width: this.state.image.width,
|
width,
|
||||||
height: this.state.image.height,
|
height,
|
||||||
});
|
});
|
||||||
this.konva.group.add(this.konva.image);
|
this.konva.group.add(this.konva.image);
|
||||||
}
|
}
|
||||||
@ -150,10 +157,16 @@ export class CanvasImageRenderer {
|
|||||||
this.konva.image.filters([]);
|
this.konva.image.filters([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.konva.placeholder.rect.setAttrs({ width, height });
|
||||||
|
this.konva.placeholder.text.setAttrs({ width, height, fontSize: width / 16 });
|
||||||
|
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.isError = false;
|
this.isError = false;
|
||||||
this.konva.placeholder.group.visible(false);
|
this.konva.placeholder.group.visible(false);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
update = async (state: CanvasImageState, force = false): Promise<boolean> => {
|
update = async (state: CanvasImageState, force = false): Promise<boolean> => {
|
||||||
@ -173,8 +186,6 @@ export class CanvasImageRenderer {
|
|||||||
this.konva.image?.clearCache();
|
this.konva.image?.clearCache();
|
||||||
this.konva.image?.filters([]);
|
this.konva.image?.filters([]);
|
||||||
}
|
}
|
||||||
this.konva.placeholder.rect.setAttrs({ width, height });
|
|
||||||
this.konva.placeholder.text.setAttrs({ width, height, fontSize: width / 16 });
|
|
||||||
this.state = state;
|
this.state = state;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import type { CanvasBrushLineRenderer } from 'features/controlLayers/konva/Canva
|
|||||||
import type { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
import type { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
||||||
import type { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
|
import type { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
|
||||||
import { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
import { CanvasProgressPreview } from 'features/controlLayers/konva/CanvasProgressPreview';
|
|
||||||
import type { CanvasRectRenderer } from 'features/controlLayers/konva/CanvasRect';
|
import type { CanvasRectRenderer } from 'features/controlLayers/konva/CanvasRect';
|
||||||
import type { CanvasTransformer } from 'features/controlLayers/konva/CanvasTransformer';
|
import type { CanvasTransformer } from 'features/controlLayers/konva/CanvasTransformer';
|
||||||
import { MAX_CANVAS_SCALE, MIN_CANVAS_SCALE } from 'features/controlLayers/konva/constants';
|
import { MAX_CANVAS_SCALE, MIN_CANVAS_SCALE } from 'features/controlLayers/konva/constants';
|
||||||
@ -19,7 +18,6 @@ import {
|
|||||||
nanoid,
|
nanoid,
|
||||||
} from 'features/controlLayers/konva/util';
|
} from 'features/controlLayers/konva/util';
|
||||||
import type { Extents, ExtentsResult, GetBboxTask, WorkerLogMessage } from 'features/controlLayers/konva/worker';
|
import type { Extents, ExtentsResult, GetBboxTask, WorkerLogMessage } from 'features/controlLayers/konva/worker';
|
||||||
import { $lastProgressEvent, $shouldShowStagedImage } from 'features/controlLayers/store/canvasV2Slice';
|
|
||||||
import type {
|
import type {
|
||||||
CanvasControlAdapterState,
|
CanvasControlAdapterState,
|
||||||
CanvasEntityIdentifier,
|
CanvasEntityIdentifier,
|
||||||
@ -49,14 +47,12 @@ import type { ImageCategory, ImageDTO } from 'services/api/types';
|
|||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
import { CanvasBackground } from './CanvasBackground';
|
import { CanvasBackground } from './CanvasBackground';
|
||||||
import { CanvasBbox } from './CanvasBbox';
|
|
||||||
import { CanvasControlAdapter } from './CanvasControlAdapter';
|
import { CanvasControlAdapter } from './CanvasControlAdapter';
|
||||||
import { CanvasLayerAdapter } from './CanvasLayerAdapter';
|
import { CanvasLayerAdapter } from './CanvasLayerAdapter';
|
||||||
import { CanvasMaskAdapter } from './CanvasMaskAdapter';
|
import { CanvasMaskAdapter } from './CanvasMaskAdapter';
|
||||||
import { CanvasPreview } from './CanvasPreview';
|
import { CanvasPreview } from './CanvasPreview';
|
||||||
import { CanvasStagingArea } from './CanvasStagingArea';
|
import { CanvasStagingArea } from './CanvasStagingArea';
|
||||||
import { CanvasStateApi } from './CanvasStateApi';
|
import { CanvasStateApi } from './CanvasStateApi';
|
||||||
import { CanvasTool } from './CanvasTool';
|
|
||||||
import { setStageEventHandlers } from './events';
|
import { setStageEventHandlers } from './events';
|
||||||
|
|
||||||
// type Extents = {
|
// type Extents = {
|
||||||
@ -159,15 +155,6 @@ export class CanvasManager {
|
|||||||
|
|
||||||
this.transformingEntity = new PubSub<CanvasEntityIdentifier | null>(null);
|
this.transformingEntity = new PubSub<CanvasEntityIdentifier | null>(null);
|
||||||
this.toolState = new PubSub(this.stateApi.getToolState());
|
this.toolState = new PubSub(this.stateApi.getToolState());
|
||||||
this.currentFill = new PubSub(this.getCurrentFill());
|
|
||||||
this.selectedEntityIdentifier = new PubSub(
|
|
||||||
this.stateApi.getState().selectedEntityIdentifier,
|
|
||||||
(a, b) => a?.id === b?.id
|
|
||||||
);
|
|
||||||
this.selectedEntity = new PubSub(
|
|
||||||
this.getSelectedEntity(),
|
|
||||||
(a, b) => a?.state === b?.state && a?.adapter === b?.adapter
|
|
||||||
);
|
|
||||||
|
|
||||||
this._prevState = this.stateApi.getState();
|
this._prevState = this.stateApi.getState();
|
||||||
|
|
||||||
@ -187,13 +174,8 @@ export class CanvasManager {
|
|||||||
uploadImage,
|
uploadImage,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.preview = new CanvasPreview(
|
this.preview = new CanvasPreview(this);
|
||||||
new CanvasBbox(this),
|
this.stage.add(this.preview.getLayer());
|
||||||
new CanvasTool(this),
|
|
||||||
new CanvasStagingArea(this),
|
|
||||||
new CanvasProgressPreview(this)
|
|
||||||
);
|
|
||||||
this.stage.add(this.preview.layer);
|
|
||||||
|
|
||||||
this.background = new CanvasBackground(this);
|
this.background = new CanvasBackground(this);
|
||||||
this.stage.add(this.background.konva.layer);
|
this.stage.add(this.background.konva.layer);
|
||||||
@ -226,6 +208,16 @@ export class CanvasManager {
|
|||||||
this.log.error('Worker message error');
|
this.log.error('Worker message error');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.currentFill = new PubSub(this.getCurrentFill());
|
||||||
|
this.selectedEntityIdentifier = new PubSub(
|
||||||
|
this.stateApi.getState().selectedEntityIdentifier,
|
||||||
|
(a, b) => a?.id === b?.id
|
||||||
|
);
|
||||||
|
this.selectedEntity = new PubSub(
|
||||||
|
this.getSelectedEntity(),
|
||||||
|
(a, b) => a?.state === b?.state && a?.adapter === b?.adapter
|
||||||
|
);
|
||||||
|
|
||||||
this.inpaintMask = new CanvasMaskAdapter(this.stateApi.getInpaintMaskState(), this);
|
this.inpaintMask = new CanvasMaskAdapter(this.stateApi.getInpaintMaskState(), this);
|
||||||
this.stage.add(this.inpaintMask.konva.layer);
|
this.stage.add(this.inpaintMask.konva.layer);
|
||||||
}
|
}
|
||||||
@ -249,10 +241,6 @@ export class CanvasManager {
|
|||||||
this._worker.postMessage(task, [data.buffer]);
|
this._worker.postMessage(task, [data.buffer]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderProgressPreview() {
|
|
||||||
await this.preview.progressPreview.render(this.stateApi.$lastProgressEvent.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
async renderControlAdapters() {
|
async renderControlAdapters() {
|
||||||
const { entities } = this.stateApi.getControlAdaptersState();
|
const { entities } = this.stateApi.getControlAdaptersState();
|
||||||
|
|
||||||
@ -291,7 +279,7 @@ export class CanvasManager {
|
|||||||
this.regions.get(rg.id)?.konva.layer.zIndex(++zIndex);
|
this.regions.get(rg.id)?.konva.layer.zIndex(++zIndex);
|
||||||
}
|
}
|
||||||
this.inpaintMask.konva.layer.zIndex(++zIndex);
|
this.inpaintMask.konva.layer.zIndex(++zIndex);
|
||||||
this.preview.layer.zIndex(++zIndex);
|
this.preview.getLayer().zIndex(++zIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
fitStageToContainer() {
|
fitStageToContainer() {
|
||||||
@ -611,25 +599,6 @@ export class CanvasManager {
|
|||||||
|
|
||||||
const unsubscribeRenderer = this._store.subscribe(this.render);
|
const unsubscribeRenderer = this._store.subscribe(this.render);
|
||||||
|
|
||||||
// When we this flag, we need to render the staging area
|
|
||||||
const unsubscribeShouldShowStagedImage = $shouldShowStagedImage.subscribe(
|
|
||||||
async (shouldShowStagedImage, prevShouldShowStagedImage) => {
|
|
||||||
if (shouldShowStagedImage !== prevShouldShowStagedImage) {
|
|
||||||
this.log.debug('Rendering staging area');
|
|
||||||
await this.preview.stagingArea.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const unsubscribeLastProgressEvent = $lastProgressEvent.subscribe(
|
|
||||||
async (lastProgressEvent, prevLastProgressEvent) => {
|
|
||||||
if (lastProgressEvent !== prevLastProgressEvent) {
|
|
||||||
this.log.debug('Rendering progress image');
|
|
||||||
await this.preview.progressPreview.render(lastProgressEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.log.debug('First render of konva stage');
|
this.log.debug('First render of konva stage');
|
||||||
this.preview.tool.render();
|
this.preview.tool.render();
|
||||||
this.render();
|
this.render();
|
||||||
@ -650,8 +619,6 @@ export class CanvasManager {
|
|||||||
this.preview.destroy();
|
this.preview.destroy();
|
||||||
unsubscribeRenderer();
|
unsubscribeRenderer();
|
||||||
unsubscribeListeners();
|
unsubscribeListeners();
|
||||||
unsubscribeShouldShowStagedImage();
|
|
||||||
unsubscribeLastProgressEvent();
|
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,40 +1,51 @@
|
|||||||
import type { CanvasProgressPreview } from 'features/controlLayers/konva/CanvasProgressPreview';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasProgressImage } from 'features/controlLayers/konva/CanvasProgressImage';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
|
||||||
import type { CanvasBbox } from './CanvasBbox';
|
import { CanvasBbox } from './CanvasBbox';
|
||||||
import type { CanvasStagingArea } from './CanvasStagingArea';
|
import { CanvasStagingArea } from './CanvasStagingArea';
|
||||||
import type { CanvasTool } from './CanvasTool';
|
import { CanvasTool } from './CanvasTool';
|
||||||
|
|
||||||
export class CanvasPreview {
|
export class CanvasPreview {
|
||||||
|
manager: CanvasManager;
|
||||||
|
|
||||||
|
konva: {
|
||||||
layer: Konva.Layer;
|
layer: Konva.Layer;
|
||||||
|
};
|
||||||
|
|
||||||
tool: CanvasTool;
|
tool: CanvasTool;
|
||||||
bbox: CanvasBbox;
|
bbox: CanvasBbox;
|
||||||
stagingArea: CanvasStagingArea;
|
stagingArea: CanvasStagingArea;
|
||||||
progressPreview: CanvasProgressPreview;
|
progressImage: CanvasProgressImage;
|
||||||
|
|
||||||
constructor(
|
constructor(manager: CanvasManager) {
|
||||||
bbox: CanvasBbox,
|
this.manager = manager;
|
||||||
tool: CanvasTool,
|
this.konva = {
|
||||||
stagingArea: CanvasStagingArea,
|
layer: new Konva.Layer({ listening: true, imageSmoothingEnabled: false }),
|
||||||
progressPreview: CanvasProgressPreview
|
};
|
||||||
) {
|
|
||||||
this.layer = new Konva.Layer({ listening: true, imageSmoothingEnabled: false });
|
|
||||||
|
|
||||||
this.stagingArea = stagingArea;
|
this.stagingArea = new CanvasStagingArea(this);
|
||||||
this.layer.add(this.stagingArea.konva.group);
|
this.konva.layer.add(...this.stagingArea.getNodes());
|
||||||
|
|
||||||
this.bbox = bbox;
|
this.progressImage = new CanvasProgressImage(this);
|
||||||
this.layer.add(this.bbox.konva.group);
|
this.konva.layer.add(...this.progressImage.getNodes());
|
||||||
|
|
||||||
this.tool = tool;
|
this.bbox = new CanvasBbox(this);
|
||||||
this.layer.add(this.tool.konva.group);
|
this.konva.layer.add(this.bbox.konva.group);
|
||||||
|
|
||||||
this.progressPreview = progressPreview;
|
this.tool = new CanvasTool(this);
|
||||||
this.layer.add(this.progressPreview.konva.group);
|
this.konva.layer.add(this.tool.konva.group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLayer = () => {
|
||||||
|
return this.konva.layer;
|
||||||
|
};
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
// this.stagingArea.destroy(); // TODO(psyche): implement destroy
|
||||||
|
this.progressImage.destroy();
|
||||||
|
// this.bbox.destroy(); // TODO(psyche): implement destroy
|
||||||
this.tool.destroy();
|
this.tool.destroy();
|
||||||
this.layer.destroy();
|
this.konva.layer.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { loadImage } from 'features/controlLayers/konva/util';
|
import { Mutex } from 'async-mutex';
|
||||||
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import type { CanvasPreview } from 'features/controlLayers/konva/CanvasPreview';
|
||||||
|
import { getPrefixedId, loadImage } from 'features/controlLayers/konva/util';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
import type { InvocationDenoiseProgressEvent } from 'services/events/types';
|
||||||
|
|
||||||
export class CanvasProgressImage {
|
export class CanvasProgressImage {
|
||||||
static NAME_PREFIX = 'progress-image';
|
static NAME_PREFIX = 'progress-image';
|
||||||
@ -7,53 +11,86 @@ export class CanvasProgressImage {
|
|||||||
static IMAGE_NAME = `${CanvasProgressImage.NAME_PREFIX}_image`;
|
static IMAGE_NAME = `${CanvasProgressImage.NAME_PREFIX}_image`;
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
progressImageId: string | null;
|
parent: CanvasPreview;
|
||||||
|
manager: CanvasManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of subscriptions that should be cleaned up when the transformer is destroyed.
|
||||||
|
*/
|
||||||
|
subscriptions: Set<() => void> = new Set();
|
||||||
|
|
||||||
|
progressImageId: string | null = null;
|
||||||
konva: {
|
konva: {
|
||||||
group: Konva.Group;
|
group: Konva.Group;
|
||||||
image: Konva.Image | null; // The image is loaded asynchronously, so it may not be available immediately
|
image: Konva.Image | null; // The image is loaded asynchronously, so it may not be available immediately
|
||||||
};
|
};
|
||||||
isLoading: boolean;
|
isLoading: boolean = false;
|
||||||
isError: boolean;
|
isError: boolean = false;
|
||||||
|
imageElement: HTMLImageElement | null = null;
|
||||||
|
|
||||||
constructor(arg: { id: string }) {
|
lastProgressEvent: InvocationDenoiseProgressEvent | null = null;
|
||||||
const { id } = arg;
|
|
||||||
|
mutex: Mutex = new Mutex();
|
||||||
|
|
||||||
|
constructor(parent: CanvasPreview) {
|
||||||
|
this.id = getPrefixedId(CanvasProgressImage.NAME_PREFIX);
|
||||||
|
this.parent = parent;
|
||||||
|
this.manager = parent.manager;
|
||||||
this.konva = {
|
this.konva = {
|
||||||
group: new Konva.Group({ name: CanvasProgressImage.GROUP_NAME, listening: false }),
|
group: new Konva.Group({ name: CanvasProgressImage.GROUP_NAME, listening: false }),
|
||||||
image: null,
|
image: null,
|
||||||
};
|
};
|
||||||
this.id = id;
|
|
||||||
this.progressImageId = null;
|
this.manager.stateApi.$lastProgressEvent.listen((event) => {
|
||||||
this.isLoading = false;
|
this.lastProgressEvent = event;
|
||||||
this.isError = false;
|
this.render();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateImageSource(
|
getNodes = () => {
|
||||||
progressImageId: string,
|
return [this.konva.group];
|
||||||
dataURL: string,
|
};
|
||||||
x: number,
|
|
||||||
y: number,
|
render = async () => {
|
||||||
width: number,
|
const release = await this.mutex.acquire();
|
||||||
height: number
|
|
||||||
) {
|
if (!this.lastProgressEvent) {
|
||||||
if (this.isLoading) {
|
this.konva.group.visible(false);
|
||||||
|
this.imageElement = null;
|
||||||
|
this.isLoading = false;
|
||||||
|
this.isError = false;
|
||||||
|
release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { isStaging } = this.manager.stateApi.getSession();
|
||||||
|
|
||||||
|
if (!isStaging) {
|
||||||
|
release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
|
const { x, y } = this.manager.stateApi.getBbox().rect;
|
||||||
|
const { dataURL, width, height } = this.lastProgressEvent.progress_image;
|
||||||
try {
|
try {
|
||||||
const imageEl = await loadImage(dataURL);
|
this.imageElement = await loadImage(dataURL);
|
||||||
if (this.konva.image) {
|
if (this.konva.image) {
|
||||||
|
console.log('UPDATING PROGRESS IMAGE')
|
||||||
this.konva.image.setAttrs({
|
this.konva.image.setAttrs({
|
||||||
image: imageEl,
|
image: this.imageElement,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
console.log('CREATING NEW PROGRESS IMAGE')
|
||||||
this.konva.image = new Konva.Image({
|
this.konva.image = new Konva.Image({
|
||||||
name: CanvasProgressImage.IMAGE_NAME,
|
name: CanvasProgressImage.IMAGE_NAME,
|
||||||
listening: false,
|
listening: false,
|
||||||
image: imageEl,
|
image: this.imageElement,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
@ -61,14 +98,19 @@ export class CanvasProgressImage {
|
|||||||
});
|
});
|
||||||
this.konva.group.add(this.konva.image);
|
this.konva.group.add(this.konva.image);
|
||||||
}
|
}
|
||||||
this.isLoading = false;
|
this.konva.group.visible(true);
|
||||||
this.id = progressImageId;
|
|
||||||
} catch {
|
} catch {
|
||||||
this.isError = true;
|
this.isError = true;
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false;
|
||||||
|
release();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
destroy() {
|
destroy = () => {
|
||||||
this.konva.group.destroy();
|
for (const unsubscribe of this.subscriptions) {
|
||||||
|
unsubscribe();
|
||||||
}
|
}
|
||||||
|
this.konva.group.destroy();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
|
||||||
import { CanvasProgressImage } from 'features/controlLayers/konva/CanvasProgressImage';
|
|
||||||
import Konva from 'konva';
|
|
||||||
import type { InvocationDenoiseProgressEvent } from 'services/events/types';
|
|
||||||
|
|
||||||
export class CanvasProgressPreview {
|
|
||||||
static NAME_PREFIX = 'progress-preview';
|
|
||||||
static GROUP_NAME = `${CanvasProgressPreview.NAME_PREFIX}_group`;
|
|
||||||
|
|
||||||
konva: {
|
|
||||||
group: Konva.Group;
|
|
||||||
progressImage: CanvasProgressImage;
|
|
||||||
};
|
|
||||||
manager: CanvasManager;
|
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
|
||||||
this.manager = manager;
|
|
||||||
this.konva = {
|
|
||||||
group: new Konva.Group({ name: CanvasProgressPreview.GROUP_NAME, listening: false }),
|
|
||||||
progressImage: new CanvasProgressImage({ id: 'progress-image' }),
|
|
||||||
};
|
|
||||||
this.konva.group.add(this.konva.progressImage.konva.group);
|
|
||||||
}
|
|
||||||
|
|
||||||
async render(lastProgressEvent: InvocationDenoiseProgressEvent | null) {
|
|
||||||
const bboxRect = this.manager.stateApi.getBbox().rect;
|
|
||||||
const session = this.manager.stateApi.getSession();
|
|
||||||
|
|
||||||
if (lastProgressEvent && session.isStaging) {
|
|
||||||
const { invocation, step, progress_image } = lastProgressEvent;
|
|
||||||
const { dataURL } = progress_image;
|
|
||||||
const { x, y, width, height } = bboxRect;
|
|
||||||
const progressImageId = `${invocation.id}_${step}`;
|
|
||||||
if (
|
|
||||||
!this.konva.progressImage.isLoading &&
|
|
||||||
!this.konva.progressImage.isError &&
|
|
||||||
this.konva.progressImage.progressImageId !== progressImageId
|
|
||||||
) {
|
|
||||||
await this.konva.progressImage.updateImageSource(progressImageId, dataURL, x, y, width, height);
|
|
||||||
this.konva.progressImage.konva.group.visible(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.konva.progressImage.konva.group.visible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
import { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
|
import { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import type { CanvasPreview } from 'features/controlLayers/konva/CanvasPreview';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import type { GetLoggingContext, StagingAreaImage } from 'features/controlLayers/store/types';
|
import type { GetLoggingContext, StagingAreaImage } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
@ -10,6 +11,7 @@ export class CanvasStagingArea {
|
|||||||
static GROUP_NAME = `${CanvasStagingArea.TYPE}_group`;
|
static GROUP_NAME = `${CanvasStagingArea.TYPE}_group`;
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
|
parent: CanvasPreview;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
getLoggingContext: GetLoggingContext;
|
getLoggingContext: GetLoggingContext;
|
||||||
@ -19,9 +21,15 @@ export class CanvasStagingArea {
|
|||||||
image: CanvasImageRenderer | null;
|
image: CanvasImageRenderer | null;
|
||||||
selectedImage: StagingAreaImage | null;
|
selectedImage: StagingAreaImage | null;
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
/**
|
||||||
|
* A set of subscriptions that should be cleaned up when the transformer is destroyed.
|
||||||
|
*/
|
||||||
|
subscriptions: Set<() => void> = new Set();
|
||||||
|
|
||||||
|
constructor(parent: CanvasPreview) {
|
||||||
this.id = getPrefixedId(CanvasStagingArea.TYPE);
|
this.id = getPrefixedId(CanvasStagingArea.TYPE);
|
||||||
this.manager = manager;
|
this.parent = parent;
|
||||||
|
this.manager = this.parent.manager;
|
||||||
this.getLoggingContext = this.manager.buildGetLoggingContext(this);
|
this.getLoggingContext = this.manager.buildGetLoggingContext(this);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.debug('Creating staging area');
|
this.log.debug('Creating staging area');
|
||||||
@ -29,14 +37,17 @@ export class CanvasStagingArea {
|
|||||||
this.konva = { group: new Konva.Group({ name: CanvasStagingArea.GROUP_NAME, listening: false }) };
|
this.konva = { group: new Konva.Group({ name: CanvasStagingArea.GROUP_NAME, listening: false }) };
|
||||||
this.image = null;
|
this.image = null;
|
||||||
this.selectedImage = null;
|
this.selectedImage = null;
|
||||||
|
|
||||||
|
this.subscriptions.add(this.manager.stateApi.$shouldShowStagedImage.listen(this.render));
|
||||||
}
|
}
|
||||||
|
|
||||||
render = async () => {
|
render = async () => {
|
||||||
const session = this.manager.stateApi.getSession();
|
const session = this.manager.stateApi.getSession();
|
||||||
const bboxRect = this.manager.stateApi.getBbox().rect;
|
const { rect } = this.manager.stateApi.getBbox();
|
||||||
const shouldShowStagedImage = this.manager.stateApi.$shouldShowStagedImage.get();
|
const shouldShowStagedImage = this.manager.stateApi.$shouldShowStagedImage.get();
|
||||||
|
|
||||||
this.selectedImage = session.stagedImages[session.selectedStagedImageIndex] ?? null;
|
this.selectedImage = session.stagedImages[session.selectedStagedImageIndex] ?? null;
|
||||||
|
this.konva.group.position({ x: rect.x, y: rect.y });
|
||||||
|
|
||||||
if (this.selectedImage) {
|
if (this.selectedImage) {
|
||||||
const { imageDTO, offsetX, offsetY } = this.selectedImage;
|
const { imageDTO, offsetX, offsetY } = this.selectedImage;
|
||||||
@ -47,10 +58,6 @@ export class CanvasStagingArea {
|
|||||||
{
|
{
|
||||||
id: 'staging-area-image',
|
id: 'staging-area-image',
|
||||||
type: 'image',
|
type: 'image',
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
filters: [],
|
filters: [],
|
||||||
image: {
|
image: {
|
||||||
image_name: image_name,
|
image_name: image_name,
|
||||||
@ -63,11 +70,7 @@ export class CanvasStagingArea {
|
|||||||
this.konva.group.add(this.image.konva.group);
|
this.konva.group.add(this.image.konva.group);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.image.isLoading && !this.image.isError && this.image.imageName !== imageDTO.image_name) {
|
if (!this.image.isLoading && !this.image.isError) {
|
||||||
this.image.konva.image?.width(imageDTO.width);
|
|
||||||
this.image.konva.image?.height(imageDTO.height);
|
|
||||||
this.image.konva.group.x(bboxRect.x + offsetX);
|
|
||||||
this.image.konva.group.y(bboxRect.y + offsetY);
|
|
||||||
await this.image.updateImageSource(imageDTO.image_name);
|
await this.image.updateImageSource(imageDTO.image_name);
|
||||||
this.manager.stateApi.$lastProgressEvent.set(null);
|
this.manager.stateApi.$lastProgressEvent.set(null);
|
||||||
}
|
}
|
||||||
@ -77,6 +80,22 @@ export class CanvasStagingArea {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getNodes = () => {
|
||||||
|
return [this.konva.group];
|
||||||
|
};
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
if (this.image) {
|
||||||
|
this.image.destroy();
|
||||||
|
}
|
||||||
|
for (const unsubscribe of this.subscriptions) {
|
||||||
|
unsubscribe();
|
||||||
|
}
|
||||||
|
for (const node of this.getNodes()) {
|
||||||
|
node.destroy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
repr = () => {
|
repr = () => {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import type { CanvasPreview } from 'features/controlLayers/konva/CanvasPreview';
|
||||||
import {
|
import {
|
||||||
BRUSH_BORDER_INNER_COLOR,
|
BRUSH_BORDER_INNER_COLOR,
|
||||||
BRUSH_BORDER_OUTER_COLOR,
|
BRUSH_BORDER_OUTER_COLOR,
|
||||||
@ -25,6 +26,7 @@ export class CanvasTool {
|
|||||||
static ERASER_INNER_BORDER_CIRCLE_NAME = `${CanvasTool.ERASER_NAME_PREFIX}_inner-border-circle`;
|
static ERASER_INNER_BORDER_CIRCLE_NAME = `${CanvasTool.ERASER_NAME_PREFIX}_inner-border-circle`;
|
||||||
static ERASER_OUTER_BORDER_CIRCLE_NAME = `${CanvasTool.ERASER_NAME_PREFIX}_outer-border-circle`;
|
static ERASER_OUTER_BORDER_CIRCLE_NAME = `${CanvasTool.ERASER_NAME_PREFIX}_outer-border-circle`;
|
||||||
|
|
||||||
|
parent: CanvasPreview;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
konva: {
|
konva: {
|
||||||
group: Konva.Group;
|
group: Konva.Group;
|
||||||
@ -47,8 +49,9 @@ export class CanvasTool {
|
|||||||
*/
|
*/
|
||||||
subscriptions: Set<() => void> = new Set();
|
subscriptions: Set<() => void> = new Set();
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(parent: CanvasPreview) {
|
||||||
this.manager = manager;
|
this.parent = parent;
|
||||||
|
this.manager = this.parent.manager;
|
||||||
this.konva = {
|
this.konva = {
|
||||||
group: new Konva.Group({ name: CanvasTool.GROUP_NAME }),
|
group: new Konva.Group({ name: CanvasTool.GROUP_NAME }),
|
||||||
brush: {
|
brush: {
|
||||||
|
@ -48,13 +48,6 @@ export const bboxReducers = {
|
|||||||
state.bbox.aspectRatio.id = 'Free';
|
state.bbox.aspectRatio.id = 'Free';
|
||||||
state.bbox.aspectRatio.isLocked = false;
|
state.bbox.aspectRatio.isLocked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.session.isActive) {
|
|
||||||
if (state.initialImage.imageObject) {
|
|
||||||
state.initialImage.imageObject.width = state.bbox.rect.width;
|
|
||||||
state.initialImage.imageObject.height = state.bbox.rect.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
bboxHeightChanged: (
|
bboxHeightChanged: (
|
||||||
state,
|
state,
|
||||||
@ -73,13 +66,6 @@ export const bboxReducers = {
|
|||||||
state.bbox.aspectRatio.id = 'Free';
|
state.bbox.aspectRatio.id = 'Free';
|
||||||
state.bbox.aspectRatio.isLocked = false;
|
state.bbox.aspectRatio.isLocked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.session.isActive) {
|
|
||||||
if (state.initialImage.imageObject) {
|
|
||||||
state.initialImage.imageObject.width = state.bbox.rect.width;
|
|
||||||
state.initialImage.imageObject.height = state.bbox.rect.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
bboxAspectRatioLockToggled: (state) => {
|
bboxAspectRatioLockToggled: (state) => {
|
||||||
state.bbox.aspectRatio.isLocked = !state.bbox.aspectRatio.isLocked;
|
state.bbox.aspectRatio.isLocked = !state.bbox.aspectRatio.isLocked;
|
||||||
@ -99,12 +85,6 @@ export const bboxReducers = {
|
|||||||
state.bbox.rect.width = width;
|
state.bbox.rect.width = width;
|
||||||
state.bbox.rect.height = height;
|
state.bbox.rect.height = height;
|
||||||
}
|
}
|
||||||
if (!state.session.isActive) {
|
|
||||||
if (state.initialImage.imageObject) {
|
|
||||||
state.initialImage.imageObject.width = state.bbox.rect.width;
|
|
||||||
state.initialImage.imageObject.height = state.bbox.rect.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
bboxDimensionsSwapped: (state) => {
|
bboxDimensionsSwapped: (state) => {
|
||||||
state.bbox.aspectRatio.value = 1 / state.bbox.aspectRatio.value;
|
state.bbox.aspectRatio.value = 1 / state.bbox.aspectRatio.value;
|
||||||
@ -122,12 +102,6 @@ export const bboxReducers = {
|
|||||||
state.bbox.rect.height = height;
|
state.bbox.rect.height = height;
|
||||||
state.bbox.aspectRatio.id = ASPECT_RATIO_MAP[state.bbox.aspectRatio.id].inverseID;
|
state.bbox.aspectRatio.id = ASPECT_RATIO_MAP[state.bbox.aspectRatio.id].inverseID;
|
||||||
}
|
}
|
||||||
if (!state.session.isActive) {
|
|
||||||
if (state.initialImage.imageObject) {
|
|
||||||
state.initialImage.imageObject.width = state.bbox.rect.width;
|
|
||||||
state.initialImage.imageObject.height = state.bbox.rect.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
bboxSizeOptimized: (state) => {
|
bboxSizeOptimized: (state) => {
|
||||||
const optimalDimension = getOptimalDimension(state.params.model);
|
const optimalDimension = getOptimalDimension(state.params.model);
|
||||||
@ -140,11 +114,5 @@ export const bboxReducers = {
|
|||||||
state.bbox.rect.width = optimalDimension;
|
state.bbox.rect.width = optimalDimension;
|
||||||
state.bbox.rect.height = optimalDimension;
|
state.bbox.rect.height = optimalDimension;
|
||||||
}
|
}
|
||||||
if (!state.session.isActive) {
|
|
||||||
if (state.initialImage.imageObject) {
|
|
||||||
state.initialImage.imageObject.width = state.bbox.rect.width;
|
|
||||||
state.initialImage.imageObject.height = state.bbox.rect.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
} satisfies SliceCaseReducers<CanvasV2State>;
|
||||||
|
@ -411,7 +411,6 @@ export const {
|
|||||||
bboxSizeOptimized,
|
bboxSizeOptimized,
|
||||||
// layers
|
// layers
|
||||||
layerAdded,
|
layerAdded,
|
||||||
layerAddedFromImage,
|
|
||||||
layerRecalled,
|
layerRecalled,
|
||||||
layerOpacityChanged,
|
layerOpacityChanged,
|
||||||
layerAllDeleted,
|
layerAllDeleted,
|
||||||
|
@ -4,7 +4,7 @@ import { merge } from 'lodash-es';
|
|||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
import type { CanvasImageState, CanvasLayerState, CanvasV2State } from './types';
|
import type { CanvasLayerState, CanvasV2State } from './types';
|
||||||
import { imageDTOToImageWithDims } from './types';
|
import { imageDTOToImageWithDims } from './types';
|
||||||
|
|
||||||
export const selectLayer = (state: CanvasV2State, id: string) => state.layers.entities.find((layer) => layer.id === id);
|
export const selectLayer = (state: CanvasV2State, id: string) => state.layers.entities.find((layer) => layer.id === id);
|
||||||
@ -16,8 +16,11 @@ export const selectLayerOrThrow = (state: CanvasV2State, id: string) => {
|
|||||||
|
|
||||||
export const layersReducers = {
|
export const layersReducers = {
|
||||||
layerAdded: {
|
layerAdded: {
|
||||||
reducer: (state, action: PayloadAction<{ id: string; overrides?: Partial<CanvasLayerState> }>) => {
|
reducer: (
|
||||||
const { id } = action.payload;
|
state,
|
||||||
|
action: PayloadAction<{ id: string; overrides?: Partial<CanvasLayerState>; isSelected?: boolean }>
|
||||||
|
) => {
|
||||||
|
const { id, overrides, isSelected } = action.payload;
|
||||||
const layer: CanvasLayerState = {
|
const layer: CanvasLayerState = {
|
||||||
id,
|
id,
|
||||||
type: 'layer',
|
type: 'layer',
|
||||||
@ -27,12 +30,14 @@ export const layersReducers = {
|
|||||||
position: { x: 0, y: 0 },
|
position: { x: 0, y: 0 },
|
||||||
imageCache: null,
|
imageCache: null,
|
||||||
};
|
};
|
||||||
merge(layer, action.payload.overrides);
|
merge(layer, overrides);
|
||||||
state.layers.entities.push(layer);
|
state.layers.entities.push(layer);
|
||||||
|
if (isSelected) {
|
||||||
state.selectedEntityIdentifier = { type: 'layer', id };
|
state.selectedEntityIdentifier = { type: 'layer', id };
|
||||||
|
}
|
||||||
state.layers.imageCache = null;
|
state.layers.imageCache = null;
|
||||||
},
|
},
|
||||||
prepare: (payload: { overrides?: Partial<CanvasLayerState> }) => ({
|
prepare: (payload: { overrides?: Partial<CanvasLayerState>; isSelected?: boolean }) => ({
|
||||||
payload: { ...payload, id: getPrefixedId('layer') },
|
payload: { ...payload, id: getPrefixedId('layer') },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -42,26 +47,6 @@ export const layersReducers = {
|
|||||||
state.selectedEntityIdentifier = { type: 'layer', id: data.id };
|
state.selectedEntityIdentifier = { type: 'layer', id: data.id };
|
||||||
state.layers.imageCache = null;
|
state.layers.imageCache = null;
|
||||||
},
|
},
|
||||||
layerAddedFromImage: {
|
|
||||||
reducer: (state, action: PayloadAction<{ id: string; imageObject: CanvasImageState }>) => {
|
|
||||||
const { id, imageObject } = action.payload;
|
|
||||||
const layer: CanvasLayerState = {
|
|
||||||
id,
|
|
||||||
type: 'layer',
|
|
||||||
isEnabled: true,
|
|
||||||
objects: [imageObject],
|
|
||||||
opacity: 1,
|
|
||||||
position: { x: 0, y: 0 },
|
|
||||||
imageCache: null,
|
|
||||||
};
|
|
||||||
state.layers.entities.push(layer);
|
|
||||||
state.selectedEntityIdentifier = { type: 'layer', id };
|
|
||||||
state.layers.imageCache = null;
|
|
||||||
},
|
|
||||||
prepare: (payload: { imageObject: CanvasImageState }) => ({
|
|
||||||
payload: { ...payload, id: getPrefixedId('layer') },
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
layerAllDeleted: (state) => {
|
layerAllDeleted: (state) => {
|
||||||
state.layers.entities = [];
|
state.layers.entities = [];
|
||||||
state.layers.imageCache = null;
|
state.layers.imageCache = null;
|
||||||
|
Loading…
Reference in New Issue
Block a user