From 9945c20d02a9d0d8465b86927467b4cd0e65dbd1 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 7 Jul 2025 17:14:35 +1000 Subject: [PATCH] refactor(ui): simplifiy graph builders (WIP) --- .../hooks/useDynamicPromptsWatcher.tsx | 10 ++-- .../util/graph/generation/addFLUXFill.ts | 18 +++--- .../util/graph/generation/addImageToImage.ts | 16 +++--- .../nodes/util/graph/generation/addInpaint.ts | 15 ++--- .../util/graph/generation/addOutpaint.ts | 18 ++---- .../util/graph/generation/addTextToImage.ts | 29 ++++++++-- .../graph/generation/buildChatGPT4oGraph.ts | 29 +++++++--- .../graph/generation/buildCogView4Graph.ts | 52 ++++++++--------- .../util/graph/generation/buildFLUXGraph.ts | 45 +++++++-------- .../graph/generation/buildFluxKontextGraph.ts | 20 ++++--- .../graph/generation/buildImagen3Graph.ts | 21 +++---- .../graph/generation/buildImagen4Graph.ts | 22 ++++---- .../util/graph/generation/buildSD1Graph.ts | 51 ++++++++--------- .../util/graph/generation/buildSD3Graph.ts | 49 +++++++--------- .../util/graph/generation/buildSDXLGraph.ts | 51 ++++++++--------- .../nodes/util/graph/graphBuilderUtils.ts | 56 ++++++++++++------- 16 files changed, 250 insertions(+), 252 deletions(-) diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/hooks/useDynamicPromptsWatcher.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/hooks/useDynamicPromptsWatcher.tsx index 97b3aa16aa..011ca414f0 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/hooks/useDynamicPromptsWatcher.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/hooks/useDynamicPromptsWatcher.tsx @@ -59,13 +59,11 @@ export const useDynamicPromptsWatcher = () => { return; } - const { positivePrompt } = presetModifiedPrompts; - // Before we execute, imperatively check the dynamic prompts query cache to see if we have already fetched this prompt const state = getState(); const cachedPrompts = utilitiesApi.endpoints.dynamicPrompts.select({ - prompt: positivePrompt, + prompt: presetModifiedPrompts.positive, max_prompts: maxPrompts, })(state).data; @@ -77,8 +75,8 @@ export const useDynamicPromptsWatcher = () => { } // If the prompt is not in the cache, check if we should process it - this is just looking for dynamic prompts syntax - if (!getShouldProcessPrompt(positivePrompt)) { - dispatch(promptsChanged([positivePrompt])); + if (!getShouldProcessPrompt(presetModifiedPrompts.positive)) { + dispatch(promptsChanged([presetModifiedPrompts.positive])); dispatch(parsingErrorChanged(undefined)); dispatch(isErrorChanged(false)); return; @@ -89,6 +87,6 @@ export const useDynamicPromptsWatcher = () => { dispatch(isLoadingChanged(true)); } - debouncedUpdateDynamicPrompts(positivePrompt, maxPrompts); + debouncedUpdateDynamicPrompts(presetModifiedPrompts.positive, maxPrompts); }, [debouncedUpdateDynamicPrompts, dispatch, dynamicPrompting, getState, maxPrompts, presetModifiedPrompts]); }; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts index 282a0568c2..20833ff08c 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts @@ -4,10 +4,11 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; -import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; -import type { Dimensions } from 'features/controlLayers/store/types'; import type { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { getDenoisingStartAndEnd } from 'features/nodes/util/graph/graphBuilderUtils'; +import { + getDenoisingStartAndEnd, + getOriginalAndScaledSizesForOtherModes, +} from 'features/nodes/util/graph/graphBuilderUtils'; import type { Invocation } from 'services/api/types'; type AddFLUXFillArg = { @@ -16,8 +17,6 @@ type AddFLUXFillArg = { manager: CanvasManager; l2i: Invocation<'flux_vae_decode'>; denoise: Invocation<'flux_denoise'>; - originalSize: Dimensions; - scaledSize: Dimensions; }; export const addFLUXFill = async ({ @@ -26,8 +25,6 @@ export const addFLUXFill = async ({ manager, l2i, denoise, - originalSize, - scaledSize, }: AddFLUXFillArg): Promise> => { const { denoising_start, denoising_end } = getDenoisingStartAndEnd(state); denoise.denoising_start = denoising_start; @@ -35,18 +32,17 @@ export const addFLUXFill = async ({ const params = selectParamsSlice(state); const canvasSettings = selectCanvasSettingsSlice(state); - const canvas = selectCanvasSlice(state); - const { bbox } = canvas; + const { originalSize, scaledSize, rect } = getOriginalAndScaledSizesForOtherModes(state); const rasterAdapters = manager.compositor.getVisibleAdaptersOfType('raster_layer'); - const initialImage = await manager.compositor.getCompositeImageDTO(rasterAdapters, bbox.rect, { + const initialImage = await manager.compositor.getCompositeImageDTO(rasterAdapters, rect, { is_intermediate: true, silent: true, }); const inpaintMaskAdapters = manager.compositor.getVisibleAdaptersOfType('inpaint_mask'); - const maskImage = await manager.compositor.getCompositeImageDTO(inpaintMaskAdapters, bbox.rect, { + const maskImage = await manager.compositor.getCompositeImageDTO(inpaintMaskAdapters, rect, { is_intermediate: true, silent: true, }); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts index 6ebdfc2cae..27be363a1d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addImageToImage.ts @@ -2,9 +2,11 @@ import { objectEquals } from '@observ33r/object-equals'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import type { CanvasState, Dimensions } from 'features/controlLayers/store/types'; import type { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { getDenoisingStartAndEnd } from 'features/nodes/util/graph/graphBuilderUtils'; +import { + getDenoisingStartAndEnd, + getOriginalAndScaledSizesForOtherModes, +} from 'features/nodes/util/graph/graphBuilderUtils'; import type { DenoiseLatentsNodes, LatentToImageNodes, @@ -21,9 +23,6 @@ type AddImageToImageArg = { i2l: Invocation<'i2l' | 'flux_vae_encode' | 'sd3_i2l' | 'cogview4_i2l'>; denoise: Invocation; vaeSource: Invocation; - originalSize: Dimensions; - scaledSize: Dimensions; - bbox: CanvasState['bbox']; }; export const addImageToImage = async ({ @@ -34,16 +33,15 @@ export const addImageToImage = async ({ i2l, denoise, vaeSource, - originalSize, - scaledSize, - bbox, }: AddImageToImageArg): Promise> => { const { denoising_start, denoising_end } = getDenoisingStartAndEnd(state); denoise.denoising_start = denoising_start; denoise.denoising_end = denoising_end; + const { originalSize, scaledSize, rect } = getOriginalAndScaledSizesForOtherModes(state); + const adapters = manager.compositor.getVisibleAdaptersOfType('raster_layer'); - const { image_name } = await manager.compositor.getCompositeImageDTO(adapters, bbox.rect, { + const { image_name } = await manager.compositor.getCompositeImageDTO(adapters, rect, { is_intermediate: true, silent: true, }); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts index d712b12993..6d33fe77c2 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts @@ -4,10 +4,12 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; -import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; -import type { Dimensions } from 'features/controlLayers/store/types'; import type { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { getDenoisingStartAndEnd, isMainModelWithoutUnet } from 'features/nodes/util/graph/graphBuilderUtils'; +import { + getDenoisingStartAndEnd, + getOriginalAndScaledSizesForOtherModes, + isMainModelWithoutUnet, +} from 'features/nodes/util/graph/graphBuilderUtils'; import type { DenoiseLatentsNodes, LatentToImageNodes, @@ -25,8 +27,6 @@ type AddInpaintArg = { denoise: Invocation; vaeSource: Invocation; modelLoader: Invocation; - originalSize: Dimensions; - scaledSize: Dimensions; seed: Invocation<'integer'>; }; @@ -39,8 +39,6 @@ export const addInpaint = async ({ denoise, vaeSource, modelLoader, - originalSize, - scaledSize, seed, }: AddInpaintArg): Promise> => { const { denoising_start, denoising_end } = getDenoisingStartAndEnd(state); @@ -49,9 +47,8 @@ export const addInpaint = async ({ const params = selectParamsSlice(state); const canvasSettings = selectCanvasSettingsSlice(state); - const canvas = selectCanvasSlice(state); - const { rect } = canvas.bbox; + const { originalSize, scaledSize, rect } = getOriginalAndScaledSizesForOtherModes(state); const rasterAdapters = manager.compositor.getVisibleAdaptersOfType('raster_layer'); const initialImage = await manager.compositor.getCompositeImageDTO(rasterAdapters, rect, { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts index 15a19f582d..eee5c243f3 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts @@ -4,12 +4,11 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; -import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; -import type { Dimensions } from 'features/controlLayers/store/types'; import type { Graph } from 'features/nodes/util/graph/generation/Graph'; import { getDenoisingStartAndEnd, getInfill, + getOriginalAndScaledSizesForOtherModes, isMainModelWithoutUnet, } from 'features/nodes/util/graph/graphBuilderUtils'; import type { @@ -22,30 +21,26 @@ import type { import type { ImageDTO, Invocation } from 'services/api/types'; type AddOutpaintArg = { - state: RootState; g: Graph; + state: RootState; manager: CanvasManager; l2i: Invocation; i2l: Invocation; denoise: Invocation; vaeSource: Invocation; modelLoader: Invocation; - originalSize: Dimensions; - scaledSize: Dimensions; seed: Invocation<'integer'>; }; export const addOutpaint = async ({ - state, g, + state, manager, l2i, i2l, denoise, vaeSource, modelLoader, - originalSize, - scaledSize, seed, }: AddOutpaintArg): Promise> => { const { denoising_start, denoising_end } = getDenoisingStartAndEnd(state); @@ -54,20 +49,17 @@ export const addOutpaint = async ({ const params = selectParamsSlice(state); const canvasSettings = selectCanvasSettingsSlice(state); - const canvas = selectCanvasSlice(state); - const { bbox } = canvas; + const { originalSize, scaledSize, rect } = getOriginalAndScaledSizesForOtherModes(state); const rasterAdapters = manager.compositor.getVisibleAdaptersOfType('raster_layer'); - const initialImage = await manager.compositor.getCompositeImageDTO(rasterAdapters, bbox.rect, { + const initialImage = await manager.compositor.getCompositeImageDTO(rasterAdapters, rect, { is_intermediate: true, silent: true, }); const inpaintMaskAdapters = manager.compositor.getVisibleAdaptersOfType('inpaint_mask'); - const { rect } = canvas.bbox; - // Get inpaint mask adapters that have noise settings const noiseMaskAdapters = inpaintMaskAdapters.filter((adapter) => adapter.state.noiseLevel !== undefined); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts index 436285fc9d..c9844b51eb 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addTextToImage.ts @@ -1,28 +1,47 @@ import { objectEquals } from '@observ33r/object-equals'; +import type { RootState } from 'app/store/store'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import type { Dimensions } from 'features/controlLayers/store/types'; import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { getOriginalAndScaledSizesForTextToImage } from 'features/nodes/util/graph/graphBuilderUtils'; import type { DenoiseLatentsNodes, LatentToImageNodes } from 'features/nodes/util/graph/types'; import type { Invocation } from 'services/api/types'; +import { assert } from 'tsafe'; type AddTextToImageArg = { g: Graph; + state: RootState; + noise?: Invocation<'noise'>; denoise: Invocation; l2i: Invocation; - originalSize: Dimensions; - scaledSize: Dimensions; }; export const addTextToImage = ({ g, + state, + noise, denoise, l2i, - originalSize, - scaledSize, }: AddTextToImageArg): Invocation<'img_resize' | 'l2i' | 'flux_vae_decode' | 'sd3_l2i' | 'cogview4_l2i'> => { denoise.denoising_start = 0; denoise.denoising_end = 1; + const { originalSize, scaledSize } = getOriginalAndScaledSizesForTextToImage(state); + + if (denoise.type === 'cogview4_denoise' || denoise.type === 'flux_denoise' || denoise.type === 'sd3_denoise') { + denoise.width = scaledSize.width; + denoise.height = scaledSize.height; + } else { + assert(denoise.type === 'denoise_latents'); + assert(noise, 'SD1.5/SD2/SDXL graphs require a noise node to be passed in'); + noise.width = scaledSize.width; + noise.height = scaledSize.height; + } + + g.upsertMetadata({ + width: originalSize.width, + height: originalSize.height, + }); + if (!objectEquals(scaledSize, originalSize)) { // We need to resize the output image back to the original size const resizeImageToOriginalSize = g.addNode({ diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts index 4770a77df1..86a9efb244 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts @@ -2,13 +2,19 @@ import { logger } from 'app/logging/logger'; import { getPrefixedId } from 'features/controlLayers/konva/util'; import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice'; import { selectRefImagesSlice } from 'features/controlLayers/store/refImagesSlice'; +import { selectCanvasMetadata } from 'features/controlLayers/store/selectors'; import { isChatGPT4oAspectRatioID, isChatGPT4oReferenceImageConfig } from 'features/controlLayers/store/types'; import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/validators'; import { type ImageField, zModelIdentifierField } from 'features/nodes/types/common'; import { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { selectCanvasOutputFields, selectOriginalAndScaledSizes } from 'features/nodes/util/graph/graphBuilderUtils'; +import { + getOriginalAndScaledSizesForOtherModes, + getOriginalAndScaledSizesForTextToImage, + selectCanvasOutputFields, +} from 'features/nodes/util/graph/graphBuilderUtils'; import type { GraphBuilderArg, GraphBuilderReturn } from 'features/nodes/util/graph/types'; import { UnsupportedGenerationModeError } from 'features/nodes/util/graph/types'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { t } from 'i18next'; import type { Equals } from 'tsafe'; import { assert } from 'tsafe'; @@ -28,8 +34,6 @@ export const buildChatGPT4oGraph = async (arg: GraphBuilderArg): Promise entity.isEnabled) @@ -60,7 +62,7 @@ export const buildFluxKontextGraph = (arg: GraphBuilderArg): GraphBuilderReturn // @ts-expect-error: These nodes are not available in the OSS application type: input_image ? 'flux_kontext_edit_image' : 'flux_kontext_generate_image', model: zModelIdentifierField.parse(model), - aspect_ratio: bbox.aspectRatio.id, + aspect_ratio: aspectRatio.id, input_image, prompt_upsampling: true, ...selectCanvasOutputFields(state), @@ -76,8 +78,8 @@ export const buildFluxKontextGraph = (arg: GraphBuilderArg): GraphBuilderReturn g.addEdgeToMetadata(positivePrompt, 'value', 'positive_prompt'); g.upsertMetadata({ model: Graph.getModelMetadataField(model), - width: bbox.rect.width, - height: bbox.rect.height, + width: originalSize.width, + height: originalSize.height, }); return { g, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen3Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen3Graph.ts index a5268f22a7..18539a6cc3 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen3Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen3Graph.ts @@ -1,11 +1,14 @@ import { logger } from 'app/logging/logger'; import { getPrefixedId } from 'features/controlLayers/konva/util'; import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice'; -import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { isImagenAspectRatioID } from 'features/controlLayers/store/types'; import { zModelIdentifierField } from 'features/nodes/types/common'; import { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { selectCanvasOutputFields, selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import { + getOriginalAndScaledSizesForTextToImage, + selectCanvasOutputFields, + selectPresetModifiedPrompts, +} from 'features/nodes/util/graph/graphBuilderUtils'; import type { GraphBuilderArg, GraphBuilderReturn } from 'features/nodes/util/graph/types'; import { UnsupportedGenerationModeError } from 'features/nodes/util/graph/types'; import { t } from 'i18next'; @@ -26,13 +29,12 @@ export const buildImagen3Graph = (arg: GraphBuilderArg): GraphBuilderReturn => { throw new UnsupportedGenerationModeError(t('toast.imagenIncompatibleGenerationMode', { model: 'Imagen3' })); } - const canvas = selectCanvasSlice(state); - - const { bbox } = canvas; const prompts = selectPresetModifiedPrompts(state); - assert(isImagenAspectRatioID(bbox.aspectRatio.id), 'Imagen3 does not support this aspect ratio'); assert(prompts.positive.length > 0, 'Imagen3 requires positive prompt to have at least one character'); + const { originalSize, aspectRatio } = getOriginalAndScaledSizesForTextToImage(state); + assert(isImagenAspectRatioID(aspectRatio.id), 'Imagen3 does not support this aspect ratio'); + const g = new Graph(getPrefixedId('imagen3_txt2img_graph')); const positivePrompt = g.addNode({ id: getPrefixedId('positive_prompt'), @@ -43,7 +45,7 @@ export const buildImagen3Graph = (arg: GraphBuilderArg): GraphBuilderReturn => { type: 'google_imagen3_generate_image', model: zModelIdentifierField.parse(model), negative_prompt: prompts.negative, - aspect_ratio: bbox.aspectRatio.id, + aspect_ratio: aspectRatio.id, // When enhance_prompt is true, Imagen3 will return a new image every time, ignoring the seed. enhance_prompt: true, ...selectCanvasOutputFields(state), @@ -60,10 +62,9 @@ export const buildImagen3Graph = (arg: GraphBuilderArg): GraphBuilderReturn => { g.upsertMetadata({ negative_prompt: prompts.negative, - width: bbox.rect.width, - height: bbox.rect.height, + width: originalSize.width, + height: originalSize.height, model: Graph.getModelMetadataField(model), - ...selectCanvasMetadata(state), }); return { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen4Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen4Graph.ts index d2cf67934d..16baae50d1 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen4Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen4Graph.ts @@ -1,11 +1,14 @@ import { logger } from 'app/logging/logger'; import { getPrefixedId } from 'features/controlLayers/konva/util'; import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice'; -import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { isImagenAspectRatioID } from 'features/controlLayers/store/types'; import { zModelIdentifierField } from 'features/nodes/types/common'; import { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { selectCanvasOutputFields, selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import { + getOriginalAndScaledSizesForTextToImage, + selectCanvasOutputFields, + selectPresetModifiedPrompts, +} from 'features/nodes/util/graph/graphBuilderUtils'; import type { GraphBuilderArg, GraphBuilderReturn } from 'features/nodes/util/graph/types'; import { UnsupportedGenerationModeError } from 'features/nodes/util/graph/types'; import { t } from 'i18next'; @@ -25,14 +28,12 @@ export const buildImagen4Graph = (arg: GraphBuilderArg): GraphBuilderReturn => { throw new UnsupportedGenerationModeError(t('toast.imagenIncompatibleGenerationMode', { model: 'Imagen4' })); } - const canvas = selectCanvasSlice(state); - - const { bbox } = canvas; - const prompts = selectPresetModifiedPrompts(state); - assert(isImagenAspectRatioID(bbox.aspectRatio.id), 'Imagen4 does not support this aspect ratio'); assert(prompts.positive.length > 0, 'Imagen4 requires positive prompt to have at least one character'); + const { originalSize, aspectRatio } = getOriginalAndScaledSizesForTextToImage(state); + assert(isImagenAspectRatioID(aspectRatio.id), 'Imagen4 does not support this aspect ratio'); + const g = new Graph(getPrefixedId('imagen4_txt2img_graph')); const positivePrompt = g.addNode({ id: getPrefixedId('positive_prompt'), @@ -43,7 +44,7 @@ export const buildImagen4Graph = (arg: GraphBuilderArg): GraphBuilderReturn => { type: 'google_imagen4_generate_image', model: zModelIdentifierField.parse(model), negative_prompt: prompts.negative, - aspect_ratio: bbox.aspectRatio.id, + aspect_ratio: aspectRatio.id, // When enhance_prompt is true, Imagen4 will return a new image every time, ignoring the seed. enhance_prompt: true, ...selectCanvasOutputFields(state), @@ -60,10 +61,9 @@ export const buildImagen4Graph = (arg: GraphBuilderArg): GraphBuilderReturn => { g.upsertMetadata({ negative_prompt: prompts.negative, - width: bbox.rect.width, - height: bbox.rect.height, + width: originalSize.width, + height: originalSize.height, model: Graph.getModelMetadataField(model), - ...selectCanvasMetadata(state), }); return { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts index d0023b45f1..e40b6da8c6 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts @@ -15,12 +15,9 @@ import { addSeamless } from 'features/nodes/util/graph/generation/addSeamless'; import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage'; import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker'; import { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { - selectCanvasOutputFields, - selectOriginalAndScaledSizes, - selectPresetModifiedPrompts, -} from 'features/nodes/util/graph/graphBuilderUtils'; +import { selectCanvasOutputFields, selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import type { GraphBuilderArg, GraphBuilderReturn, ImageOutputNodes } from 'features/nodes/util/graph/types'; +import { selectActiveTab } from 'features/ui/store/uiSelectors'; import type { Invocation } from 'services/api/types'; import type { Equals } from 'tsafe'; import { assert } from 'tsafe'; @@ -42,8 +39,6 @@ export const buildSD1Graph = async (arg: GraphBuilderArg): Promise { - if (tab === 'generate') { - const { width, height } = params.dimensions.rect; - const { aspectRatio } = params.dimensions; - return { - originalSize: { width, height }, - scaledSize: { width, height }, - aspectRatio, - }; - } else { - // tab === 'canvas' - const { width, height } = canvas.bbox.rect; - const { aspectRatio } = canvas.bbox; - const originalSize = { width, height }; - const scaledSize = ['auto', 'manual'].includes(canvas.bbox.scaleMethod) ? canvas.bbox.scaledSize : originalSize; - return { originalSize, scaledSize, aspectRatio }; - } +export const getOriginalAndScaledSizesForTextToImage = (state: RootState) => { + const tab = selectActiveTab(state); + const params = selectParamsSlice(state); + const canvas = selectCanvasSlice(state); + + if (tab === 'canvas') { + const { rect, aspectRatio } = canvas.bbox; + const { width, height } = rect; + const originalSize = { width, height }; + const scaledSize = ['auto', 'manual'].includes(canvas.bbox.scaleMethod) ? canvas.bbox.scaledSize : originalSize; + return { originalSize, scaledSize, aspectRatio }; + } else if (tab === 'generate') { + const { rect, aspectRatio } = params.dimensions; + const { width, height } = rect; + return { + originalSize: { width, height }, + scaledSize: { width, height }, + aspectRatio, + }; } -); + + assert(false, `Cannot get sizes for tab ${tab} - this function is only for the Canvas or Generate tabs`); +}; + +export const getOriginalAndScaledSizesForOtherModes = (state: RootState) => { + const tab = selectActiveTab(state); + const canvas = selectCanvasSlice(state); + + assert(tab === 'canvas', `Cannot get sizes for tab ${tab} - this function is only for the Canvas tab`); + + const { rect, aspectRatio } = canvas.bbox; + const { width, height } = rect; + const originalSize = { width, height }; + const scaledSize = ['auto', 'manual'].includes(canvas.bbox.scaleMethod) ? canvas.bbox.scaledSize : originalSize; + + return { originalSize, scaledSize, aspectRatio, rect }; +}; export const getInfill = ( g: Graph,