mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): add CanvasModuleBase class to standardize canvas APIs
I did this ages ago but undid it for some reason, not sure why. Caught a few issues related to subscriptions.
This commit is contained in:
parent
17e76981bb
commit
301da97670
@ -38,8 +38,8 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const manager = new CanvasManager(stage, container, store, socket);
|
const manager = new CanvasManager(stage, container, store, socket);
|
||||||
const cleanup = manager.initialize();
|
manager.initialize();
|
||||||
return cleanup;
|
return manager.destroy;
|
||||||
}, [asPreview, container, socket, stage, store]);
|
}, [asPreview, container, socket, stage, store]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
@ -1,29 +1,35 @@
|
|||||||
import { getArbitraryBaseColor } from '@invoke-ai/ui-library';
|
import { getArbitraryBaseColor } from '@invoke-ai/ui-library';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasBackgroundModule {
|
export class CanvasBackgroundModule extends CanvasModuleBase {
|
||||||
readonly type = 'background_grid';
|
readonly type = 'background';
|
||||||
|
|
||||||
static GRID_LINE_COLOR_COARSE = getArbitraryBaseColor(27);
|
static GRID_LINE_COLOR_COARSE = getArbitraryBaseColor(27);
|
||||||
static GRID_LINE_COLOR_FINE = getArbitraryBaseColor(18);
|
static GRID_LINE_COLOR_FINE = getArbitraryBaseColor(18);
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
|
path: string[];
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
log: Logger;
|
||||||
|
|
||||||
konva: {
|
konva: {
|
||||||
layer: Konva.Layer;
|
layer: Konva.Layer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of subscriptions that should be cleaned up when the transformer is destroyed.
|
|
||||||
*/
|
|
||||||
subscriptions: Set<() => void> = new Set();
|
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(manager: CanvasManager) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.path = this.manager.path.concat(this.id);
|
||||||
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
|
this.log.debug('Creating background module');
|
||||||
|
|
||||||
this.konva = { layer: new Konva.Layer({ name: `${this.type}:layer`, listening: false }) };
|
this.konva = { layer: new Konva.Layer({ name: `${this.type}:layer`, listening: false }) };
|
||||||
|
|
||||||
this.subscriptions.add(
|
this.subscriptions.add(
|
||||||
@ -116,9 +122,8 @@ export class CanvasBackgroundModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
for (const cleanup of this.subscriptions) {
|
this.log.trace('Destroying background module');
|
||||||
cleanup();
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
}
|
|
||||||
this.konva.layer.destroy();
|
this.konva.layer.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -145,4 +150,16 @@ export class CanvasBackgroundModule {
|
|||||||
}
|
}
|
||||||
return 256;
|
return 256;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
path: this.path,
|
||||||
|
type: this.type,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
getLoggingContext = () => {
|
||||||
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
import type { SerializableObject } from 'common/types';
|
||||||
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 { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import type { Rect } from 'features/controlLayers/store/types';
|
import type { Rect } from 'features/controlLayers/store/types';
|
||||||
@ -22,20 +23,17 @@ const ALL_ANCHORS: string[] = [
|
|||||||
const CORNER_ANCHORS: string[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
const CORNER_ANCHORS: string[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
||||||
const NO_ANCHORS: string[] = [];
|
const NO_ANCHORS: string[] = [];
|
||||||
|
|
||||||
export class CanvasBboxModule {
|
export class CanvasBboxModule extends CanvasModuleBase {
|
||||||
readonly type = 'generation_bbox';
|
readonly type = 'bbox';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
parent: CanvasPreviewModule;
|
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of subscriptions that should be cleaned up when the transformer is destroyed.
|
|
||||||
*/
|
|
||||||
subscriptions: Set<() => void> = new Set();
|
subscriptions: Set<() => void> = new Set();
|
||||||
|
|
||||||
|
parent: CanvasPreviewModule;
|
||||||
|
|
||||||
konva: {
|
konva: {
|
||||||
group: Konva.Group;
|
group: Konva.Group;
|
||||||
rect: Konva.Rect;
|
rect: Konva.Rect;
|
||||||
@ -43,13 +41,14 @@ export class CanvasBboxModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor(parent: CanvasPreviewModule) {
|
constructor(parent: CanvasPreviewModule) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = this.parent.manager;
|
this.manager = this.parent.manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
this.log.trace('Creating bbox');
|
this.log.debug('Creating bbox module');
|
||||||
|
|
||||||
// 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.
|
||||||
@ -238,7 +237,7 @@ export class CanvasBboxModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
this.log.trace('Rendering generation bbox');
|
this.log.trace('Rendering bbox module');
|
||||||
|
|
||||||
const bbox = this.manager.stateApi.getBbox();
|
const bbox = this.manager.stateApi.getBbox();
|
||||||
const tool = this.manager.stateApi.$tool.get();
|
const tool = this.manager.stateApi.$tool.get();
|
||||||
@ -261,15 +260,21 @@ export class CanvasBboxModule {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
this.log.trace('Destroying generation bbox');
|
this.log.trace('Destroying bbox module');
|
||||||
for (const unsubscribe of this.subscriptions) {
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
unsubscribe();
|
|
||||||
}
|
|
||||||
this.konva.group.destroy();
|
this.konva.group.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = (): SerializableObject => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||||
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 { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
import type { CanvasBrushLineState } from 'features/controlLayers/store/types';
|
import type { CanvasBrushLineState } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasBrushLineRenderer {
|
export class CanvasBrushLineRenderer extends CanvasModuleBase {
|
||||||
readonly type = 'brush_line_renderer';
|
readonly type = 'brush_line_renderer';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
@ -15,6 +15,7 @@ export class CanvasBrushLineRenderer {
|
|||||||
parent: CanvasObjectRenderer;
|
parent: CanvasObjectRenderer;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
state: CanvasBrushLineState;
|
state: CanvasBrushLineState;
|
||||||
konva: {
|
konva: {
|
||||||
@ -23,6 +24,7 @@ export class CanvasBrushLineRenderer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor(state: CanvasBrushLineState, parent: CanvasObjectRenderer) {
|
constructor(state: CanvasBrushLineState, parent: CanvasObjectRenderer) {
|
||||||
|
super();
|
||||||
const { id, clip } = state;
|
const { id, clip } = state;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
@ -30,7 +32,7 @@ export class CanvasBrushLineRenderer {
|
|||||||
this.path = this.parent.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
this.log.trace({ state }, 'Creating brush line');
|
this.log.debug({ state }, 'Creating brush line renderer module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
group: new Konva.Group({
|
group: new Konva.Group({
|
||||||
@ -69,26 +71,28 @@ export class CanvasBrushLineRenderer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy = () => {
|
||||||
this.log.trace('Destroying brush line');
|
this.log.debug('Destroying brush line renderer module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
this.konva.group.destroy();
|
this.konva.group.destroy();
|
||||||
}
|
};
|
||||||
|
|
||||||
setVisibility(isVisible: boolean): void {
|
setVisibility(isVisible: boolean): void {
|
||||||
this.log.trace({ isVisible }, 'Setting brush line visibility');
|
this.log.trace({ isVisible }, 'Setting brush line visibility');
|
||||||
this.konva.group.visible(isVisible);
|
this.konva.group.visible(isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
repr() {
|
repr = () => {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
parent: this.parent.id,
|
parent: this.parent.id,
|
||||||
state: deepClone(this.state),
|
state: deepClone(this.state),
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = () => {
|
||||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,31 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import type { GenerationMode } from 'features/controlLayers/store/types';
|
import type { GenerationMode } from 'features/controlLayers/store/types';
|
||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasCacheModule {
|
export class CanvasCacheModule extends CanvasModuleBase {
|
||||||
|
readonly type = 'cache';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
log: Logger;
|
log: Logger;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
imageNameCache = new LRUCache<string, string>({ max: 100 });
|
imageNameCache = new LRUCache<string, string>({ max: 100 });
|
||||||
canvasElementCache = new LRUCache<string, HTMLCanvasElement>({ max: 32 });
|
canvasElementCache = new LRUCache<string, HTMLCanvasElement>({ max: 32 });
|
||||||
generationModeCache = new LRUCache<string, GenerationMode>({ max: 100 });
|
generationModeCache = new LRUCache<string, GenerationMode>({ max: 100 });
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(manager: CanvasManager) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId('cache');
|
this.id = getPrefixedId('cache');
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.debug('Creating canvas cache');
|
|
||||||
|
this.log.debug('Creating cache module');
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAll = () => {
|
clearAll = () => {
|
||||||
@ -29,7 +34,21 @@ export class CanvasCacheModule {
|
|||||||
this.generationModeCache.clear();
|
this.generationModeCache.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
path: this.path,
|
||||||
|
type: this.type,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
this.log.debug('Destroying cache module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
this.clearAll();
|
||||||
|
};
|
||||||
|
|
||||||
|
getLoggingContext = () => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
import type { SerializableObject } from 'common/types';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import {
|
import {
|
||||||
canvasToBlob,
|
canvasToBlob,
|
||||||
canvasToImageData,
|
canvasToImageData,
|
||||||
@ -14,18 +15,22 @@ import type { ImageDTO } from 'services/api/types';
|
|||||||
import stableHash from 'stable-hash';
|
import stableHash from 'stable-hash';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
export class CanvasCompositorModule {
|
export class CanvasCompositorModule extends CanvasModuleBase {
|
||||||
|
readonly type = 'compositor';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
log: Logger;
|
log: Logger;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(manager: CanvasManager) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId('canvas_compositor');
|
this.id = getPrefixedId('canvas_compositor');
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.debug('Creating canvas compositor');
|
this.log.debug('Creating compositor module');
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompositeRasterLayerEntityIds = (): string[] => {
|
getCompositeRasterLayerEntityIds = (): string[] => {
|
||||||
@ -237,7 +242,20 @@ export class CanvasCompositorModule {
|
|||||||
return generationMode;
|
return generationMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
this.log.trace('Destroying compositor module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
};
|
||||||
|
|
||||||
|
getLoggingContext = () => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
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 { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
import type { CanvasEraserLineState } from 'features/controlLayers/store/types';
|
import type { CanvasEraserLineState } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasEraserLineRenderer {
|
export class CanvasEraserLineRenderer extends CanvasModuleBase {
|
||||||
readonly type = 'eraser_line_renderer';
|
readonly type = 'eraser_line_renderer';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
@ -14,6 +14,7 @@ export class CanvasEraserLineRenderer {
|
|||||||
parent: CanvasObjectRenderer;
|
parent: CanvasObjectRenderer;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
state: CanvasEraserLineState;
|
state: CanvasEraserLineState;
|
||||||
konva: {
|
konva: {
|
||||||
@ -22,6 +23,7 @@ export class CanvasEraserLineRenderer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor(state: CanvasEraserLineState, parent: CanvasObjectRenderer) {
|
constructor(state: CanvasEraserLineState, parent: CanvasObjectRenderer) {
|
||||||
|
super();
|
||||||
const { id, clip } = state;
|
const { id, clip } = state;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
@ -29,7 +31,7 @@ export class CanvasEraserLineRenderer {
|
|||||||
this.path = this.parent.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
this.log.trace({ state }, 'Creating eraser line');
|
this.log.debug({ state }, 'Creating eraser line renderer module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
group: new Konva.Group({
|
group: new Konva.Group({
|
||||||
@ -68,26 +70,28 @@ export class CanvasEraserLineRenderer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy = () => {
|
||||||
this.log.trace('Destroying eraser line');
|
this.log.debug('Destroying eraser line renderer module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
this.konva.group.destroy();
|
this.konva.group.destroy();
|
||||||
}
|
};
|
||||||
|
|
||||||
setVisibility(isVisible: boolean): void {
|
setVisibility(isVisible: boolean): void {
|
||||||
this.log.trace({ isVisible }, 'Setting brush line visibility');
|
this.log.trace({ isVisible }, 'Setting brush line visibility');
|
||||||
this.konva.group.visible(isVisible);
|
this.konva.group.visible(isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
repr() {
|
repr = () => {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
parent: this.parent.id,
|
parent: this.parent.id,
|
||||||
state: deepClone(this.state),
|
state: deepClone(this.state),
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = () => {
|
||||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
import type { SerializableObject } from 'common/types';
|
||||||
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import type { CanvasEntityIdentifier, CanvasImageState, FilterConfig } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier, CanvasImageState, FilterConfig } from 'features/controlLayers/store/types';
|
||||||
import { IMAGE_FILTERS, imageDTOToImageObject } from 'features/controlLayers/store/types';
|
import { IMAGE_FILTERS, imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||||
@ -10,15 +11,14 @@ import { getImageDTO } from 'services/api/endpoints/images';
|
|||||||
import type { BatchConfig, ImageDTO, S } from 'services/api/types';
|
import type { BatchConfig, ImageDTO, S } from 'services/api/types';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
const TYPE = 'entity_filter_preview';
|
export class CanvasFilterModule extends CanvasModuleBase {
|
||||||
|
readonly type = 'canvas_filter';
|
||||||
export class CanvasFilterModule {
|
|
||||||
readonly type = TYPE;
|
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
imageState: CanvasImageState | null = null;
|
imageState: CanvasImageState | null = null;
|
||||||
|
|
||||||
@ -28,11 +28,13 @@ export class CanvasFilterModule {
|
|||||||
$config = atom<FilterConfig>(IMAGE_FILTERS.canny_image_processor.buildDefaults());
|
$config = atom<FilterConfig>(IMAGE_FILTERS.canny_image_processor.buildDefaults());
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(manager: CanvasManager) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.trace('Creating filter');
|
|
||||||
|
this.log.debug('Creating filter module');
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize = (entityIdentifier: CanvasEntityIdentifier) => {
|
initialize = (entityIdentifier: CanvasEntityIdentifier) => {
|
||||||
@ -167,17 +169,19 @@ export class CanvasFilterModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
this.log.trace('Destroying filter');
|
this.log.trace('Destroying filter module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
};
|
};
|
||||||
|
|
||||||
repr = () => {
|
repr = () => {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = () => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
import type { CanvasFilterModule } from 'features/controlLayers/konva/CanvasFilterModule';
|
import type { CanvasFilterModule } from 'features/controlLayers/konva/CanvasFilterModule';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
import type { CanvasStagingAreaModule } from 'features/controlLayers/konva/CanvasStagingAreaModule';
|
import type { CanvasStagingAreaModule } from 'features/controlLayers/konva/CanvasStagingAreaModule';
|
||||||
import { loadImage } from 'features/controlLayers/konva/util';
|
import { loadImage } from 'features/controlLayers/konva/util';
|
||||||
@ -12,7 +12,7 @@ import Konva from 'konva';
|
|||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
import { getImageDTO } from 'services/api/endpoints/images';
|
import { getImageDTO } from 'services/api/endpoints/images';
|
||||||
|
|
||||||
export class CanvasImageRenderer {
|
export class CanvasImageRenderer extends CanvasModuleBase {
|
||||||
readonly type = 'image_renderer';
|
readonly type = 'image_renderer';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
@ -20,6 +20,7 @@ export class CanvasImageRenderer {
|
|||||||
parent: CanvasObjectRenderer | CanvasStagingAreaModule | CanvasFilterModule;
|
parent: CanvasObjectRenderer | CanvasStagingAreaModule | CanvasFilterModule;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
state: CanvasImageState;
|
state: CanvasImageState;
|
||||||
konva: {
|
konva: {
|
||||||
@ -33,6 +34,7 @@ export class CanvasImageRenderer {
|
|||||||
mutex = new Mutex();
|
mutex = new Mutex();
|
||||||
|
|
||||||
constructor(state: CanvasImageState, parent: CanvasObjectRenderer | CanvasStagingAreaModule | CanvasFilterModule) {
|
constructor(state: CanvasImageState, parent: CanvasObjectRenderer | CanvasStagingAreaModule | CanvasFilterModule) {
|
||||||
|
super();
|
||||||
const { id, image } = state;
|
const { id, image } = state;
|
||||||
const { width, height } = image;
|
const { width, height } = image;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -41,7 +43,7 @@ export class CanvasImageRenderer {
|
|||||||
this.path = this.parent.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
this.log.trace({ state }, 'Creating image');
|
this.log.debug({ state }, 'Creating image renderer module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
||||||
@ -166,7 +168,8 @@ export class CanvasImageRenderer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
this.log.trace('Destroying image');
|
this.log.debug('Destroying image renderer module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
this.konva.group.destroy();
|
this.konva.group.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,6 +182,7 @@ export class CanvasImageRenderer {
|
|||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
parent: this.parent.id,
|
parent: this.parent.id,
|
||||||
isLoading: this.isLoading,
|
isLoading: this.isLoading,
|
||||||
isError: this.isError,
|
isError: this.isError,
|
||||||
@ -186,7 +190,7 @@ export class CanvasImageRenderer {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = () => {
|
||||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
import type { SerializableObject } from 'common/types';
|
||||||
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 { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
import { CanvasTransformer } from 'features/controlLayers/konva/CanvasTransformer';
|
import { CanvasTransformer } from 'features/controlLayers/konva/CanvasTransformer';
|
||||||
import { getLastPointOfLine } from 'features/controlLayers/konva/util';
|
import { getLastPointOfLine } from 'features/controlLayers/konva/util';
|
||||||
@ -22,12 +23,13 @@ import type { Logger } from 'roarr';
|
|||||||
import stableHash from 'stable-hash';
|
import stableHash from 'stable-hash';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
export class CanvasLayerAdapter {
|
export class CanvasLayerAdapter extends CanvasModuleBase {
|
||||||
readonly type = 'layer_adapter';
|
readonly type = 'layer_adapter';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
|
||||||
state: CanvasRasterLayerState | CanvasControlLayerState;
|
state: CanvasRasterLayerState | CanvasControlLayerState;
|
||||||
@ -41,11 +43,14 @@ export class CanvasLayerAdapter {
|
|||||||
isFirstRender: boolean = true;
|
isFirstRender: boolean = true;
|
||||||
|
|
||||||
constructor(state: CanvasLayerAdapter['state'], manager: CanvasLayerAdapter['manager']) {
|
constructor(state: CanvasLayerAdapter['state'], manager: CanvasLayerAdapter['manager']) {
|
||||||
|
super();
|
||||||
this.id = state.id;
|
this.id = state.id;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.debug({ state }, 'Creating layer');
|
|
||||||
|
this.log.debug({ state }, 'Creating layer adapter module');
|
||||||
|
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
@ -71,10 +76,10 @@ export class CanvasLayerAdapter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
destroy = (): void => {
|
destroy = (): void => {
|
||||||
this.log.debug('Destroying layer');
|
this.log.debug('Destroying layer adapter module');
|
||||||
// We need to call the destroy method on all children so they can do their own cleanup.
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
this.transformer.destroy();
|
|
||||||
this.renderer.destroy();
|
this.renderer.destroy();
|
||||||
|
this.transformer.destroy();
|
||||||
this.konva.layer.destroy();
|
this.konva.layer.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -144,6 +149,7 @@ export class CanvasLayerAdapter {
|
|||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
state: deepClone(this.state),
|
state: deepClone(this.state),
|
||||||
transformer: this.transformer.repr(),
|
transformer: this.transformer.repr(),
|
||||||
renderer: this.renderer.repr(),
|
renderer: this.renderer.repr(),
|
||||||
|
@ -6,6 +6,7 @@ import { SyncableMap } from 'common/util/SyncableMap/SyncableMap';
|
|||||||
import { CanvasCacheModule } from 'features/controlLayers/konva/CanvasCacheModule';
|
import { CanvasCacheModule } from 'features/controlLayers/konva/CanvasCacheModule';
|
||||||
import { CanvasCompositorModule } from 'features/controlLayers/konva/CanvasCompositorModule';
|
import { CanvasCompositorModule } from 'features/controlLayers/konva/CanvasCompositorModule';
|
||||||
import { CanvasFilterModule } from 'features/controlLayers/konva/CanvasFilterModule';
|
import { CanvasFilterModule } from 'features/controlLayers/konva/CanvasFilterModule';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { CanvasRenderingModule } from 'features/controlLayers/konva/CanvasRenderingModule';
|
import { CanvasRenderingModule } from 'features/controlLayers/konva/CanvasRenderingModule';
|
||||||
import { CanvasStageModule } from 'features/controlLayers/konva/CanvasStageModule';
|
import { CanvasStageModule } from 'features/controlLayers/konva/CanvasStageModule';
|
||||||
import { CanvasWorkerModule } from 'features/controlLayers/konva/CanvasWorkerModule.js';
|
import { CanvasWorkerModule } from 'features/controlLayers/konva/CanvasWorkerModule.js';
|
||||||
@ -21,17 +22,20 @@ import { CanvasPreviewModule } from './CanvasPreviewModule';
|
|||||||
import { CanvasStateApiModule } from './CanvasStateApiModule';
|
import { CanvasStateApiModule } from './CanvasStateApiModule';
|
||||||
|
|
||||||
export const $canvasManager = atom<CanvasManager | null>(null);
|
export const $canvasManager = atom<CanvasManager | null>(null);
|
||||||
const TYPE = 'manager';
|
|
||||||
|
|
||||||
export class CanvasManager {
|
export class CanvasManager extends CanvasModuleBase {
|
||||||
readonly type = TYPE;
|
readonly type = 'manager';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
|
manager: CanvasManager;
|
||||||
|
log: Logger;
|
||||||
|
|
||||||
store: AppStore;
|
store: AppStore;
|
||||||
socket: AppSocket;
|
socket: AppSocket;
|
||||||
|
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
adapters = {
|
adapters = {
|
||||||
rasterLayers: new SyncableMap<string, CanvasLayerAdapter>(),
|
rasterLayers: new SyncableMap<string, CanvasLayerAdapter>(),
|
||||||
controlLayers: new SyncableMap<string, CanvasLayerAdapter>(),
|
controlLayers: new SyncableMap<string, CanvasLayerAdapter>(),
|
||||||
@ -60,8 +64,21 @@ export class CanvasManager {
|
|||||||
_isDebugging: boolean = false;
|
_isDebugging: boolean = false;
|
||||||
|
|
||||||
constructor(stage: Konva.Stage, container: HTMLDivElement, store: AppStore, socket: AppSocket) {
|
constructor(stage: Konva.Stage, container: HTMLDivElement, store: AppStore, socket: AppSocket) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.path = [this.id];
|
this.path = [this.id];
|
||||||
|
this.manager = this;
|
||||||
|
this.log = logger('canvas').child((message) => {
|
||||||
|
return {
|
||||||
|
...message,
|
||||||
|
context: {
|
||||||
|
...this.getLoggingContext(),
|
||||||
|
...message.context,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.log.debug('Creating canvas manager module');
|
||||||
|
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
|
|
||||||
@ -80,16 +97,6 @@ export class CanvasManager {
|
|||||||
this.stage.addLayer(this.background.konva.layer);
|
this.stage.addLayer(this.background.konva.layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
log = logger('canvas').child((message) => {
|
|
||||||
return {
|
|
||||||
...message,
|
|
||||||
context: {
|
|
||||||
...this.getLoggingContext(),
|
|
||||||
...message.context,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
enableDebugging() {
|
enableDebugging() {
|
||||||
this._isDebugging = true;
|
this._isDebugging = true;
|
||||||
this.logDebugInfo();
|
this.logDebugInfo();
|
||||||
@ -100,7 +107,7 @@ export class CanvasManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize = () => {
|
initialize = () => {
|
||||||
this.log.debug('Initializing canvas manager');
|
this.log.debug('Initializing canvas manager module');
|
||||||
|
|
||||||
// These atoms require the canvas manager to be set up before we can provide their initial values
|
// These atoms require the canvas manager to be set up before we can provide their initial values
|
||||||
this.stateApi.$transformingEntity.set(null);
|
this.stateApi.$transformingEntity.set(null);
|
||||||
@ -109,26 +116,52 @@ export class CanvasManager {
|
|||||||
this.stateApi.$currentFill.set(this.stateApi.getCurrentFill());
|
this.stateApi.$currentFill.set(this.stateApi.getCurrentFill());
|
||||||
this.stateApi.$selectedEntity.set(this.stateApi.getSelectedEntity());
|
this.stateApi.$selectedEntity.set(this.stateApi.getSelectedEntity());
|
||||||
|
|
||||||
const cleanupStage = this.stage.initialize();
|
this.subscriptions.add(this.store.subscribe(this.renderer.render));
|
||||||
const cleanupStore = this.store.subscribe(this.renderer.render);
|
this.stage.initialize();
|
||||||
|
};
|
||||||
|
|
||||||
return () => {
|
destroy = () => {
|
||||||
this.log.debug('Cleaning up canvas manager');
|
this.log.debug('Destroying canvas manager module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
for (const adapter of this.adapters.getAll()) {
|
for (const adapter of this.adapters.getAll()) {
|
||||||
adapter.destroy();
|
adapter.destroy();
|
||||||
}
|
}
|
||||||
this.background.destroy();
|
this.stateApi.destroy();
|
||||||
this.preview.destroy();
|
this.preview.destroy();
|
||||||
cleanupStore();
|
this.background.destroy();
|
||||||
cleanupStage();
|
this.filter.destroy();
|
||||||
};
|
this.worker.destroy();
|
||||||
|
this.renderer.destroy();
|
||||||
|
this.compositor.destroy();
|
||||||
|
this.stage.destroy();
|
||||||
|
$canvasManager.set(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
setCanvasManager = () => {
|
setCanvasManager = () => {
|
||||||
this.log.debug('Setting canvas manager');
|
this.log.debug('Setting canvas manager global');
|
||||||
$canvasManager.set(this);
|
$canvasManager.set(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
rasterLayers: Array.from(this.adapters.rasterLayers.values()).map((adapter) => adapter.repr()),
|
||||||
|
controlLayers: Array.from(this.adapters.controlLayers.values()).map((adapter) => adapter.repr()),
|
||||||
|
inpaintMasks: Array.from(this.adapters.inpaintMasks.values()).map((adapter) => adapter.repr()),
|
||||||
|
regionMasks: Array.from(this.adapters.regionMasks.values()).map((adapter) => adapter.repr()),
|
||||||
|
stateApi: this.stateApi.repr(),
|
||||||
|
preview: this.preview.repr(),
|
||||||
|
background: this.background.repr(),
|
||||||
|
filter: this.filter.repr(),
|
||||||
|
worker: this.worker.repr(),
|
||||||
|
renderer: this.renderer.repr(),
|
||||||
|
compositor: this.compositor.repr(),
|
||||||
|
stage: this.stage.repr(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = (): SerializableObject => {
|
||||||
return {
|
return {
|
||||||
path: this.path.join('.'),
|
path: this.path.join('.'),
|
||||||
@ -150,11 +183,6 @@ export class CanvasManager {
|
|||||||
logDebugInfo() {
|
logDebugInfo() {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('Canvas manager', this);
|
console.log('Canvas manager', this);
|
||||||
for (const adapter of this.adapters.getAll()) {
|
this.log.debug({ manager: this.repr() }, 'Canvas manager');
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(adapter.id, adapter);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
getPrefixedId = getPrefixedId;
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
import type { SerializableObject } from 'common/types';
|
||||||
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 { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
import { CanvasTransformer } from 'features/controlLayers/konva/CanvasTransformer';
|
import { CanvasTransformer } from 'features/controlLayers/konva/CanvasTransformer';
|
||||||
import { getLastPointOfLine } from 'features/controlLayers/konva/util';
|
import { getLastPointOfLine } from 'features/controlLayers/konva/util';
|
||||||
@ -21,13 +22,14 @@ import { get, omit } from 'lodash-es';
|
|||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
import stableHash from 'stable-hash';
|
import stableHash from 'stable-hash';
|
||||||
|
|
||||||
export class CanvasMaskAdapter {
|
export class CanvasMaskAdapter extends CanvasModuleBase {
|
||||||
readonly type = 'mask_adapter';
|
readonly type = 'mask_adapter';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
state: CanvasInpaintMaskState | CanvasRegionalGuidanceState;
|
state: CanvasInpaintMaskState | CanvasRegionalGuidanceState;
|
||||||
|
|
||||||
@ -41,11 +43,14 @@ export class CanvasMaskAdapter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor(state: CanvasMaskAdapter['state'], manager: CanvasMaskAdapter['manager']) {
|
constructor(state: CanvasMaskAdapter['state'], manager: CanvasMaskAdapter['manager']) {
|
||||||
|
super();
|
||||||
this.id = state.id;
|
this.id = state.id;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.debug({ state }, 'Creating mask');
|
|
||||||
|
this.log.debug({ state }, 'Creating mask adapter module');
|
||||||
|
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
@ -71,8 +76,8 @@ export class CanvasMaskAdapter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
destroy = (): void => {
|
destroy = (): void => {
|
||||||
this.log.debug('Destroying mask');
|
this.log.debug('Destroying mask adapter module');
|
||||||
// We need to call the destroy method on all children so they can do their own cleanup.
|
|
||||||
this.transformer.destroy();
|
this.transformer.destroy();
|
||||||
this.renderer.destroy();
|
this.renderer.destroy();
|
||||||
this.konva.layer.destroy();
|
this.konva.layer.destroy();
|
||||||
@ -157,6 +162,7 @@ export class CanvasMaskAdapter {
|
|||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
state: deepClone(this.state),
|
state: deepClone(this.state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -182,7 +188,8 @@ export class CanvasMaskAdapter {
|
|||||||
const canvas = this.renderer.getCanvas(rect, attrs);
|
const canvas = this.renderer.getCanvas(rect, attrs);
|
||||||
return canvas;
|
return canvas;
|
||||||
};
|
};
|
||||||
getLoggingContext = (): SerializableObject => {
|
|
||||||
|
getLoggingContext = () => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
import type { SerializableObject } from 'common/types';
|
||||||
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
|
export abstract class CanvasModuleBase {
|
||||||
|
abstract id: string;
|
||||||
|
abstract type: string;
|
||||||
|
abstract path: string[];
|
||||||
|
abstract manager: CanvasManager;
|
||||||
|
abstract log: Logger;
|
||||||
|
abstract subscriptions: Set<() => void>;
|
||||||
|
|
||||||
|
abstract getLoggingContext: () => SerializableObject;
|
||||||
|
abstract destroy: () => void;
|
||||||
|
abstract repr: () => SerializableObject & {
|
||||||
|
id: string;
|
||||||
|
path: string[];
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine';
|
import { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine';
|
||||||
import { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
import { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
||||||
@ -6,6 +5,7 @@ import { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
|
|||||||
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { CanvasRectRenderer } from 'features/controlLayers/konva/CanvasRect';
|
import { CanvasRectRenderer } from 'features/controlLayers/konva/CanvasRect';
|
||||||
import { LightnessToAlphaFilter } from 'features/controlLayers/konva/filters';
|
import { LightnessToAlphaFilter } from 'features/controlLayers/konva/filters';
|
||||||
import { getPatternSVG } from 'features/controlLayers/konva/patterns/getPatternSVG';
|
import { getPatternSVG } from 'features/controlLayers/konva/patterns/getPatternSVG';
|
||||||
@ -55,8 +55,8 @@ type AnyObjectState = CanvasBrushLineState | CanvasEraserLineState | CanvasImage
|
|||||||
/**
|
/**
|
||||||
* Handles rendering of objects for a canvas entity.
|
* Handles rendering of objects for a canvas entity.
|
||||||
*/
|
*/
|
||||||
export class CanvasObjectRenderer {
|
export class CanvasObjectRenderer extends CanvasModuleBase {
|
||||||
readonly type = 'object_renderer';
|
readonly type = 'entity_object_renderer';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
@ -123,12 +123,13 @@ export class CanvasObjectRenderer {
|
|||||||
$canvasCache = atom<{ canvas: HTMLCanvasElement; rect: Rect } | null>(null);
|
$canvasCache = atom<{ canvas: HTMLCanvasElement; rect: Rect } | null>(null);
|
||||||
|
|
||||||
constructor(parent: CanvasLayerAdapter | CanvasMaskAdapter) {
|
constructor(parent: CanvasLayerAdapter | CanvasMaskAdapter) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.path = this.parent.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.manager = parent.manager;
|
this.manager = parent.manager;
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.trace('Creating object renderer');
|
this.log.debug('Creating entity object renderer module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
objectGroup: new Konva.Group({ name: `${this.type}:object_group`, listening: false }),
|
objectGroup: new Konva.Group({ name: `${this.type}:object_group`, listening: false }),
|
||||||
@ -597,11 +598,8 @@ export class CanvasObjectRenderer {
|
|||||||
* Destroys this renderer and all of its object renderers.
|
* Destroys this renderer and all of its object renderers.
|
||||||
*/
|
*/
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
this.log.trace('Destroying object renderer');
|
this.log.debug('Destroying entity object renderer module');
|
||||||
for (const cleanup of this.subscriptions) {
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
this.log.trace('Cleaning up listener');
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
for (const renderer of this.renderers.values()) {
|
for (const renderer of this.renderers.values()) {
|
||||||
renderer.destroy();
|
renderer.destroy();
|
||||||
}
|
}
|
||||||
@ -616,13 +614,14 @@ export class CanvasObjectRenderer {
|
|||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
parent: this.parent.id,
|
parent: this.parent.id,
|
||||||
renderers: Array.from(this.renderers.values()).map((renderer) => renderer.repr()),
|
renderers: Array.from(this.renderers.values()).map((renderer) => renderer.repr()),
|
||||||
buffer: this.bufferRenderer?.repr(),
|
buffer: this.bufferRenderer?.repr(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = () => {
|
||||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { CanvasProgressImageModule } from 'features/controlLayers/konva/CanvasProgressImageModule';
|
import { CanvasProgressImageModule } from 'features/controlLayers/konva/CanvasProgressImageModule';
|
||||||
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
import { CanvasBboxModule } from './CanvasBboxModule';
|
import { CanvasBboxModule } from './CanvasBboxModule';
|
||||||
import { CanvasStagingAreaModule } from './CanvasStagingAreaModule';
|
import { CanvasStagingAreaModule } from './CanvasStagingAreaModule';
|
||||||
import { CanvasToolModule } from './CanvasToolModule';
|
import { CanvasToolModule } from './CanvasToolModule';
|
||||||
|
|
||||||
export class CanvasPreviewModule {
|
export class CanvasPreviewModule extends CanvasModuleBase {
|
||||||
|
readonly type = 'preview';
|
||||||
|
|
||||||
|
id: string;
|
||||||
|
path: string[];
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
log: Logger;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
konva: {
|
konva: {
|
||||||
layer: Konva.Layer;
|
layer: Konva.Layer;
|
||||||
@ -19,7 +28,14 @@ export class CanvasPreviewModule {
|
|||||||
progressImage: CanvasProgressImageModule;
|
progressImage: CanvasProgressImageModule;
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(manager: CanvasManager) {
|
||||||
|
super();
|
||||||
|
this.id = getPrefixedId(this.type);
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.path = this.manager.path.concat(this.id);
|
||||||
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
|
this.log.debug('Creating preview module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
layer: new Konva.Layer({ listening: false, imageSmoothingEnabled: false }),
|
layer: new Konva.Layer({ listening: false, imageSmoothingEnabled: false }),
|
||||||
};
|
};
|
||||||
@ -41,11 +57,25 @@ export class CanvasPreviewModule {
|
|||||||
return this.konva.layer;
|
return this.konva.layer;
|
||||||
};
|
};
|
||||||
|
|
||||||
destroy() {
|
repr = () => {
|
||||||
// this.stagingArea.destroy(); // TODO(psyche): implement destroy
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
this.log.debug('Destroying preview module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
this.stagingArea.destroy();
|
||||||
this.progressImage.destroy();
|
this.progressImage.destroy();
|
||||||
// this.bbox.destroy(); // TODO(psyche): implement destroy
|
this.bbox.destroy();
|
||||||
this.tool.destroy();
|
this.tool.destroy();
|
||||||
this.konva.layer.destroy();
|
this.konva.layer.destroy();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
getLoggingContext = () => {
|
||||||
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
||||||
import { getPrefixedId, loadImage } from 'features/controlLayers/konva/util';
|
import { getPrefixedId, loadImage } from 'features/controlLayers/konva/util';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
import type { S } from 'services/api/types';
|
import type { S } from 'services/api/types';
|
||||||
|
|
||||||
export class CanvasProgressImageModule {
|
export class CanvasProgressImageModule extends CanvasModuleBase {
|
||||||
readonly type = 'progress_image';
|
readonly type = 'progress_image';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
@ -35,13 +35,14 @@ export class CanvasProgressImageModule {
|
|||||||
mutex: Mutex = new Mutex();
|
mutex: Mutex = new Mutex();
|
||||||
|
|
||||||
constructor(parent: CanvasPreviewModule) {
|
constructor(parent: CanvasPreviewModule) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = parent.manager;
|
this.manager = parent.manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
this.log.trace('Creating progress image');
|
this.log.debug('Creating progress image module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
||||||
@ -105,15 +106,21 @@ export class CanvasProgressImageModule {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
this.log.trace('Destroying progress image');
|
this.log.debug('Destroying progress image module');
|
||||||
for (const unsubscribe of this.subscriptions) {
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
unsubscribe();
|
|
||||||
}
|
|
||||||
this.konva.group.destroy();
|
this.konva.group.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = () => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||||
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 { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
import type { CanvasRectState } from 'features/controlLayers/store/types';
|
import type { CanvasRectState } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasRectRenderer {
|
export class CanvasRectRenderer extends CanvasModuleBase {
|
||||||
readonly type = 'rect_renderer';
|
readonly type = 'rect_renderer';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
@ -15,6 +15,7 @@ export class CanvasRectRenderer {
|
|||||||
parent: CanvasObjectRenderer;
|
parent: CanvasObjectRenderer;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
state: CanvasRectState;
|
state: CanvasRectState;
|
||||||
konva: {
|
konva: {
|
||||||
@ -24,13 +25,15 @@ export class CanvasRectRenderer {
|
|||||||
isFirstRender: boolean = false;
|
isFirstRender: boolean = false;
|
||||||
|
|
||||||
constructor(state: CanvasRectState, parent: CanvasObjectRenderer) {
|
constructor(state: CanvasRectState, parent: CanvasObjectRenderer) {
|
||||||
|
super();
|
||||||
const { id } = state;
|
const { id } = state;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = parent.manager;
|
this.manager = parent.manager;
|
||||||
this.path = this.parent.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.trace({ state }, 'Creating rect');
|
|
||||||
|
this.log.debug({ state }, 'Creating rect renderer module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
||||||
@ -60,27 +63,29 @@ export class CanvasRectRenderer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.log.trace('Destroying rect');
|
|
||||||
this.konva.group.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
setVisibility(isVisible: boolean): void {
|
setVisibility(isVisible: boolean): void {
|
||||||
this.log.trace({ isVisible }, 'Setting rect visibility');
|
this.log.trace({ isVisible }, 'Setting rect visibility');
|
||||||
this.konva.group.visible(isVisible);
|
this.konva.group.visible(isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
repr() {
|
destroy = () => {
|
||||||
|
this.log.debug('Destroying rect renderer module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
this.konva.group.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
repr = () => {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
parent: this.parent.id,
|
parent: this.parent.id,
|
||||||
isFirstRender: this.isFirstRender,
|
isFirstRender: this.isFirstRender,
|
||||||
state: deepClone(this.state),
|
state: deepClone(this.state),
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = () => {
|
||||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,29 @@ import type { SerializableObject } from 'common/types';
|
|||||||
import { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
import { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
import { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasRenderingModule {
|
export class CanvasRenderingModule extends CanvasModuleBase {
|
||||||
|
readonly type = 'canvas_renderer';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
log: Logger;
|
log: Logger;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
state: CanvasV2State | null = null;
|
state: CanvasV2State | null = null;
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(manager: CanvasManager) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId('canvas_renderer');
|
this.id = getPrefixedId('canvas_renderer');
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.debug('Creating canvas renderer');
|
this.log.debug('Creating canvas renderer module');
|
||||||
}
|
}
|
||||||
|
|
||||||
render = async () => {
|
render = async () => {
|
||||||
@ -263,4 +268,17 @@ export class CanvasRenderingModule {
|
|||||||
this.manager.preview.getLayer().zIndex(++zIndex);
|
this.manager.preview.getLayer().zIndex(++zIndex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
path: this.path,
|
||||||
|
type: this.type,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
this.log.debug('Destroying canvas renderer module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { CANVAS_SCALE_BY } from 'features/controlLayers/konva/constants';
|
import { CANVAS_SCALE_BY } from 'features/controlLayers/konva/constants';
|
||||||
import { getPrefixedId, getRectUnion } from 'features/controlLayers/konva/util';
|
import { getPrefixedId, getRectUnion } from 'features/controlLayers/konva/util';
|
||||||
import type { Coordinate, Dimensions, Rect } from 'features/controlLayers/store/types';
|
import type { Coordinate, Dimensions, Rect } from 'features/controlLayers/store/types';
|
||||||
@ -8,7 +8,9 @@ import type { KonvaEventObject } from 'konva/lib/Node';
|
|||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasStageModule {
|
export class CanvasStageModule extends CanvasModuleBase {
|
||||||
|
readonly type = 'stage';
|
||||||
|
|
||||||
static MIN_CANVAS_SCALE = 0.1;
|
static MIN_CANVAS_SCALE = 0.1;
|
||||||
static MAX_CANVAS_SCALE = 20;
|
static MAX_CANVAS_SCALE = 20;
|
||||||
|
|
||||||
@ -19,12 +21,17 @@ export class CanvasStageModule {
|
|||||||
container: HTMLDivElement;
|
container: HTMLDivElement;
|
||||||
log: Logger;
|
log: Logger;
|
||||||
|
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
constructor(stage: Konva.Stage, container: HTMLDivElement, manager: CanvasManager) {
|
constructor(stage: Konva.Stage, container: HTMLDivElement, manager: CanvasManager) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId('stage');
|
this.id = getPrefixedId('stage');
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
this.log.debug('Creating stage module');
|
this.log.debug('Creating stage module');
|
||||||
|
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.konva = { stage };
|
this.konva = { stage };
|
||||||
}
|
}
|
||||||
@ -50,12 +57,10 @@ export class CanvasStageModule {
|
|||||||
this.fitLayersToStage();
|
this.fitLayersToStage();
|
||||||
const cleanupListeners = this.setEventListeners();
|
const cleanupListeners = this.setEventListeners();
|
||||||
|
|
||||||
return () => {
|
this.subscriptions.add(cleanupListeners);
|
||||||
this.log.debug('Destroying stage');
|
this.subscriptions.add(() => {
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
this.konva.stage.destroy();
|
});
|
||||||
cleanupListeners();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fitStageToContainer = () => {
|
fitStageToContainer = () => {
|
||||||
@ -276,7 +281,21 @@ export class CanvasStageModule {
|
|||||||
this.konva.stage.add(layer);
|
this.konva.stage.add(layer);
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
this.log.debug('Destroying stage module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
this.konva.stage.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
getLoggingContext = () => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
import type { SerializableObject } from 'common/types';
|
||||||
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 { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import { imageDTOToImageWithDims, type StagingAreaImage } from 'features/controlLayers/store/types';
|
import { imageDTOToImageWithDims, type StagingAreaImage } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasStagingAreaModule {
|
export class CanvasStagingAreaModule extends CanvasModuleBase {
|
||||||
readonly type = 'staging_area';
|
readonly type = 'staging_area';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
@ -27,12 +28,14 @@ export class CanvasStagingAreaModule {
|
|||||||
subscriptions: Set<() => void> = new Set();
|
subscriptions: Set<() => void> = new Set();
|
||||||
|
|
||||||
constructor(parent: CanvasPreviewModule) {
|
constructor(parent: CanvasPreviewModule) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = this.parent.manager;
|
this.manager = this.parent.manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
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 module');
|
||||||
|
|
||||||
this.konva = { group: new Konva.Group({ name: `${this.type}:group`, listening: false }) };
|
this.konva = { group: new Konva.Group({ name: `${this.type}:group`, listening: false }) };
|
||||||
this.image = null;
|
this.image = null;
|
||||||
@ -85,12 +88,11 @@ export class CanvasStagingAreaModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
|
this.log.debug('Destroying staging area module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
if (this.image) {
|
if (this.image) {
|
||||||
this.image.destroy();
|
this.image.destroy();
|
||||||
}
|
}
|
||||||
for (const unsubscribe of this.subscriptions) {
|
|
||||||
unsubscribe();
|
|
||||||
}
|
|
||||||
for (const node of this.getNodes()) {
|
for (const node of this.getNodes()) {
|
||||||
node.destroy();
|
node.destroy();
|
||||||
}
|
}
|
||||||
@ -100,6 +102,7 @@ export class CanvasStagingAreaModule {
|
|||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
selectedImage: this.selectedImage,
|
selectedImage: this.selectedImage,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,8 @@ import type { AppStore } from 'app/store/store';
|
|||||||
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import {
|
import {
|
||||||
bboxChanged,
|
bboxChanged,
|
||||||
brushWidthChanged,
|
brushWidthChanged,
|
||||||
@ -40,6 +42,7 @@ import type {
|
|||||||
import { RGBA_BLACK } from 'features/controlLayers/store/types';
|
import { RGBA_BLACK } from 'features/controlLayers/store/types';
|
||||||
import type { WritableAtom } from 'nanostores';
|
import type { WritableAtom } from 'nanostores';
|
||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
|
import type { Logger } from 'roarr';
|
||||||
import { queueApi } from 'services/api/endpoints/queue';
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
import type { BatchConfig } from 'services/api/types';
|
import type { BatchConfig } from 'services/api/types';
|
||||||
import { $lastCanvasProgressEvent } from 'services/events/setEventListeners';
|
import { $lastCanvasProgressEvent } from 'services/events/setEventListeners';
|
||||||
@ -70,13 +73,27 @@ type EntityStateAndAdapter =
|
|||||||
adapter: CanvasMaskAdapter;
|
adapter: CanvasMaskAdapter;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CanvasStateApiModule {
|
export class CanvasStateApiModule extends CanvasModuleBase {
|
||||||
store: AppStore;
|
readonly type = 'state_api';
|
||||||
|
|
||||||
|
id: string;
|
||||||
|
path: string[];
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
log: Logger;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
|
store: AppStore;
|
||||||
|
|
||||||
constructor(store: AppStore, manager: CanvasManager) {
|
constructor(store: AppStore, manager: CanvasManager) {
|
||||||
this.store = store;
|
super();
|
||||||
|
this.id = getPrefixedId(this.type);
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.path = this.manager.path.concat(this.id);
|
||||||
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
|
this.log.debug('Creating state api module');
|
||||||
|
|
||||||
|
this.store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reminder - use arrow functions to avoid binding issues
|
// Reminder - use arrow functions to avoid binding issues
|
||||||
@ -258,4 +275,21 @@ export class CanvasStateApiModule {
|
|||||||
height: 0,
|
height: 0,
|
||||||
scale: 0,
|
scale: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
this.log.debug('Destroying state api module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
};
|
||||||
|
|
||||||
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
getLoggingContext = () => {
|
||||||
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import { rgbaColorToString, rgbColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbaColorToString, rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
|
||||||
import {
|
import {
|
||||||
BRUSH_BORDER_INNER_COLOR,
|
BRUSH_BORDER_INNER_COLOR,
|
||||||
@ -31,8 +31,8 @@ import Konva from 'konva';
|
|||||||
import type { KonvaEventObject } from 'konva/lib/Node';
|
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasToolModule {
|
export class CanvasToolModule extends CanvasModuleBase {
|
||||||
readonly type = 'tool_preview';
|
readonly type = 'tool';
|
||||||
static readonly COLOR_PICKER_RADIUS = 25;
|
static readonly COLOR_PICKER_RADIUS = 25;
|
||||||
static readonly COLOR_PICKER_THICKNESS = 15;
|
static readonly COLOR_PICKER_THICKNESS = 15;
|
||||||
static readonly COLOR_PICKER_CROSSHAIR_SPACE = 5;
|
static readonly COLOR_PICKER_CROSSHAIR_SPACE = 5;
|
||||||
@ -84,11 +84,15 @@ export class CanvasToolModule {
|
|||||||
subscriptions: Set<() => void> = new Set();
|
subscriptions: Set<() => void> = new Set();
|
||||||
|
|
||||||
constructor(parent: CanvasPreviewModule) {
|
constructor(parent: CanvasPreviewModule) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = this.parent.manager;
|
this.manager = this.parent.manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
|
||||||
|
this.log.debug('Creating tool module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
stage: this.manager.stage.konva.stage,
|
stage: this.manager.stage.konva.stage,
|
||||||
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
||||||
@ -240,13 +244,6 @@ export class CanvasToolModule {
|
|||||||
this.subscriptions.add(cleanupListeners);
|
this.subscriptions.add(cleanupListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy = () => {
|
|
||||||
for (const cleanup of this.subscriptions) {
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
this.konva.group.destroy();
|
|
||||||
};
|
|
||||||
|
|
||||||
setToolVisibility = (tool: Tool) => {
|
setToolVisibility = (tool: Tool) => {
|
||||||
this.konva.brush.group.visible(tool === 'brush');
|
this.konva.brush.group.visible(tool === 'brush');
|
||||||
this.konva.eraser.group.visible(tool === 'eraser');
|
this.konva.eraser.group.visible(tool === 'eraser');
|
||||||
@ -882,7 +879,21 @@ export class CanvasToolModule {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
repr = () => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
this.log.debug('Destroying tool module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
this.konva.group.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
getLoggingContext = () => {
|
||||||
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { canvasToImageData, getEmptyRect, getPrefixedId } from 'features/controlLayers/konva/util';
|
import { canvasToImageData, getEmptyRect, getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import type { Coordinate, Rect } from 'features/controlLayers/store/types';
|
import type { Coordinate, Rect } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
@ -18,7 +18,7 @@ import type { Logger } from 'roarr';
|
|||||||
*
|
*
|
||||||
* It renders an outline when dragging and resizing the entity, with transform anchors for resizing and rotation.
|
* It renders an outline when dragging and resizing the entity, with transform anchors for resizing and rotation.
|
||||||
*/
|
*/
|
||||||
export class CanvasTransformer {
|
export class CanvasTransformer extends CanvasModuleBase {
|
||||||
readonly type = 'entity_transformer';
|
readonly type = 'entity_transformer';
|
||||||
|
|
||||||
static RECT_CALC_DEBOUNCE_MS = 300;
|
static RECT_CALC_DEBOUNCE_MS = 300;
|
||||||
@ -99,11 +99,13 @@ export class CanvasTransformer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor(parent: CanvasLayerAdapter | CanvasMaskAdapter) {
|
constructor(parent: CanvasLayerAdapter | CanvasMaskAdapter) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = parent.manager;
|
this.manager = parent.manager;
|
||||||
this.path = this.parent.path.concat(this.id);
|
this.path = this.parent.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
|
this.log.debug('Creating entity transformer module');
|
||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
outlineRect: new Konva.Rect({
|
outlineRect: new Konva.Rect({
|
||||||
@ -721,6 +723,7 @@ export class CanvasTransformer {
|
|||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
mode: this.interactionMode,
|
mode: this.interactionMode,
|
||||||
isTransformEnabled: this.isTransformEnabled,
|
isTransformEnabled: this.isTransformEnabled,
|
||||||
isDragEnabled: this.isDragEnabled,
|
isDragEnabled: this.isDragEnabled,
|
||||||
@ -731,17 +734,14 @@ export class CanvasTransformer {
|
|||||||
* Destroys the transformer, cleaning up any subscriptions.
|
* Destroys the transformer, cleaning up any subscriptions.
|
||||||
*/
|
*/
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
this.log.trace('Destroying transformer');
|
this.log.debug('Destroying entity transformer module');
|
||||||
for (const cleanup of this.subscriptions) {
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
this.log.trace('Cleaning up listener');
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
this.konva.outlineRect.destroy();
|
this.konva.outlineRect.destroy();
|
||||||
this.konva.transformer.destroy();
|
this.konva.transformer.destroy();
|
||||||
this.konva.proxyRect.destroy();
|
this.konva.proxyRect.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
getLoggingContext = () => {
|
||||||
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,29 @@
|
|||||||
import type { SerializableObject } from 'common/types';
|
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
|
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } 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 type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
export class CanvasWorkerModule {
|
export class CanvasWorkerModule extends CanvasModuleBase {
|
||||||
|
readonly type = 'worker';
|
||||||
|
|
||||||
id: string;
|
id: string;
|
||||||
path: string[];
|
path: string[];
|
||||||
log: Logger;
|
log: Logger;
|
||||||
manager: CanvasManager;
|
manager: CanvasManager;
|
||||||
|
subscriptions = new Set<() => void>();
|
||||||
|
|
||||||
worker: Worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module', name: 'worker' });
|
worker: Worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module', name: 'worker' });
|
||||||
tasks: Map<string, { task: GetBboxTask; onComplete: (extents: Extents | null) => void }> = new Map();
|
tasks: Map<string, { task: GetBboxTask; onComplete: (extents: Extents | null) => void }> = new Map();
|
||||||
|
|
||||||
constructor(manager: CanvasManager) {
|
constructor(manager: CanvasManager) {
|
||||||
|
super();
|
||||||
this.id = getPrefixedId('worker');
|
this.id = getPrefixedId('worker');
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.path = this.manager.path.concat(this.id);
|
this.path = this.manager.path.concat(this.id);
|
||||||
this.log = this.manager.buildLogger(this.getLoggingContext);
|
this.log = this.manager.buildLogger(this.getLoggingContext);
|
||||||
this.log.debug('Creating canvas worker');
|
|
||||||
|
this.log.debug('Creating worker module');
|
||||||
|
|
||||||
this.worker.onmessage = (event: MessageEvent<ExtentsResult | WorkerLogMessage>) => {
|
this.worker.onmessage = (event: MessageEvent<ExtentsResult | WorkerLogMessage>) => {
|
||||||
const { type, data } = event.data;
|
const { type, data } = event.data;
|
||||||
@ -55,7 +60,23 @@ export class CanvasWorkerModule {
|
|||||||
this.worker.postMessage(task, [data.buffer]);
|
this.worker.postMessage(task, [data.buffer]);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLoggingContext = (): SerializableObject => {
|
repr = () => {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
type: this.type,
|
||||||
|
path: this.path,
|
||||||
|
tasks: Array.from(this.tasks.keys()),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
destroy = () => {
|
||||||
|
this.log.trace('Destroying worker module');
|
||||||
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||||
|
this.worker.terminate();
|
||||||
|
this.tasks.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
getLoggingContext = () => {
|
||||||
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
return { ...this.manager.getLoggingContext(), path: this.path.join('.') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user