From 4e5f4dadf254d718df6518374a6776e3bae1d47d Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Wed, 4 Sep 2024 19:06:49 +1000
Subject: [PATCH] feat(ui): abstract out CanvasEntityAdapterBase
Things were getting to complex to reason about & classes a bit complicated. Trying to simplify...
---
...tityListActionBarSelectedEntityOpacity.tsx | 4 +-
.../components/Transform/Transform.tsx | 137 +++++------
.../konva/CanvasControlLayerAdapter.ts | 94 +-------
.../konva/CanvasEntityAdapterBase.ts | 105 ++++++++
.../konva/CanvasEntityLayerAdapter.ts | 228 ------------------
.../konva/CanvasEntityMaskAdapter.ts | 189 ---------------
.../konva/CanvasEntityRenderer.ts | 19 +-
.../konva/CanvasEntityTransformer.ts | 11 +-
.../konva/CanvasInpaintMaskAdapter.ts | 83 +------
.../konva/CanvasRasterLayerAdapter.ts | 91 +------
.../konva/CanvasRegionalGuidanceAdapter.ts | 90 +------
.../konva/CanvasStateApiModule.ts | 9 +-
.../controlLayers/konva/CanvasToolModule.ts | 4 +-
.../controlLayers/store/canvasSlice.ts | 14 +-
.../src/features/controlLayers/store/types.ts | 14 +-
15 files changed, 214 insertions(+), 878 deletions(-)
create mode 100644 invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityAdapterBase.ts
delete mode 100644 invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityLayerAdapter.ts
delete mode 100644 invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityMaskAdapter.ts
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListActionBarSelectedEntityOpacity.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListActionBarSelectedEntityOpacity.tsx
index fed4fcf816..c514e4ee8b 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListActionBarSelectedEntityOpacity.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListActionBarSelectedEntityOpacity.tsx
@@ -22,7 +22,7 @@ import {
selectEntity,
selectSelectedEntityIdentifier,
} from 'features/controlLayers/store/selectors';
-import { isDrawableEntity } from 'features/controlLayers/store/types';
+import { isRenderableEntity } from 'features/controlLayers/store/types';
import { clamp, round } from 'lodash-es';
import type { KeyboardEvent } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';
@@ -70,7 +70,7 @@ const selectOpacity = createSelector(selectCanvasSlice, (canvas) => {
if (!selectedEntity) {
return 1; // fallback to 100% opacity
}
- if (!isDrawableEntity(selectedEntity)) {
+ if (!isRenderableEntity(selectedEntity)) {
return 1; // fallback to 100% opacity
}
// Opacity is a float from 0-1, but we want to display it as a percentage
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx
index bfb9e8a7d5..d9608788cf 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Transform/Transform.tsx
@@ -1,86 +1,73 @@
import { Button, ButtonGroup, Flex, Heading, Spacer } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
-import type { CanvasControlLayerAdapter } from 'features/controlLayers/konva/CanvasControlLayerAdapter';
-import type { CanvasInpaintMaskAdapter } from 'features/controlLayers/konva/CanvasInpaintMaskAdapter';
-import type { CanvasRasterLayerAdapter } from 'features/controlLayers/konva/CanvasRasterLayerAdapter';
-import type { CanvasRegionalGuidanceAdapter } from 'features/controlLayers/konva/CanvasRegionalGuidanceAdapter';
+import type { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapterBase';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowsCounterClockwiseBold, PiArrowsOutBold, PiCheckBold, PiXBold } from 'react-icons/pi';
-const TransformBox = memo(
- ({
- adapter,
- }: {
- adapter:
- | CanvasRasterLayerAdapter
- | CanvasControlLayerAdapter
- | CanvasInpaintMaskAdapter
- | CanvasRegionalGuidanceAdapter;
- }) => {
- const { t } = useTranslation();
- const isProcessing = useStore(adapter.transformer.$isProcessing);
+const TransformBox = memo(({ adapter }: { adapter: CanvasEntityAdapterBase }) => {
+ const { t } = useTranslation();
+ const isProcessing = useStore(adapter.transformer.$isProcessing);
- return (
-
-
- {t('controlLayers.transform.transform')}
-
-
- }
- onClick={adapter.transformer.fitProxyRectToBbox}
- isLoading={isProcessing}
- loadingText={t('controlLayers.transform.reset')}
- variant="ghost"
- >
- {t('controlLayers.transform.fitToBbox')}
-
-
- }
- onClick={adapter.transformer.resetTransform}
- isLoading={isProcessing}
- loadingText={t('controlLayers.reset')}
- variant="ghost"
- >
- {t('controlLayers.transform.reset')}
-
- }
- onClick={adapter.transformer.applyTransform}
- isLoading={isProcessing}
- loadingText={t('common.apply')}
- variant="ghost"
- >
- {t('controlLayers.transform.apply')}
-
- }
- onClick={adapter.transformer.stopTransform}
- isLoading={isProcessing}
- loadingText={t('common.cancel')}
- variant="ghost"
- >
- {t('controlLayers.transform.cancel')}
-
-
-
- );
- }
-);
+ return (
+
+
+ {t('controlLayers.transform.transform')}
+
+
+ }
+ onClick={adapter.transformer.fitProxyRectToBbox}
+ isLoading={isProcessing}
+ loadingText={t('controlLayers.transform.reset')}
+ variant="ghost"
+ >
+ {t('controlLayers.transform.fitToBbox')}
+
+
+ }
+ onClick={adapter.transformer.resetTransform}
+ isLoading={isProcessing}
+ loadingText={t('controlLayers.reset')}
+ variant="ghost"
+ >
+ {t('controlLayers.transform.reset')}
+
+ }
+ onClick={adapter.transformer.applyTransform}
+ isLoading={isProcessing}
+ loadingText={t('common.apply')}
+ variant="ghost"
+ >
+ {t('controlLayers.transform.apply')}
+
+ }
+ onClick={adapter.transformer.stopTransform}
+ isLoading={isProcessing}
+ loadingText={t('common.cancel')}
+ variant="ghost"
+ >
+ {t('controlLayers.transform.cancel')}
+
+
+
+ );
+});
TransformBox.displayName = 'Transform';
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasControlLayerAdapter.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasControlLayerAdapter.ts
index 576b592854..d9e200b3bf 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasControlLayerAdapter.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasControlLayerAdapter.ts
@@ -1,73 +1,17 @@
import type { SerializableObject } from 'common/types';
-import { deepClone } from 'common/util/deepClone';
-import { CanvasEntityRenderer } from 'features/controlLayers/konva/CanvasEntityRenderer';
-import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
+import { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapterBase';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
-import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import type { CanvasControlLayerState, CanvasEntityIdentifier, Rect } from 'features/controlLayers/store/types';
-import Konva from 'konva';
import type { GroupConfig } from 'konva/lib/Group';
import { omit } from 'lodash-es';
-import type { Logger } from 'roarr';
-import stableHash from 'stable-hash';
import { assert } from 'tsafe';
-export class CanvasControlLayerAdapter extends CanvasModuleBase {
- readonly type = 'control_layer_adapter';
- readonly id: string;
- readonly path: string[];
- readonly manager: CanvasManager;
- readonly parent: CanvasManager;
- readonly log: Logger;
-
- entityIdentifier: CanvasEntityIdentifier<'control_layer'>;
-
- /**
- * The last known state of the entity.
- */
+export class CanvasControlLayerAdapter extends CanvasEntityAdapterBase {
+ static TYPE = 'control_layer_adapter';
private _state: CanvasControlLayerState | null = null;
- /**
- * The Konva nodes that make up the entity layer:
- * - A layer to hold the everything
- *
- * Note that the transformer and object renderer have their own Konva nodes, but they are not stored here.
- */
- konva: {
- layer: Konva.Layer;
- };
-
- /**
- * The transformer for this entity layer.
- */
- transformer: CanvasEntityTransformer;
-
- /**
- * The renderer for this entity layer.
- */
- renderer: CanvasEntityRenderer;
-
constructor(entityIdentifier: CanvasEntityIdentifier<'control_layer'>, manager: CanvasManager) {
- super();
- this.id = entityIdentifier.id;
- this.entityIdentifier = entityIdentifier;
- this.manager = manager;
- this.parent = manager;
- this.path = this.manager.buildPath(this);
- this.log = this.manager.buildLogger(this);
-
- this.log.debug('Creating module');
-
- this.konva = {
- layer: new Konva.Layer({
- name: `${this.type}:layer`,
- listening: false,
- imageSmoothingEnabled: false,
- }),
- };
-
- this.renderer = new CanvasEntityRenderer(this);
- this.transformer = new CanvasEntityTransformer(this);
+ super(entityIdentifier, manager, CanvasControlLayerAdapter.TYPE);
}
get state(): CanvasControlLayerState {
@@ -134,34 +78,4 @@ export class CanvasControlLayerAdapter extends CanvasModuleBase {
const keysToOmit: (keyof CanvasControlLayerState)[] = ['name', 'controlAdapter', 'withTransparencyEffect'];
return omit(this.state, keysToOmit);
};
-
- isInteractable = (): boolean => {
- return this.state.isEnabled && !this.state.isLocked;
- };
-
- hash = (extra?: SerializableObject): string => {
- const arg = {
- state: this.getHashableState(),
- extra,
- };
- return stableHash(arg);
- };
-
- destroy = (): void => {
- this.log.debug('Destroying module');
- this.renderer.destroy();
- this.transformer.destroy();
- this.konva.layer.destroy();
- };
-
- repr = () => {
- return {
- id: this.id,
- type: this.type,
- path: this.path,
- state: deepClone(this.state),
- transformer: this.transformer.repr(),
- renderer: this.renderer.repr(),
- };
- };
}
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityAdapterBase.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityAdapterBase.ts
new file mode 100644
index 0000000000..7a155b7773
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityAdapterBase.ts
@@ -0,0 +1,105 @@
+import type { SerializableObject } from 'common/types';
+import { deepClone } from 'common/util/deepClone';
+import { CanvasEntityRenderer } from 'features/controlLayers/konva/CanvasEntityRenderer';
+import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
+import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
+import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
+import type { CanvasEntityIdentifier, CanvasRenderableEntityState, Rect } from 'features/controlLayers/store/types';
+import Konva from 'konva';
+import type { Logger } from 'roarr';
+import stableHash from 'stable-hash';
+
+export abstract class CanvasEntityAdapterBase<
+ T extends CanvasRenderableEntityState = CanvasRenderableEntityState,
+> extends CanvasModuleBase {
+ readonly type: string;
+ readonly id: string;
+ readonly path: string[];
+ readonly manager: CanvasManager;
+ readonly parent: CanvasManager;
+ readonly log: Logger;
+
+ readonly entityIdentifier: CanvasEntityIdentifier;
+
+ /**
+ * The Konva nodes that make up the entity adapter:
+ * - A Konva.Layer to hold the everything
+ *
+ * Note that the transformer and object renderer have their own Konva nodes, but they are not stored here.
+ */
+ konva: {
+ layer: Konva.Layer;
+ };
+
+ /**
+ * The transformer for this entity adapter.
+ */
+ transformer: CanvasEntityTransformer;
+
+ /**
+ * The renderer for this entity adapter.
+ */
+ renderer: CanvasEntityRenderer;
+
+ constructor(entityIdentifier: CanvasEntityIdentifier, manager: CanvasManager, adapterType: string) {
+ super();
+ this.type = adapterType;
+ this.id = entityIdentifier.id;
+ this.entityIdentifier = entityIdentifier;
+ this.manager = manager;
+ this.parent = manager;
+ this.path = this.manager.buildPath(this);
+ this.log = this.manager.buildLogger(this);
+
+ this.log.debug('Creating module');
+
+ this.konva = {
+ layer: new Konva.Layer({
+ name: `${this.type}:layer`,
+ listening: false,
+ imageSmoothingEnabled: false,
+ }),
+ };
+
+ this.renderer = new CanvasEntityRenderer(this);
+ this.transformer = new CanvasEntityTransformer(this);
+ }
+
+ abstract get state(): T;
+
+ abstract set state(state: T);
+
+ abstract getCanvas: (rect?: Rect) => HTMLCanvasElement;
+
+ abstract getHashableState: () => SerializableObject;
+
+ isInteractable = (): boolean => {
+ return this.state.isEnabled && !this.state.isLocked;
+ };
+
+ hash = (extra?: SerializableObject): string => {
+ const arg = {
+ state: this.getHashableState(),
+ extra,
+ };
+ return stableHash(arg);
+ };
+
+ destroy = (): void => {
+ this.log.debug('Destroying module');
+ this.renderer.destroy();
+ this.transformer.destroy();
+ this.konva.layer.destroy();
+ };
+
+ repr = () => {
+ return {
+ id: this.id,
+ type: this.type,
+ path: this.path,
+ state: deepClone(this.state),
+ transformer: this.transformer.repr(),
+ renderer: this.renderer.repr(),
+ };
+ };
+}
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityLayerAdapter.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityLayerAdapter.ts
deleted file mode 100644
index fa846b3cd0..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityLayerAdapter.ts
+++ /dev/null
@@ -1,228 +0,0 @@
-import type { SerializableObject } from 'common/types';
-import { deepClone } from 'common/util/deepClone';
-import { CanvasEntityRenderer } from 'features/controlLayers/konva/CanvasEntityRenderer';
-import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
-import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
-import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
-import { getLastPointOfLine } from 'features/controlLayers/konva/util';
-import type {
- CanvasBrushLineState,
- CanvasControlLayerState,
- CanvasEntityIdentifier,
- CanvasEraserLineState,
- CanvasRasterLayerState,
- Coordinate,
- Rect,
-} from 'features/controlLayers/store/types';
-import { getEntityIdentifier } from 'features/controlLayers/store/types';
-import Konva from 'konva';
-import type { GroupConfig } from 'konva/lib/Group';
-import { get, omit } from 'lodash-es';
-import type { Logger } from 'roarr';
-import stableHash from 'stable-hash';
-import { assert } from 'tsafe';
-
-/**
- * Handles the rendering for a single raster or control layer entity.
- *
- * This module has two main components:
- * - A transformer, which handles the positioning and interaction state of the layer
- * - A renderer, which handles the rendering of the layer's objects
- *
- * The canvas rendering module interacts with this module to coordinate the rendering of all raster and control layers.
- */
-export class CanvasEntityLayerAdapter extends CanvasModuleBase {
- readonly type = 'entity_layer_adapter';
- readonly id: string;
- readonly path: string[];
- readonly manager: CanvasManager;
- readonly parent: CanvasManager;
- readonly log: Logger;
-
- /**
- * The last known state of the entity.
- */
- state: CanvasRasterLayerState | CanvasControlLayerState;
-
- /**
- * The Konva nodes that make up the entity layer:
- * - A layer to hold the everything
- *
- * Note that the transformer and object renderer have their own Konva nodes, but they are not stored here.
- */
- konva: {
- layer: Konva.Layer;
- };
-
- /**
- * The transformer for this entity layer.
- */
- transformer: CanvasEntityTransformer;
-
- /**
- * The renderer for this entity layer.
- */
- renderer: CanvasEntityRenderer;
-
- /**
- * Whether this is the first render of the entity layer.
- */
- isFirstRender: boolean = true;
-
- constructor(state: CanvasEntityLayerAdapter['state'], manager: CanvasEntityLayerAdapter['manager']) {
- super();
- this.id = state.id;
- this.manager = manager;
- this.parent = manager;
- this.path = this.manager.buildPath(this);
- this.log = this.manager.buildLogger(this);
-
- this.log.debug('Creating module');
-
- this.state = state;
-
- this.konva = {
- layer: new Konva.Layer({
- name: `${this.type}:layer`,
- listening: false,
- imageSmoothingEnabled: false,
- }),
- };
-
- this.renderer = new CanvasEntityRenderer(this);
- this.transformer = new CanvasEntityTransformer(this);
- }
-
- /**
- * Get this entity's entity identifier
- */
- getEntityIdentifier = (): CanvasEntityIdentifier => {
- return getEntityIdentifier(this.state);
- };
-
- update = async (arg?: { state: CanvasEntityLayerAdapter['state'] }): Promise => {
- const state = get(arg, 'state', this.state);
-
- const prevState = this.state;
- this.state = state;
-
- if (!this.isFirstRender && prevState === state) {
- this.log.trace('State unchanged, skipping update');
- return;
- }
-
- this.log.debug('Updating');
- const { position, objects, opacity, isEnabled, isLocked } = state;
-
- if (this.isFirstRender || isEnabled !== prevState.isEnabled) {
- this.updateVisibility({ isEnabled });
- }
- if (this.isFirstRender || isLocked !== prevState.isLocked) {
- this.transformer.syncInteractionState();
- }
- if (this.isFirstRender || objects !== prevState.objects) {
- await this.updateObjects({ objects });
- }
- if (this.isFirstRender || position !== prevState.position) {
- this.transformer.updatePosition({ position });
- }
- if (this.isFirstRender || opacity !== prevState.opacity) {
- this.renderer.updateOpacity(opacity);
- }
-
- if (state.type === 'control_layer' && prevState.type === 'control_layer') {
- if (this.isFirstRender || state.withTransparencyEffect !== prevState.withTransparencyEffect) {
- this.renderer.updateTransparencyEffect(state.withTransparencyEffect);
- }
- }
-
- if (this.isFirstRender) {
- this.transformer.updateBbox();
- }
-
- this.isFirstRender = false;
- };
-
- updateVisibility = (arg?: { isEnabled: boolean }) => {
- this.log.trace('Updating visibility');
- const isEnabled = get(arg, 'isEnabled', this.state.isEnabled);
- this.konva.layer.visible(isEnabled);
- this.renderer.syncCache(isEnabled);
- };
-
- updateObjects = async (arg?: { objects: CanvasRasterLayerState['objects'] }) => {
- this.log.trace('Updating objects');
-
- const objects = get(arg, 'objects', this.state.objects);
-
- const didUpdate = await this.renderer.render(objects);
-
- if (didUpdate) {
- this.transformer.requestRectCalculation();
- }
- };
-
- getCanvas = (rect?: Rect): HTMLCanvasElement => {
- this.log.trace({ rect }, 'Getting canvas');
- // The opacity may have been changed in response to user selecting a different entity category, so we must restore
- // the original opacity before rendering the canvas
- const attrs: GroupConfig = { opacity: this.state.opacity };
- const canvas = this.renderer.getCanvas(rect, attrs);
- return canvas;
- };
-
- getHashableState = (): SerializableObject => {
- if (this.state.type === 'control_layer') {
- const keysToOmit: (keyof CanvasControlLayerState)[] = ['name', 'controlAdapter', 'withTransparencyEffect'];
- return omit(this.state, keysToOmit);
- } else if (this.state.type === 'raster_layer') {
- const keysToOmit: (keyof CanvasRasterLayerState)[] = ['name'];
- return omit(this.state, keysToOmit);
- } else {
- assert(false, 'Unexpected layer type');
- }
- };
-
- hash = (extra?: SerializableObject): string => {
- const arg = {
- state: this.getHashableState(),
- extra,
- };
- return stableHash(arg);
- };
-
- getLastPointOfLastLine = (type: CanvasBrushLineState['type'] | CanvasEraserLineState['type']): Coordinate | null => {
- const lastObject = this.state.objects[this.state.objects.length - 1];
- if (!lastObject) {
- return null;
- }
-
- if (lastObject.type === type) {
- return getLastPointOfLine(lastObject.points);
- }
-
- return null;
- };
-
- isInteractable = (): boolean => {
- return this.state.isEnabled && !this.state.isLocked;
- };
-
- destroy = (): void => {
- this.log.debug('Destroying module');
- this.renderer.destroy();
- this.transformer.destroy();
- this.konva.layer.destroy();
- };
-
- repr = () => {
- return {
- id: this.id,
- type: this.type,
- path: this.path,
- state: deepClone(this.state),
- transformer: this.transformer.repr(),
- renderer: this.renderer.repr(),
- };
- };
-}
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityMaskAdapter.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityMaskAdapter.ts
deleted file mode 100644
index eb5a4fe5fb..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityMaskAdapter.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-import type { SerializableObject } from 'common/types';
-import { deepClone } from 'common/util/deepClone';
-import { CanvasEntityRenderer } from 'features/controlLayers/konva/CanvasEntityRenderer';
-import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
-import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
-import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
-import { getLastPointOfLine } from 'features/controlLayers/konva/util';
-import type {
- CanvasBrushLineState,
- CanvasEntityIdentifier,
- CanvasEraserLineState,
- CanvasInpaintMaskState,
- CanvasRegionalGuidanceState,
- Coordinate,
- Rect,
-} from 'features/controlLayers/store/types';
-import { getEntityIdentifier } from 'features/controlLayers/store/types';
-import Konva from 'konva';
-import type { GroupConfig } from 'konva/lib/Group';
-import { get, omit } from 'lodash-es';
-import type { Logger } from 'roarr';
-import stableHash from 'stable-hash';
-
-export class CanvasEntityMaskAdapter extends CanvasModuleBase {
- readonly type = 'entity_mask_adapter';
- readonly id: string;
- readonly path: string[];
- readonly parent: CanvasManager;
- readonly manager: CanvasManager;
- readonly log: Logger;
-
- state: CanvasInpaintMaskState | CanvasRegionalGuidanceState;
-
- transformer: CanvasEntityTransformer;
- renderer: CanvasEntityRenderer;
-
- isFirstRender: boolean = true;
-
- konva: {
- layer: Konva.Layer;
- };
-
- constructor(state: CanvasEntityMaskAdapter['state'], manager: CanvasEntityMaskAdapter['manager']) {
- super();
- this.id = state.id;
- this.parent = manager;
- this.manager = manager;
- this.path = this.manager.buildPath(this);
- this.log = this.manager.buildLogger(this);
-
- this.log.debug('Creating module');
-
- this.state = state;
-
- this.konva = {
- layer: new Konva.Layer({
- name: `${this.type}:layer`,
- listening: false,
- imageSmoothingEnabled: false,
- }),
- };
-
- this.renderer = new CanvasEntityRenderer(this);
- this.transformer = new CanvasEntityTransformer(this);
- }
-
- /**
- * Get this entity's entity identifier
- */
- getEntityIdentifier = (): CanvasEntityIdentifier => {
- return getEntityIdentifier(this.state);
- };
-
- update = async (arg?: { state: CanvasEntityMaskAdapter['state'] }) => {
- const state = get(arg, 'state', this.state);
-
- const prevState = this.state;
- this.state = state;
-
- if (!this.isFirstRender && prevState === state && prevState.fill === state.fill) {
- this.log.trace('State unchanged, skipping update');
- return;
- }
-
- this.log.debug('Updating');
- const { position, objects, isEnabled, isLocked, opacity } = state;
-
- if (this.isFirstRender || objects !== prevState.objects) {
- await this.updateObjects({ objects });
- }
- if (this.isFirstRender || position !== prevState.position) {
- this.transformer.updatePosition({ position });
- }
- if (this.isFirstRender || opacity !== prevState.opacity) {
- this.renderer.updateOpacity(opacity);
- }
- if (this.isFirstRender || isEnabled !== prevState.isEnabled) {
- this.updateVisibility({ isEnabled });
- }
- if (this.isFirstRender || isLocked !== prevState.isLocked) {
- this.transformer.syncInteractionState();
- }
- if (this.isFirstRender || state.fill !== prevState.fill) {
- this.renderer.updateCompositingRectFill(state.fill);
- }
-
- if (this.isFirstRender) {
- this.renderer.updateCompositingRectSize();
- }
-
- if (this.isFirstRender) {
- this.transformer.updateBbox();
- }
-
- this.isFirstRender = false;
- };
-
- updateObjects = async (arg?: { objects: CanvasInpaintMaskState['objects'] }) => {
- this.log.trace('Updating objects');
-
- const objects = get(arg, 'objects', this.state.objects);
-
- const didUpdate = await this.renderer.render(objects);
-
- if (didUpdate) {
- this.transformer.requestRectCalculation();
- }
- };
-
- updateVisibility = (arg?: { isEnabled: boolean }) => {
- this.log.trace('Updating visibility');
- const isEnabled = get(arg, 'isEnabled', this.state.isEnabled);
- this.konva.layer.visible(isEnabled);
- };
-
- getLastPointOfLastLine = (type: CanvasBrushLineState['type'] | CanvasEraserLineState['type']): Coordinate | null => {
- const lastObject = this.state.objects[this.state.objects.length - 1];
- if (!lastObject) {
- return null;
- }
-
- if (lastObject.type === type) {
- return getLastPointOfLine(lastObject.points);
- }
-
- return null;
- };
-
- getHashableState = (): SerializableObject => {
- const keysToOmit: (keyof CanvasEntityMaskAdapter['state'])[] = ['fill', 'name', 'opacity'];
- return omit(this.state, keysToOmit);
- };
-
- hash = (extra?: SerializableObject): string => {
- const arg = {
- state: this.getHashableState(),
- extra,
- };
- return stableHash(arg);
- };
-
- getCanvas = (rect?: Rect): HTMLCanvasElement => {
- // The opacity may have been changed in response to user selecting a different entity category, and the mask regions
- // should be fully opaque - set opacity to 1 before rendering the canvas
- const attrs: GroupConfig = { opacity: 1 };
- const canvas = this.renderer.getCanvas(rect, attrs);
- return canvas;
- };
-
- isInteractable = (): boolean => {
- return this.state.isEnabled && !this.state.isLocked;
- };
-
- destroy = () => {
- this.log.debug('Destroying module');
- this.transformer.destroy();
- this.renderer.destroy();
- this.konva.layer.destroy();
- };
-
- repr = () => {
- return {
- id: this.id,
- type: this.type,
- path: this.path,
- state: deepClone(this.state),
- };
- };
-}
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityRenderer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityRenderer.ts
index 8c9f8e12d9..ee28a35ac4 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityRenderer.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityRenderer.ts
@@ -1,14 +1,11 @@
import { rgbColorToString } from 'common/util/colorCodeTransformers';
-import type { CanvasControlLayerAdapter } from 'features/controlLayers/konva/CanvasControlLayerAdapter';
-import type { CanvasInpaintMaskAdapter } from 'features/controlLayers/konva/CanvasInpaintMaskAdapter';
+import type { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapterBase';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import { CanvasObjectBrushLineRenderer } from 'features/controlLayers/konva/CanvasObjectBrushLineRenderer';
import { CanvasObjectEraserLineRenderer } from 'features/controlLayers/konva/CanvasObjectEraserLineRenderer';
import { CanvasObjectImageRenderer } from 'features/controlLayers/konva/CanvasObjectImageRenderer';
import { CanvasObjectRectRenderer } from 'features/controlLayers/konva/CanvasObjectRectRenderer';
-import type { CanvasRasterLayerAdapter } from 'features/controlLayers/konva/CanvasRasterLayerAdapter';
-import type { CanvasRegionalGuidanceAdapter } from 'features/controlLayers/konva/CanvasRegionalGuidanceAdapter';
import { LightnessToAlphaFilter } from 'features/controlLayers/konva/filters';
import { getPatternSVG } from 'features/controlLayers/konva/patterns/getPatternSVG';
import {
@@ -66,11 +63,7 @@ export class CanvasEntityRenderer extends CanvasModuleBase {
readonly type = 'entity_renderer';
readonly id: string;
readonly path: string[];
- readonly parent:
- | CanvasRasterLayerAdapter
- | CanvasControlLayerAdapter
- | CanvasInpaintMaskAdapter
- | CanvasRegionalGuidanceAdapter;
+ readonly parent: CanvasEntityAdapterBase;
readonly manager: CanvasManager;
readonly log: Logger;
@@ -141,13 +134,7 @@ export class CanvasEntityRenderer extends CanvasModuleBase {
*/
$canvasCache = atom<{ canvas: HTMLCanvasElement; rect: Rect } | null>(null);
- constructor(
- parent:
- | CanvasRasterLayerAdapter
- | CanvasControlLayerAdapter
- | CanvasInpaintMaskAdapter
- | CanvasRegionalGuidanceAdapter
- ) {
+ constructor(parent: CanvasEntityAdapterBase) {
super();
this.id = getPrefixedId(this.type);
this.parent = parent;
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityTransformer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityTransformer.ts
index 7c755624ad..0c63901000 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityTransformer.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntityTransformer.ts
@@ -1,9 +1,6 @@
-import type { CanvasControlLayerAdapter } from 'features/controlLayers/konva/CanvasControlLayerAdapter';
-import type { CanvasInpaintMaskAdapter } from 'features/controlLayers/konva/CanvasInpaintMaskAdapter';
+import type { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapterBase';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
-import type { CanvasRasterLayerAdapter } from 'features/controlLayers/konva/CanvasRasterLayerAdapter';
-import type { CanvasRegionalGuidanceAdapter } from 'features/controlLayers/konva/CanvasRegionalGuidanceAdapter';
import { canvasToImageData, getEmptyRect, getPrefixedId } from 'features/controlLayers/konva/util';
import type { Coordinate, Rect, RectWithRotation } from 'features/controlLayers/store/types';
import Konva from 'konva';
@@ -82,11 +79,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
readonly type = 'entity_transformer';
readonly id: string;
readonly path: string[];
- readonly parent:
- | CanvasRasterLayerAdapter
- | CanvasControlLayerAdapter
- | CanvasInpaintMaskAdapter
- | CanvasRegionalGuidanceAdapter;
+ readonly parent: CanvasEntityAdapterBase;
readonly manager: CanvasManager;
readonly log: Logger;
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMaskAdapter.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMaskAdapter.ts
index 1a02b8077c..0a0028b830 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMaskAdapter.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMaskAdapter.ts
@@ -1,61 +1,21 @@
import type { SerializableObject } from 'common/types';
-import { deepClone } from 'common/util/deepClone';
-import { CanvasEntityRenderer } from 'features/controlLayers/konva/CanvasEntityRenderer';
-import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
+import { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapterBase';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
-import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import type { CanvasEntityIdentifier, CanvasInpaintMaskState, Rect } from 'features/controlLayers/store/types';
-import { getEntityIdentifier } from 'features/controlLayers/store/types';
-import Konva from 'konva';
import type { GroupConfig } from 'konva/lib/Group';
import { omit } from 'lodash-es';
-import type { Logger } from 'roarr';
-import stableHash from 'stable-hash';
import { assert } from 'tsafe';
-export class CanvasInpaintMaskAdapter extends CanvasModuleBase {
- readonly type = 'inpaint_mask_adapter';
- readonly id: string;
- readonly path: string[];
- readonly parent: CanvasManager;
- readonly manager: CanvasManager;
- readonly log: Logger;
-
- entityIdentifier: CanvasEntityIdentifier<'inpaint_mask'>;
+export class CanvasInpaintMaskAdapter extends CanvasEntityAdapterBase {
+ static TYPE = 'inpaint_mask_adapter';
/**
* The last known state of the entity.
*/
private _state: CanvasInpaintMaskState | null = null;
- transformer: CanvasEntityTransformer;
- renderer: CanvasEntityRenderer;
-
- konva: {
- layer: Konva.Layer;
- };
-
constructor(entityIdentifier: CanvasEntityIdentifier<'inpaint_mask'>, manager: CanvasManager) {
- super();
- this.id = entityIdentifier.id;
- this.entityIdentifier = entityIdentifier;
- this.parent = manager;
- this.manager = manager;
- this.path = this.manager.buildPath(this);
- this.log = this.manager.buildLogger(this);
-
- this.log.debug('Creating module');
-
- this.konva = {
- layer: new Konva.Layer({
- name: `${this.type}:layer`,
- listening: false,
- imageSmoothingEnabled: false,
- }),
- };
-
- this.renderer = new CanvasEntityRenderer(this);
- this.transformer = new CanvasEntityTransformer(this);
+ super(entityIdentifier, manager, CanvasInpaintMaskAdapter.TYPE);
}
get state(): CanvasInpaintMaskState {
@@ -71,13 +31,6 @@ export class CanvasInpaintMaskAdapter extends CanvasModuleBase {
this._state = state;
}
- /**
- * Get this entity's entity identifier
- */
- getEntityIdentifier = (): CanvasEntityIdentifier => {
- return getEntityIdentifier(this.state);
- };
-
update = async (state: CanvasInpaintMaskState) => {
const prevState = this.state;
this.state = state;
@@ -125,14 +78,6 @@ export class CanvasInpaintMaskAdapter extends CanvasModuleBase {
return omit(this.state, keysToOmit);
};
- hash = (extra?: SerializableObject): string => {
- const arg = {
- state: this.getHashableState(),
- extra,
- };
- return stableHash(arg);
- };
-
getCanvas = (rect?: Rect): HTMLCanvasElement => {
// The opacity may have been changed in response to user selecting a different entity category, and the mask regions
// should be fully opaque - set opacity to 1 before rendering the canvas
@@ -140,24 +85,4 @@ export class CanvasInpaintMaskAdapter extends CanvasModuleBase {
const canvas = this.renderer.getCanvas(rect, attrs);
return canvas;
};
-
- isInteractable = (): boolean => {
- return this.state.isEnabled && !this.state.isLocked;
- };
-
- destroy = () => {
- this.log.debug('Destroying module');
- this.transformer.destroy();
- this.renderer.destroy();
- this.konva.layer.destroy();
- };
-
- repr = () => {
- return {
- id: this.id,
- type: this.type,
- path: this.path,
- state: deepClone(this.state),
- };
- };
}
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRasterLayerAdapter.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRasterLayerAdapter.ts
index e1e8bbeecc..c3a9da71d3 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRasterLayerAdapter.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRasterLayerAdapter.ts
@@ -1,73 +1,20 @@
import type { SerializableObject } from 'common/types';
-import { deepClone } from 'common/util/deepClone';
-import { CanvasEntityRenderer } from 'features/controlLayers/konva/CanvasEntityRenderer';
-import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
+import { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapterBase';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
-import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import type { CanvasEntityIdentifier, CanvasRasterLayerState, Rect } from 'features/controlLayers/store/types';
-import Konva from 'konva';
import type { GroupConfig } from 'konva/lib/Group';
import { omit } from 'lodash-es';
-import type { Logger } from 'roarr';
-import stableHash from 'stable-hash';
import { assert } from 'tsafe';
-export class CanvasRasterLayerAdapter extends CanvasModuleBase {
- readonly type = 'raster_layer_adapter';
- readonly id: string;
- readonly path: string[];
- readonly manager: CanvasManager;
- readonly parent: CanvasManager;
- readonly log: Logger;
-
- entityIdentifier: CanvasEntityIdentifier<'raster_layer'>;
-
+export class CanvasRasterLayerAdapter extends CanvasEntityAdapterBase {
+ static TYPE = 'raster_layer_adapter';
/**
* The last known state of the entity.
*/
private _state: CanvasRasterLayerState | null = null;
- /**
- * The Konva nodes that make up the entity layer:
- * - A layer to hold the everything
- *
- * Note that the transformer and object renderer have their own Konva nodes, but they are not stored here.
- */
- konva: {
- layer: Konva.Layer;
- };
-
- /**
- * The transformer for this entity layer.
- */
- transformer: CanvasEntityTransformer;
-
- /**
- * The renderer for this entity layer.
- */
- renderer: CanvasEntityRenderer;
-
constructor(entityIdentifier: CanvasEntityIdentifier<'raster_layer'>, manager: CanvasManager) {
- super();
- this.id = entityIdentifier.id;
- this.entityIdentifier = entityIdentifier;
- this.manager = manager;
- this.parent = manager;
- this.path = this.manager.buildPath(this);
- this.log = this.manager.buildLogger(this);
-
- this.log.debug('Creating module');
-
- this.konva = {
- layer: new Konva.Layer({
- name: `${this.type}:layer`,
- listening: false,
- imageSmoothingEnabled: false,
- }),
- };
-
- this.renderer = new CanvasEntityRenderer(this);
- this.transformer = new CanvasEntityTransformer(this);
+ super(entityIdentifier, manager, CanvasRasterLayerAdapter.TYPE);
}
get state(): CanvasRasterLayerState {
@@ -126,38 +73,8 @@ export class CanvasRasterLayerAdapter extends CanvasModuleBase {
return canvas;
};
- isInteractable = (): boolean => {
- return this.state.isEnabled && !this.state.isLocked;
- };
-
getHashableState = (): SerializableObject => {
const keysToOmit: (keyof CanvasRasterLayerState)[] = ['name'];
return omit(this.state, keysToOmit);
};
-
- hash = (extra?: SerializableObject): string => {
- const arg = {
- state: this.getHashableState(),
- extra,
- };
- return stableHash(arg);
- };
-
- destroy = (): void => {
- this.log.debug('Destroying module');
- this.renderer.destroy();
- this.transformer.destroy();
- this.konva.layer.destroy();
- };
-
- repr = () => {
- return {
- id: this.id,
- type: this.type,
- path: this.path,
- state: deepClone(this.state),
- transformer: this.transformer.repr(),
- renderer: this.renderer.repr(),
- };
- };
}
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegionalGuidanceAdapter.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegionalGuidanceAdapter.ts
index e72dbfc174..5ce08fa789 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegionalGuidanceAdapter.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegionalGuidanceAdapter.ts
@@ -1,61 +1,21 @@
import type { SerializableObject } from 'common/types';
-import { deepClone } from 'common/util/deepClone';
-import { CanvasEntityRenderer } from 'features/controlLayers/konva/CanvasEntityRenderer';
-import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
+import { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapterBase';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
-import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import type { CanvasEntityIdentifier, CanvasRegionalGuidanceState, Rect } from 'features/controlLayers/store/types';
-import { getEntityIdentifier } from 'features/controlLayers/store/types';
-import Konva from 'konva';
import type { GroupConfig } from 'konva/lib/Group';
import { omit } from 'lodash-es';
-import type { Logger } from 'roarr';
-import stableHash from 'stable-hash';
import { assert } from 'tsafe';
-export class CanvasRegionalGuidanceAdapter extends CanvasModuleBase {
- readonly type = 'regional_guidance_adapter';
- readonly id: string;
- readonly path: string[];
- readonly parent: CanvasManager;
- readonly manager: CanvasManager;
- readonly log: Logger;
-
- entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>;
+export class CanvasRegionalGuidanceAdapter extends CanvasEntityAdapterBase {
+ static TYPE = 'regional_guidance_adapter';
/**
* The last known state of the entity.
*/
private _state: CanvasRegionalGuidanceState | null = null;
- transformer: CanvasEntityTransformer;
- renderer: CanvasEntityRenderer;
-
- konva: {
- layer: Konva.Layer;
- };
-
constructor(entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>, manager: CanvasManager) {
- super();
- this.id = entityIdentifier.id;
- this.entityIdentifier = entityIdentifier;
- this.parent = manager;
- this.manager = manager;
- this.path = this.manager.buildPath(this);
- this.log = this.manager.buildLogger(this);
-
- this.log.debug('Creating module');
-
- this.konva = {
- layer: new Konva.Layer({
- name: `${this.type}:layer`,
- listening: false,
- imageSmoothingEnabled: false,
- }),
- };
-
- this.renderer = new CanvasEntityRenderer(this);
- this.transformer = new CanvasEntityTransformer(this);
+ super(entityIdentifier, manager, CanvasRegionalGuidanceAdapter.TYPE);
}
get state(): CanvasRegionalGuidanceState {
@@ -68,20 +28,12 @@ export class CanvasRegionalGuidanceAdapter extends CanvasModuleBase {
}
set state(state: CanvasRegionalGuidanceState) {
+ const prevState = this._state;
this._state = state;
+ this.render(state, prevState);
}
- /**
- * Get this entity's entity identifier
- */
- getEntityIdentifier = (): CanvasEntityIdentifier => {
- return getEntityIdentifier(this.state);
- };
-
- update = async (state: CanvasRegionalGuidanceState) => {
- const prevState = this.state;
- this.state = state;
-
+ render = async (state: CanvasRegionalGuidanceState, prevState: CanvasRegionalGuidanceState | null) => {
if (prevState && prevState === state) {
this.log.trace('State unchanged, skipping update');
return;
@@ -125,14 +77,6 @@ export class CanvasRegionalGuidanceAdapter extends CanvasModuleBase {
return omit(this.state, keysToOmit);
};
- hash = (extra?: SerializableObject): string => {
- const arg = {
- state: this.getHashableState(),
- extra,
- };
- return stableHash(arg);
- };
-
getCanvas = (rect?: Rect): HTMLCanvasElement => {
// The opacity may have been changed in response to user selecting a different entity category, and the mask regions
// should be fully opaque - set opacity to 1 before rendering the canvas
@@ -140,24 +84,4 @@ export class CanvasRegionalGuidanceAdapter extends CanvasModuleBase {
const canvas = this.renderer.getCanvas(rect, attrs);
return canvas;
};
-
- isInteractable = (): boolean => {
- return this.state.isEnabled && !this.state.isLocked;
- };
-
- destroy = () => {
- this.log.debug('Destroying module');
- this.transformer.destroy();
- this.renderer.destroy();
- this.konva.layer.destroy();
- };
-
- repr = () => {
- return {
- id: this.id,
- type: this.type,
- path: this.path,
- state: deepClone(this.state),
- };
- };
}
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApiModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApiModule.ts
index 0dd85b3819..b3fb091365 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApiModule.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStateApiModule.ts
@@ -1,6 +1,7 @@
import { $alt, $ctrl, $meta, $shift } from '@invoke-ai/ui-library';
import type { AppStore } from 'app/store/store';
import type { CanvasControlLayerAdapter } from 'features/controlLayers/konva/CanvasControlLayerAdapter';
+import type { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapterBase';
import type { CanvasInpaintMaskAdapter } from 'features/controlLayers/konva/CanvasInpaintMaskAdapter';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
@@ -362,13 +363,7 @@ export class CanvasStateApiModule extends CanvasModuleBase {
/**
* The entity adapter being transformed, if any.
*/
- $transformingAdapter = atom<
- | CanvasRasterLayerAdapter
- | CanvasControlLayerAdapter
- | CanvasInpaintMaskAdapter
- | CanvasRegionalGuidanceAdapter
- | null
- >(null);
+ $transformingAdapter = atom(null);
/**
* Whether an entity is currently being transformed. Derived from `$transformingAdapter`.
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasToolModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasToolModule.ts
index 0d3b01c91a..308ee5f7ed 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasToolModule.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasToolModule.ts
@@ -24,7 +24,7 @@ import type {
RgbColor,
Tool,
} from 'features/controlLayers/store/types';
-import { isDrawableEntity, RGBA_BLACK } from 'features/controlLayers/store/types';
+import { isRenderableEntity, RGBA_BLACK } from 'features/controlLayers/store/types';
import Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import { atom } from 'nanostores';
@@ -171,7 +171,7 @@ export class CanvasToolModule extends CanvasModuleBase {
!!selectedEntity &&
selectedEntity.state.isEnabled &&
!selectedEntity.state.isLocked &&
- isDrawableEntity(selectedEntity.state);
+ isRenderableEntity(selectedEntity.state);
this.syncCursorStyle();
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
index ba62226c3c..5987553f26 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
@@ -56,7 +56,7 @@ import {
imageDTOToImageWithDims,
initialControlNet,
initialIPAdapter,
- isDrawableEntity,
+ isRenderableEntity,
} from './types';
const DEFAULT_MASK_COLORS: RgbColor[] = [
@@ -818,7 +818,7 @@ export const canvasSlice = createSlice({
const entity = selectEntity(state, entityIdentifier);
if (!entity) {
return;
- } else if (isDrawableEntity(entity)) {
+ } else if (isRenderableEntity(entity)) {
entity.isEnabled = true;
entity.objects = [];
entity.position = { x: 0, y: 0 };
@@ -907,7 +907,7 @@ export const canvasSlice = createSlice({
return;
}
- if (isDrawableEntity(entity)) {
+ if (isRenderableEntity(entity)) {
entity.position = position;
}
},
@@ -918,7 +918,7 @@ export const canvasSlice = createSlice({
return;
}
- if (isDrawableEntity(entity)) {
+ if (isRenderableEntity(entity)) {
if (replaceObjects) {
entity.objects = [imageObject];
entity.position = { x: rect.x, y: rect.y };
@@ -932,7 +932,7 @@ export const canvasSlice = createSlice({
return;
}
- if (!isDrawableEntity(entity)) {
+ if (!isRenderableEntity(entity)) {
assert(false, `Cannot add a brush line to a non-drawable entity of type ${entity.type}`);
}
@@ -947,7 +947,7 @@ export const canvasSlice = createSlice({
return;
}
- if (!isDrawableEntity(entity)) {
+ if (!isRenderableEntity(entity)) {
assert(false, `Cannot add a eraser line to a non-drawable entity of type ${entity.type}`);
}
@@ -962,7 +962,7 @@ export const canvasSlice = createSlice({
return;
}
- if (!isDrawableEntity(entity)) {
+ if (!isRenderableEntity(entity)) {
assert(false, `Cannot add a rect to a non-drawable entity of type ${entity.type}`);
}
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
index 048af3b7d5..e7ce9e7a6f 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
@@ -675,6 +675,12 @@ export type CanvasEntityState =
| CanvasInpaintMaskState
| CanvasIPAdapterState;
+export type CanvasRenderableEntityState =
+ | CanvasRasterLayerState
+ | CanvasControlLayerState
+ | CanvasRegionalGuidanceState
+ | CanvasInpaintMaskState;
+
export type CanvasEntityType = CanvasEntityState['type'];
export type CanvasEntityIdentifier = { id: string; type: T };
@@ -773,7 +779,9 @@ export type EntityRasterizedPayload = EntityIdentifierPayload<{
export type GenerationMode = 'txt2img' | 'img2img' | 'inpaint' | 'outpaint';
-export function isDrawableEntityType(entityType: CanvasEntityState['type']) {
+export function isDrawableEntityType(
+ entityType: CanvasEntityState['type']
+): entityType is CanvasRenderableEntityState['type'] {
return (
entityType === 'raster_layer' ||
entityType === 'control_layer' ||
@@ -782,9 +790,7 @@ export function isDrawableEntityType(entityType: CanvasEntityState['type']) {
);
}
-export function isDrawableEntity(
- entity: CanvasEntityState
-): entity is CanvasRasterLayerState | CanvasControlLayerState | CanvasRegionalGuidanceState | CanvasInpaintMaskState {
+export function isRenderableEntity(entity: CanvasEntityState): entity is CanvasRenderableEntityState {
return isDrawableEntityType(entity.type);
}