From f7bbc4004acb59123a5dda03175d0dda130da206 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Wed, 3 May 2023 14:35:47 +1000
Subject: [PATCH] feat(ui): wip canvas nodes migration 3
---
.../canvas/hooks/useGetCanvasNodeType.ts | 30 ------
.../src/features/canvas/store/canvasSlice.ts | 8 +-
.../src/features/canvas/store/canvasTypes.ts | 1 +
...{getCanvasDataURLs.ts => getCanvasData.ts} | 3 +-
.../features/canvas/util/getCanvasNodeType.ts | 102 ------------------
.../nodes/components/NodeGraphOverlay.tsx | 2 +-
...uildLinearGraph.ts => buildCanvasGraph.ts} | 71 ++++--------
.../features/nodes/util/buildLinearGraph.ts | 39 +++++++
.../buildNodesGraph.ts | 0
.../UnifiedCanvas/UnifiedCanvasWorkarea.tsx | 27 -----
.../web/src/services/thunks/session.ts | 8 +-
11 files changed, 70 insertions(+), 221 deletions(-)
delete mode 100644 invokeai/frontend/web/src/features/canvas/hooks/useGetCanvasNodeType.ts
rename invokeai/frontend/web/src/features/canvas/util/{getCanvasDataURLs.ts => getCanvasData.ts} (97%)
delete mode 100644 invokeai/frontend/web/src/features/canvas/util/getCanvasNodeType.ts
rename invokeai/frontend/web/src/features/nodes/util/{linearGraphBuilder/buildLinearGraph.ts => buildCanvasGraph.ts} (51%)
create mode 100644 invokeai/frontend/web/src/features/nodes/util/buildLinearGraph.ts
rename invokeai/frontend/web/src/features/nodes/util/{nodesGraphBuilder => }/buildNodesGraph.ts (100%)
diff --git a/invokeai/frontend/web/src/features/canvas/hooks/useGetCanvasNodeType.ts b/invokeai/frontend/web/src/features/canvas/hooks/useGetCanvasNodeType.ts
deleted file mode 100644
index 91f8e50d29..0000000000
--- a/invokeai/frontend/web/src/features/canvas/hooks/useGetCanvasNodeType.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { createSelector } from '@reduxjs/toolkit';
-import { useAppSelector } from 'app/store/storeHooks';
-import { canvasSelector } from '../store/canvasSelectors';
-import { useMemo } from 'react';
-import { getCanvasNodeType } from '../util/getCanvasNodeType';
-
-const selector = createSelector(canvasSelector, (canvas) => {
- const {
- layerState: { objects },
- boundingBoxCoordinates,
- boundingBoxDimensions,
- stageScale,
- isMaskEnabled,
- } = canvas;
- return {
- objects,
- boundingBoxCoordinates,
- boundingBoxDimensions,
- stageScale,
- isMaskEnabled,
- };
-});
-
-export const useGetCanvasNodeType = () => {
- const data = useAppSelector(selector);
-
- const nodeType = useMemo(() => getCanvasNodeType(data), [data]);
-
- return nodeType;
-};
diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts b/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts
index 5ff8ea4295..22d4270e18 100644
--- a/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts
+++ b/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts
@@ -29,6 +29,7 @@ import {
isCanvasBaseImage,
isCanvasMaskLine,
} from './canvasTypes';
+import { invocationComplete } from 'services/events/actions';
export const initialLayerState: CanvasLayerState = {
objects: [],
@@ -289,7 +290,7 @@ export const canvasSlice = createSlice({
state,
action: PayloadAction<{
boundingBox: IRect;
- image: InvokeAI._Image;
+ image: InvokeAI.Image;
}>
) => {
const { boundingBox, image } = action.payload;
@@ -815,6 +816,11 @@ export const canvasSlice = createSlice({
state.isTransformingBoundingBox = false;
},
},
+ extraReducers(builder) {
+ builder.addCase(invocationComplete, (state, action) => {
+ //
+ });
+ },
});
export const {
diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts b/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts
index 2eec0e9bed..df24e9ee4d 100644
--- a/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts
+++ b/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts
@@ -162,4 +162,5 @@ export interface CanvasState {
stageDimensions: Dimensions;
stageScale: number;
tool: CanvasTool;
+ pendingBoundingBox?: IRect;
}
diff --git a/invokeai/frontend/web/src/features/canvas/util/getCanvasDataURLs.ts b/invokeai/frontend/web/src/features/canvas/util/getCanvasData.ts
similarity index 97%
rename from invokeai/frontend/web/src/features/canvas/util/getCanvasDataURLs.ts
rename to invokeai/frontend/web/src/features/canvas/util/getCanvasData.ts
index e309057eee..c6192f0a09 100644
--- a/invokeai/frontend/web/src/features/canvas/util/getCanvasDataURLs.ts
+++ b/invokeai/frontend/web/src/features/canvas/util/getCanvasData.ts
@@ -16,7 +16,7 @@ import { masks } from 'dateformat';
const moduleLog = log.child({ namespace: 'getCanvasDataURLs' });
-export const getCanvasDataURLs = (state: RootState) => {
+export const getCanvasData = (state: RootState) => {
const canvasBaseLayer = getCanvasBaseLayer();
const canvasStage = getCanvasStage();
@@ -85,7 +85,6 @@ export const getCanvasDataURLs = (state: RootState) => {
offscreenContainer.remove();
if (!maskImageData) {
- moduleLog.error('Unable to get mask stage context');
return;
}
diff --git a/invokeai/frontend/web/src/features/canvas/util/getCanvasNodeType.ts b/invokeai/frontend/web/src/features/canvas/util/getCanvasNodeType.ts
deleted file mode 100644
index baa250031b..0000000000
--- a/invokeai/frontend/web/src/features/canvas/util/getCanvasNodeType.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { RootState } from 'app/store/store';
-import { getCanvasBaseLayer, getCanvasStage } from './konvaInstanceProvider';
-import {
- CanvasObject,
- Dimensions,
- isCanvasMaskLine,
-} from '../store/canvasTypes';
-import { buildMaskStage, getStageImageData } from './generateMask';
-import { log } from 'app/logging/useLogger';
-import {
- areAnyPixelsBlack,
- getImageDataTransparency,
-} from 'common/util/arrayBuffer';
-import { getNodeType } from 'features/nodes/util/getNodeType';
-import { Vector2d } from 'konva/lib/types';
-
-const moduleLog = log.child({ namespace: 'getCanvasNodeTypes' });
-
-export type GetCanvasNodeTypeArg = {
- objects: CanvasObject[];
- boundingBoxCoordinates: Vector2d;
- boundingBoxDimensions: Dimensions;
- stageScale: number;
- isMaskEnabled: boolean;
-};
-
-export const getCanvasNodeType = (arg: GetCanvasNodeTypeArg) => {
- const canvasBaseLayer = getCanvasBaseLayer();
- const canvasStage = getCanvasStage();
-
- if (!canvasBaseLayer || !canvasStage) {
- moduleLog.error('Unable to find canvas / stage');
- return;
- }
-
- const {
- objects,
- boundingBoxCoordinates,
- boundingBoxDimensions,
- stageScale,
- isMaskEnabled,
- } = arg;
-
- const boundingBox = {
- ...boundingBoxCoordinates,
- ...boundingBoxDimensions,
- };
-
- const tempScale = canvasBaseLayer.scale();
-
- canvasBaseLayer.scale({
- x: 1 / stageScale,
- y: 1 / stageScale,
- });
-
- const absPos = canvasBaseLayer.getAbsolutePosition();
-
- const scaledBoundingBox = {
- x: boundingBox.x + absPos.x,
- y: boundingBox.y + absPos.y,
- width: boundingBox.width,
- height: boundingBox.height,
- };
-
- const { stage: maskStage, offscreenContainer } = buildMaskStage(
- isMaskEnabled ? objects.filter(isCanvasMaskLine) : [],
- scaledBoundingBox
- );
-
- const maskImageData = getStageImageData(maskStage, scaledBoundingBox);
-
- offscreenContainer.remove();
-
- if (!maskImageData) {
- moduleLog.error('Unable to get mask stage context');
- return;
- }
-
- const ctx = canvasBaseLayer.getContext();
-
- const baseImageData = ctx.getImageData(
- boundingBox.x + absPos.x,
- boundingBox.y + absPos.y,
- boundingBox.width,
- boundingBox.height
- );
-
- canvasBaseLayer.scale(tempScale);
-
- const {
- isPartiallyTransparent: baseIsPartiallyTransparent,
- isFullyTransparent: baseIsFullyTransparent,
- } = getImageDataTransparency(baseImageData);
-
- const doesMaskHaveBlackPixels = areAnyPixelsBlack(maskImageData);
-
- return getNodeType(
- baseIsPartiallyTransparent,
- baseIsFullyTransparent,
- doesMaskHaveBlackPixels
- );
-};
diff --git a/invokeai/frontend/web/src/features/nodes/components/NodeGraphOverlay.tsx b/invokeai/frontend/web/src/features/nodes/components/NodeGraphOverlay.tsx
index 90c8e1396f..bcf02eabf5 100644
--- a/invokeai/frontend/web/src/features/nodes/components/NodeGraphOverlay.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/NodeGraphOverlay.tsx
@@ -2,7 +2,7 @@ import { Box } from '@chakra-ui/react';
import { RootState } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { memo } from 'react';
-import { buildNodesGraph } from '../util/nodesGraphBuilder/buildNodesGraph';
+import { buildNodesGraph } from '../util/buildNodesGraph';
const NodeGraphOverlay = () => {
const state = useAppSelector((state: RootState) => state);
diff --git a/invokeai/frontend/web/src/features/nodes/util/linearGraphBuilder/buildLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/buildCanvasGraph.ts
similarity index 51%
rename from invokeai/frontend/web/src/features/nodes/util/linearGraphBuilder/buildLinearGraph.ts
rename to invokeai/frontend/web/src/features/nodes/util/buildCanvasGraph.ts
index e0dd2f4843..24c057c04a 100644
--- a/invokeai/frontend/web/src/features/nodes/util/linearGraphBuilder/buildLinearGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/buildCanvasGraph.ts
@@ -1,58 +1,31 @@
import { RootState } from 'app/store/store';
import { DataURLToImageInvocation, Graph } from 'services/api';
-import { buildImg2ImgNode } from './buildImageToImageNode';
-import { buildTxt2ImgNode } from './buildTextToImageNode';
-import { buildRangeNode } from './buildRangeNode';
-import { buildIterateNode } from './buildIterateNode';
-import { buildEdges } from './buildEdges';
+import { buildImg2ImgNode } from './linearGraphBuilder/buildImageToImageNode';
+import { buildTxt2ImgNode } from './linearGraphBuilder/buildTextToImageNode';
+import { buildRangeNode } from './linearGraphBuilder/buildRangeNode';
+import { buildIterateNode } from './linearGraphBuilder/buildIterateNode';
+import { buildEdges } from './linearGraphBuilder/buildEdges';
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
-import { getCanvasDataURLs } from 'features/canvas/util/getCanvasDataURLs';
-import { log } from 'console';
-import { getNodeType } from '../getNodeType';
+import { getCanvasData } from 'features/canvas/util/getCanvasData';
+import { getNodeType } from './getNodeType';
import { v4 as uuidv4 } from 'uuid';
+import { log } from 'app/logging/useLogger';
+
+const moduleLog = log.child({ namespace: 'buildCanvasGraph' });
/**
- * Builds the Linear workflow graph.
+ * Builds the Canvas workflow graph.
*/
-export const buildLinearGraph = (state: RootState): Graph => {
- // The base node is either a txt2img or img2img node
- const baseNode = state.generation.isImageToImageEnabled
- ? buildImg2ImgNode(state)
- : buildTxt2ImgNode(state);
-
- // We always range and iterate nodes, no matter the iteration count
- // This is required to provide the correct seeds to the backend engine
- const rangeNode = buildRangeNode(state);
- const iterateNode = buildIterateNode();
-
- // Build the edges for the nodes selected.
- const edges = buildEdges(baseNode, rangeNode, iterateNode);
-
- // Assemble!
- const graph = {
- nodes: {
- [rangeNode.id]: rangeNode,
- [iterateNode.id]: iterateNode,
- [baseNode.id]: baseNode,
- },
- edges,
- };
-
- // TODO: hires fix requires latent space upscaling; we don't have nodes for this yet
-
- return graph;
-};
-
-/**
- * Builds the Linear workflow graph.
- */
-export const buildCanvasGraph = (state: RootState): Graph => {
- const c = getCanvasDataURLs(state);
+export const buildCanvasGraph = (state: RootState): Graph | undefined => {
+ const c = getCanvasData(state);
if (!c) {
- throw 'problm creating canvas graph';
+ moduleLog.error('Unable to create canvas graph');
+ return;
}
+ moduleLog.debug({ data: c }, 'Built canvas data');
+
const {
baseDataURL,
maskDataURL,
@@ -61,21 +34,13 @@ export const buildCanvasGraph = (state: RootState): Graph => {
doesMaskHaveBlackPixels,
} = c;
- console.log({
- baseDataURL,
- maskDataURL,
- baseIsPartiallyTransparent,
- baseIsFullyTransparent,
- doesMaskHaveBlackPixels,
- });
-
const nodeType = getNodeType(
baseIsPartiallyTransparent,
baseIsFullyTransparent,
doesMaskHaveBlackPixels
);
- console.log(nodeType);
+ moduleLog.debug(`Node type ${nodeType}`);
// The base node is either a txt2img or img2img node
const baseNode =
diff --git a/invokeai/frontend/web/src/features/nodes/util/buildLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/buildLinearGraph.ts
new file mode 100644
index 0000000000..f247964c72
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/buildLinearGraph.ts
@@ -0,0 +1,39 @@
+import { RootState } from 'app/store/store';
+import { Graph } from 'services/api';
+import { buildImg2ImgNode } from './linearGraphBuilder/buildImageToImageNode';
+import { buildTxt2ImgNode } from './linearGraphBuilder/buildTextToImageNode';
+import { buildRangeNode } from './linearGraphBuilder/buildRangeNode';
+import { buildIterateNode } from './linearGraphBuilder/buildIterateNode';
+import { buildEdges } from './linearGraphBuilder/buildEdges';
+
+/**
+ * Builds the Linear workflow graph.
+ */
+export const buildLinearGraph = (state: RootState): Graph => {
+ // The base node is either a txt2img or img2img node
+ const baseNode = state.generation.isImageToImageEnabled
+ ? buildImg2ImgNode(state)
+ : buildTxt2ImgNode(state);
+
+ // We always range and iterate nodes, no matter the iteration count
+ // This is required to provide the correct seeds to the backend engine
+ const rangeNode = buildRangeNode(state);
+ const iterateNode = buildIterateNode();
+
+ // Build the edges for the nodes selected.
+ const edges = buildEdges(baseNode, rangeNode, iterateNode);
+
+ // Assemble!
+ const graph = {
+ nodes: {
+ [rangeNode.id]: rangeNode,
+ [iterateNode.id]: iterateNode,
+ [baseNode.id]: baseNode,
+ },
+ edges,
+ };
+
+ // TODO: hires fix requires latent space upscaling; we don't have nodes for this yet
+
+ return graph;
+};
diff --git a/invokeai/frontend/web/src/features/nodes/util/nodesGraphBuilder/buildNodesGraph.ts b/invokeai/frontend/web/src/features/nodes/util/buildNodesGraph.ts
similarity index 100%
rename from invokeai/frontend/web/src/features/nodes/util/nodesGraphBuilder/buildNodesGraph.ts
rename to invokeai/frontend/web/src/features/nodes/util/buildNodesGraph.ts
diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea.tsx
index 19e8f372b2..ee7f4d242d 100644
--- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea.tsx
+++ b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea.tsx
@@ -1,29 +1,3 @@
-// import { RootState } from 'app/store/store';
-// import { useAppSelector } from 'app/store/storeHooks';
-// import InvokeWorkarea from 'features/ui/components/InvokeWorkarea';
-// import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
-// import UnifiedCanvasContentBeta from './UnifiedCanvasBeta/UnifiedCanvasContentBeta';
-// import UnifiedCanvasContent from './UnifiedCanvasContent';
-// import UnifiedCanvasParameters from './UnifiedCanvasParameters';
-
-// export default function UnifiedCanvasWorkarea() {
-// const shouldUseCanvasBetaLayout = useAppSelector(
-// (state: RootState) => state.ui.shouldUseCanvasBetaLayout
-// );
-
-// const activeTabName = useAppSelector(activeTabNameSelector);
-
-// return (
-// }>
-// {activeTabName === 'unifiedCanvas' &&
-// (shouldUseCanvasBetaLayout ? (
-//
-// ) : (
-//
-// ))}
-//
-// );
-// }
import { Box, Flex } from '@chakra-ui/react';
import { useAppSelector } from 'app/store/storeHooks';
import { memo } from 'react';
@@ -34,7 +8,6 @@ import ParametersSlide from '../../common/ParametersSlide';
import UnifiedCanvasParameters from './UnifiedCanvasParameters';
import UnifiedCanvasContentBeta from './UnifiedCanvasBeta/UnifiedCanvasContentBeta';
import UnifiedCanvasContent from './UnifiedCanvasContent';
-import { useGetCanvasNodeType } from 'features/canvas/hooks/useGetCanvasNodeType';
const CanvasWorkspace = () => {
const shouldPinParametersPanel = useAppSelector(
diff --git a/invokeai/frontend/web/src/services/thunks/session.ts b/invokeai/frontend/web/src/services/thunks/session.ts
index 903bb3a2de..c92b303ff7 100644
--- a/invokeai/frontend/web/src/services/thunks/session.ts
+++ b/invokeai/frontend/web/src/services/thunks/session.ts
@@ -1,11 +1,9 @@
import { createAppAsyncThunk } from 'app/store/storeUtils';
import { SessionsService } from 'services/api';
-import {
- buildCanvasGraph,
- buildLinearGraph as buildGenerateGraph,
-} from 'features/nodes/util/linearGraphBuilder/buildLinearGraph';
+import { buildLinearGraph as buildGenerateGraph } from 'features/nodes/util/buildLinearGraph';
+import { buildCanvasGraph } from 'features/nodes/util/buildCanvasGraph';
import { isAnyOf, isFulfilled } from '@reduxjs/toolkit';
-import { buildNodesGraph } from 'features/nodes/util/nodesGraphBuilder/buildNodesGraph';
+import { buildNodesGraph } from 'features/nodes/util/buildNodesGraph';
import { log } from 'app/logging/useLogger';
import { serializeError } from 'serialize-error';