From 26e23a43dcf73b4c1753e50198c8a151bd085032 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:36:54 +1000 Subject: [PATCH] tidy(ui): create/move store utils to separate file --- .../addCommitStagingAreaImageListener.ts | 2 +- .../listeners/imageDropped.ts | 2 +- .../listeners/imageUploaded.ts | 2 +- .../common/CanvasEntityMergeVisibleButton.tsx | 2 +- .../controlLayers/hooks/addLayerHooks.ts | 2 +- .../controlLayers/hooks/saveCanvasHooks.ts | 2 +- .../CanvasEntity/CanvasEntityFilterer.ts | 2 +- .../CanvasEntityObjectRenderer.ts | 2 +- .../konva/CanvasStagingAreaModule.ts | 3 +- .../controlLayers/store/canvasSlice.ts | 227 ++++++------------ .../src/features/controlLayers/store/types.ts | 46 ---- .../src/features/controlLayers/store/util.ts | 186 ++++++++++++++ .../ImageMenuItemSendToCanvas.tsx | 2 +- .../web/src/features/metadata/util/parsers.ts | 4 +- 14 files changed, 268 insertions(+), 216 deletions(-) create mode 100644 invokeai/frontend/web/src/features/controlLayers/store/util.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts index 1650dc45d2..c209574520 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts @@ -5,7 +5,7 @@ import { canvasReset, rasterLayerAdded } from 'features/controlLayers/store/canv import { stagingAreaImageAccepted, stagingAreaReset } from 'features/controlLayers/store/canvasStagingAreaSlice'; import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; import type { CanvasRasterLayerState } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject } from 'features/controlLayers/store/types'; +import { imageDTOToImageObject } from 'features/controlLayers/store/util'; import { toast } from 'features/toast/toast'; import { t } from 'i18next'; import { queueApi } from 'services/api/endpoints/queue'; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts index 121373fe9f..bd369d36a3 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts @@ -12,7 +12,7 @@ import { } from 'features/controlLayers/store/canvasSlice'; import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; import type { CanvasControlLayerState, CanvasRasterLayerState } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject } from 'features/controlLayers/store/types'; +import { imageDTOToImageObject } from 'features/controlLayers/store/util'; import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types'; import { isValidDrop } from 'features/dnd/util/isValidDrop'; import { imageToCompareChanged, selectionChanged } from 'features/gallery/store/gallerySlice'; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index fe46736068..8b8639c8bf 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -7,7 +7,7 @@ import { rgIPAdapterImageChanged, } from 'features/controlLayers/store/canvasSlice'; import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; -import { imageDTOToImageObject } from 'features/controlLayers/store/types'; +import { imageDTOToImageObject } from 'features/controlLayers/store/util'; import { selectListBoardsQueryArgs } from 'features/gallery/store/gallerySelectors'; import { boardIdSelected, galleryViewChanged } from 'features/gallery/store/gallerySlice'; import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMergeVisibleButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMergeVisibleButton.tsx index d30da83c81..7528f2c4a8 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMergeVisibleButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityMergeVisibleButton.tsx @@ -7,7 +7,7 @@ import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { useEntityTypeCount } from 'features/controlLayers/hooks/useEntityTypeCount'; import { inpaintMaskAdded, rasterLayerAdded } from 'features/controlLayers/store/canvasSlice'; import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject } from 'features/controlLayers/store/types'; +import { imageDTOToImageObject } from 'features/controlLayers/store/util'; import { toast } from 'features/toast/toast'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts index 547a7936f4..2fab6d5cb9 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/addLayerHooks.ts @@ -20,7 +20,7 @@ import type { IPAdapterConfig, T2IAdapterConfig, } from 'features/controlLayers/store/types'; -import { initialControlNet, initialIPAdapter, initialT2IAdapter } from 'features/controlLayers/store/types'; +import { initialControlNet, initialIPAdapter, initialT2IAdapter } from 'features/controlLayers/store/util'; import { zModelIdentifierField } from 'features/nodes/types/common'; import { useCallback } from 'react'; import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models'; diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/saveCanvasHooks.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/saveCanvasHooks.ts index 2d8ff1c664..f63a0d7cdd 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/saveCanvasHooks.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/saveCanvasHooks.ts @@ -23,7 +23,7 @@ import type { Rect, RegionalGuidanceReferenceImageState, } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject, imageDTOToImageWithDims } from 'features/controlLayers/store/types'; +import { imageDTOToImageObject, imageDTOToImageWithDims } from 'features/controlLayers/store/util'; import { toast } from 'features/toast/toast'; import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer.ts index 9fc12a6d79..5b66a7c332 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer.ts @@ -9,7 +9,7 @@ import { selectAutoProcessFilter } from 'features/controlLayers/store/canvasSett import type { FilterConfig } from 'features/controlLayers/store/filters'; import { getFilterForModel, IMAGE_FILTERS } from 'features/controlLayers/store/filters'; import type { CanvasImageState } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject } from 'features/controlLayers/store/types'; +import { imageDTOToImageObject } from 'features/controlLayers/store/util'; import { debounce } from 'lodash-es'; import { atom } from 'nanostores'; import type { Logger } from 'roarr'; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts index 1adedf9411..bbad07f5e8 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer.ts @@ -19,7 +19,7 @@ import { previewBlob, } from 'features/controlLayers/konva/util'; import type { Rect } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject } from 'features/controlLayers/store/types'; +import { imageDTOToImageObject } from 'features/controlLayers/store/util'; import Konva from 'konva'; import type { GroupConfig } from 'konva/lib/Group'; import { debounce } from 'lodash-es'; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts index 41a0b8a5dc..1486ae8018 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStagingAreaModule.ts @@ -7,7 +7,8 @@ import { selectCanvasStagingAreaSlice, stagingAreaStartedStaging, } from 'features/controlLayers/store/canvasStagingAreaSlice'; -import { imageDTOToImageWithDims, type StagingAreaImage } from 'features/controlLayers/store/types'; +import type { StagingAreaImage } from 'features/controlLayers/store/types'; +import { imageDTOToImageWithDims } from 'features/controlLayers/store/util'; import Konva from 'konva'; import { atom } from 'nanostores'; import type { Logger } from 'roarr'; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts index a8bd9c5a8d..8f0dd324ec 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts @@ -27,7 +27,7 @@ import { ASPECT_RATIO_MAP, initialAspectRatioState } from 'features/parameters/c import type { AspectRatioID } from 'features/parameters/components/Bbox/types'; import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension'; import type { IRect } from 'konva/lib/types'; -import { isEqual, merge, omit } from 'lodash-es'; +import { merge, omit } from 'lodash-es'; import { atom } from 'nanostores'; import type { UndoableOptions } from 'redux-undo'; import type { @@ -59,84 +59,57 @@ import type { IPMethodV2, T2IAdapterConfig, } from './types'; +import { getEntityIdentifier, isRenderableEntity } from './types'; import { - getEntityIdentifier, + getControlLayerState, + getInpaintMaskState, + getRasterLayerState, + getReferenceImageState, + getRegionalGuidanceState, imageDTOToImageWithDims, initialControlNet, initialIPAdapter, - isRenderableEntity, -} from './types'; +} from './util'; -const DEFAULT_MASK_COLORS: RgbColor[] = [ - { r: 121, g: 157, b: 219 }, // rgb(121, 157, 219) - { r: 131, g: 214, b: 131 }, // rgb(131, 214, 131) - { r: 250, g: 225, b: 80 }, // rgb(250, 225, 80) - { r: 220, g: 144, b: 101 }, // rgb(220, 144, 101) - { r: 224, g: 117, b: 117 }, // rgb(224, 117, 117) - { r: 213, g: 139, b: 202 }, // rgb(213, 139, 202) - { r: 161, g: 120, b: 214 }, // rgb(161, 120, 214) -]; - -const getRGMaskFill = (state: CanvasState): RgbColor => { - const lastFill = state.regions.entities.slice(-1)[0]?.fill.color; - let i = DEFAULT_MASK_COLORS.findIndex((c) => isEqual(c, lastFill)); - if (i === -1) { - i = 0; - } - i = (i + 1) % DEFAULT_MASK_COLORS.length; - const fill = DEFAULT_MASK_COLORS[i]; - assert(fill, 'This should never happen'); - return fill; -}; - -const initialInpaintMaskState: CanvasInpaintMaskState = { - id: getPrefixedId('inpaint_mask'), - name: null, - type: 'inpaint_mask', - isEnabled: true, - isLocked: false, - objects: [], - opacity: 1, - position: { x: 0, y: 0 }, - fill: { - style: 'diagonal', - color: { r: 255, g: 122, b: 0 }, // some orange color - }, -}; - -const initialState: CanvasState = { - _version: 3, - selectedEntityIdentifier: getEntityIdentifier(initialInpaintMaskState), - bookmarkedEntityIdentifier: getEntityIdentifier(initialInpaintMaskState), - rasterLayers: { - isHidden: false, - entities: [], - }, - controlLayers: { - isHidden: false, - entities: [], - }, - inpaintMasks: { - isHidden: false, - entities: [deepClone(initialInpaintMaskState)], - }, - regions: { - isHidden: false, - entities: [], - }, - referenceImages: { entities: [] }, - bbox: { - rect: { x: 0, y: 0, width: 512, height: 512 }, - optimalDimension: 512, - aspectRatio: deepClone(initialAspectRatioState), - scaleMethod: 'auto', - scaledSize: { - width: 512, - height: 512, +const getInitialState = (): CanvasState => { + const initialInpaintMaskState = getInpaintMaskState(getPrefixedId('inpaint_mask')); + const initialState: CanvasState = { + _version: 3, + selectedEntityIdentifier: getEntityIdentifier(initialInpaintMaskState), + bookmarkedEntityIdentifier: getEntityIdentifier(initialInpaintMaskState), + rasterLayers: { + isHidden: false, + entities: [], }, - }, + controlLayers: { + isHidden: false, + entities: [], + }, + inpaintMasks: { + isHidden: false, + entities: [initialInpaintMaskState], + }, + regions: { + isHidden: false, + entities: [], + }, + referenceImages: { entities: [] }, + bbox: { + rect: { x: 0, y: 0, width: 512, height: 512 }, + optimalDimension: 512, + aspectRatio: deepClone(initialAspectRatioState), + scaleMethod: 'auto', + scaledSize: { + width: 512, + height: 512, + }, + }, + }; + return initialState; }; +const initialState = getInitialState(); + export const canvasSlice = createSlice({ name: 'canvas', initialState, @@ -154,27 +127,17 @@ export const canvasSlice = createSlice({ }> ) => { const { id, overrides, isSelected, isMergingVisible } = action.payload; - const entity: CanvasRasterLayerState = { - id, - name: null, - type: 'raster_layer', - isEnabled: true, - isLocked: false, - objects: [], - opacity: 1, - position: { x: 0, y: 0 }, - }; - merge(entity, overrides); + const entityState = getRasterLayerState(id, overrides); if (isMergingVisible) { // When merging visible, we delete all disabled layers state.rasterLayers.entities = state.rasterLayers.entities.filter((layer) => !layer.isEnabled); } - state.rasterLayers.entities.push(entity); + state.rasterLayers.entities.push(entityState); if (isSelected) { - state.selectedEntityIdentifier = getEntityIdentifier(entity); + state.selectedEntityIdentifier = getEntityIdentifier(entityState); } }, prepare: (payload: { @@ -235,22 +198,13 @@ export const canvasSlice = createSlice({ action: PayloadAction<{ id: string; overrides?: Partial; isSelected?: boolean }> ) => { const { id, overrides, isSelected } = action.payload; - const entity: CanvasControlLayerState = { - id, - name: null, - type: 'control_layer', - isEnabled: true, - isLocked: false, - withTransparencyEffect: true, - objects: [], - opacity: 1, - position: { x: 0, y: 0 }, - controlAdapter: deepClone(initialControlNet), - }; - merge(entity, overrides); - state.controlLayers.entities.push(entity); + + const entityState = getControlLayerState(id, overrides); + + state.controlLayers.entities.push(entityState); + if (isSelected) { - state.selectedEntityIdentifier = getEntityIdentifier(entity); + state.selectedEntityIdentifier = getEntityIdentifier(entityState); } }, prepare: (payload: { overrides?: Partial; isSelected?: boolean }) => ({ @@ -378,18 +332,12 @@ export const canvasSlice = createSlice({ action: PayloadAction<{ id: string; overrides?: Partial; isSelected?: boolean }> ) => { const { id, overrides, isSelected } = action.payload; - const entity: CanvasReferenceImageState = { - id, - type: 'reference_image', - name: null, - isLocked: false, - isEnabled: true, - ipAdapter: deepClone(initialIPAdapter), - }; - merge(entity, overrides); - state.referenceImages.entities.push(entity); + const entityState = getReferenceImageState(id, overrides); + + state.referenceImages.entities.push(entityState); + if (isSelected) { - state.selectedEntityIdentifier = getEntityIdentifier(entity); + state.selectedEntityIdentifier = getEntityIdentifier(entityState); } }, prepare: (payload?: { overrides?: Partial; isSelected?: boolean }) => ({ @@ -474,28 +422,13 @@ export const canvasSlice = createSlice({ action: PayloadAction<{ id: string; overrides?: Partial; isSelected?: boolean }> ) => { const { id, overrides, isSelected } = action.payload; - const entity: CanvasRegionalGuidanceState = { - id, - name: null, - isLocked: false, - type: 'regional_guidance', - isEnabled: true, - objects: [], - fill: { - style: 'solid', - color: getRGMaskFill(state), - }, - opacity: 0.5, - position: { x: 0, y: 0 }, - autoNegative: false, - positivePrompt: null, - negativePrompt: null, - referenceImages: [], - }; - merge(entity, overrides); - state.regions.entities.push(entity); + + const entityState = getRegionalGuidanceState(id, overrides); + + state.regions.entities.push(entityState); + if (isSelected) { - state.selectedEntityIdentifier = getEntityIdentifier(entity); + state.selectedEntityIdentifier = getEntityIdentifier(entityState); } }, prepare: (payload?: { overrides?: Partial; isSelected?: boolean }) => ({ @@ -670,31 +603,18 @@ export const canvasSlice = createSlice({ }> ) => { const { id, overrides, isSelected, isMergingVisible } = action.payload; - const entity: CanvasInpaintMaskState = { - id, - name: null, - type: 'inpaint_mask', - isEnabled: true, - isLocked: false, - objects: [], - opacity: 1, - position: { x: 0, y: 0 }, - fill: { - style: 'diagonal', - color: { r: 255, g: 122, b: 0 }, // some orange color - }, - }; - merge(entity, overrides); + + const entityState = getInpaintMaskState(id, overrides); if (isMergingVisible) { // When merging visible, we delete all disabled layers state.inpaintMasks.entities = state.inpaintMasks.entities.filter((layer) => !layer.isEnabled); } - state.inpaintMasks.entities.push(entity); + state.inpaintMasks.entities.push(entityState); if (isSelected) { - state.selectedEntityIdentifier = getEntityIdentifier(entity); + state.selectedEntityIdentifier = getEntityIdentifier(entityState); } }, prepare: (payload?: { @@ -1117,17 +1037,8 @@ export const canvasSlice = createSlice({ break; } }, - allEntitiesDeleted: (state) => { - state.referenceImages = deepClone(initialState.referenceImages); - state.rasterLayers = deepClone(initialState.rasterLayers); - state.controlLayers = deepClone(initialState.controlLayers); - state.regions = deepClone(initialState.regions); - state.inpaintMasks = deepClone(initialState.inpaintMasks); - - state.selectedEntityIdentifier = deepClone(initialState.selectedEntityIdentifier); - }, canvasReset: (state) => { - const newState = deepClone(initialState); + const newState = getInitialState(); // We need to retain the optimal dimension across resets, as it is changed only when the model changes. Copy it // from the old state, then recalculate the bbox size & scaled size. diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index deb29b37af..ac867806e5 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -1,5 +1,4 @@ import type { SerializableObject } from 'common/types'; -import { getPrefixedId } from 'features/controlLayers/konva/util'; import { zModelIdentifierField } from 'features/nodes/types/common'; import type { AspectRatioState } from 'features/parameters/components/Bbox/types'; import type { ParameterHeight, ParameterLoRAModel, ParameterWidth } from 'features/parameters/types/parameterSchemas'; @@ -212,51 +211,6 @@ const zCanvasControlLayerState = zCanvasRasterLayerState.extend({ }); export type CanvasControlLayerState = z.infer; -export const initialControlNet: ControlNetConfig = { - type: 'controlnet', - model: null, - weight: 1, - beginEndStepPct: [0, 1], - controlMode: 'balanced', -}; - -export const initialT2IAdapter: T2IAdapterConfig = { - type: 't2i_adapter', - model: null, - weight: 1, - beginEndStepPct: [0, 1], -}; - -export const initialIPAdapter: IPAdapterConfig = { - type: 'ip_adapter', - image: null, - model: null, - beginEndStepPct: [0, 1], - method: 'full', - clipVisionModel: 'ViT-H', - weight: 1, -}; - -export const imageDTOToImageWithDims = ({ image_name, width, height }: ImageDTO): ImageWithDims => ({ - image_name, - width, - height, -}); - -export const imageDTOToImageObject = (imageDTO: ImageDTO, overrides?: Partial): CanvasImageState => { - const { width, height, image_name } = imageDTO; - return { - id: getPrefixedId('image'), - type: 'image', - image: { - image_name, - width, - height, - }, - ...overrides, - }; -}; - const zBoundingBoxScaleMethod = z.enum(['none', 'auto', 'manual']); export type BoundingBoxScaleMethod = z.infer; export const isBoundingBoxScaleMethod = (v: unknown): v is BoundingBoxScaleMethod => diff --git a/invokeai/frontend/web/src/features/controlLayers/store/util.ts b/invokeai/frontend/web/src/features/controlLayers/store/util.ts new file mode 100644 index 0000000000..f799bea41c --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/store/util.ts @@ -0,0 +1,186 @@ +import { deepClone } from 'common/util/deepClone'; +import { getPrefixedId } from 'features/controlLayers/konva/util'; +import type { + CanvasControlLayerState, + CanvasImageState, + CanvasInpaintMaskState, + CanvasRasterLayerState, + CanvasReferenceImageState, + CanvasRegionalGuidanceState, + ControlNetConfig, + ImageWithDims, + IPAdapterConfig, + RgbColor, + T2IAdapterConfig, +} from 'features/controlLayers/store/types'; +import { merge } from 'lodash-es'; +import type { ImageDTO } from 'services/api/types'; +import { assert } from 'tsafe'; + +export const imageDTOToImageObject = (imageDTO: ImageDTO, overrides?: Partial): CanvasImageState => { + const { width, height, image_name } = imageDTO; + return { + id: getPrefixedId('image'), + type: 'image', + image: { + image_name, + width, + height, + }, + ...overrides, + }; +}; + +export const imageDTOToImageWithDims = ({ image_name, width, height }: ImageDTO): ImageWithDims => ({ + image_name, + width, + height, +}); + +const DEFAULT_RG_MASK_FILL_COLORS: RgbColor[] = [ + { r: 121, g: 157, b: 219 }, // rgb(121, 157, 219) + { r: 131, g: 214, b: 131 }, // rgb(131, 214, 131) + { r: 250, g: 225, b: 80 }, // rgb(250, 225, 80) + { r: 220, g: 144, b: 101 }, // rgb(220, 144, 101) + { r: 224, g: 117, b: 117 }, // rgb(224, 117, 117) + { r: 213, g: 139, b: 202 }, // rgb(213, 139, 202) + { r: 161, g: 120, b: 214 }, // rgb(161, 120, 214) +]; +const buildMaskFillCycler = (initialIndex: number): (() => RgbColor) => { + let lastFillIndex = initialIndex; + + return () => { + lastFillIndex = (lastFillIndex + 1) % DEFAULT_RG_MASK_FILL_COLORS.length; + const fill = DEFAULT_RG_MASK_FILL_COLORS[lastFillIndex]; + assert(fill, 'This should never happen'); + return fill; + }; +}; + +const getInpaintMaskFillColor = buildMaskFillCycler(3); +const getRegionalGuidanceMaskFillColor = buildMaskFillCycler(0); + +export const initialIPAdapter: IPAdapterConfig = { + type: 'ip_adapter', + image: null, + model: null, + beginEndStepPct: [0, 1], + method: 'full', + clipVisionModel: 'ViT-H', + weight: 1, +}; +export const initialT2IAdapter: T2IAdapterConfig = { + type: 't2i_adapter', + model: null, + weight: 1, + beginEndStepPct: [0, 1], +}; +export const initialControlNet: ControlNetConfig = { + type: 'controlnet', + model: null, + weight: 1, + beginEndStepPct: [0, 1], + controlMode: 'balanced', +}; + +export const getReferenceImageState = ( + id: string, + overrides?: Partial +): CanvasReferenceImageState => { + const entityState: CanvasReferenceImageState = { + id, + type: 'reference_image', + name: null, + isLocked: false, + isEnabled: true, + ipAdapter: deepClone(initialIPAdapter), + }; + merge(entityState, overrides); + return entityState; +}; + +export const getRegionalGuidanceState = ( + id: string, + overrides?: Partial +): CanvasRegionalGuidanceState => { + const entityState: CanvasRegionalGuidanceState = { + id, + name: null, + isLocked: false, + type: 'regional_guidance', + isEnabled: true, + objects: [], + fill: { + style: 'solid', + color: getRegionalGuidanceMaskFillColor(), + }, + opacity: 0.5, + position: { x: 0, y: 0 }, + autoNegative: false, + positivePrompt: null, + negativePrompt: null, + referenceImages: [], + }; + merge(entityState, overrides); + return entityState; +}; + +export const getControlLayerState = ( + id: string, + overrides?: Partial +): CanvasControlLayerState => { + const entityState: CanvasControlLayerState = { + id, + name: null, + type: 'control_layer', + isEnabled: true, + isLocked: false, + withTransparencyEffect: true, + objects: [], + opacity: 1, + position: { x: 0, y: 0 }, + controlAdapter: deepClone(initialControlNet), + }; + merge(entityState, overrides); + return entityState; +}; + +export const getRasterLayerState = ( + id: string, + overrides?: Partial +): CanvasRasterLayerState => { + const entityState: CanvasRasterLayerState = { + id, + name: null, + type: 'raster_layer', + isEnabled: true, + isLocked: false, + objects: [], + opacity: 1, + position: { x: 0, y: 0 }, + }; + merge(entityState, overrides); + return entityState; +}; + +export const getInpaintMaskState = ( + id: string, + overrides?: Partial +): CanvasInpaintMaskState => { + const entityState: CanvasInpaintMaskState = { + id, + name: null, + type: 'inpaint_mask', + isEnabled: true, + isLocked: false, + objects: [], + opacity: 1, + position: { x: 0, y: 0 }, + fill: { + style: 'diagonal', + color: getInpaintMaskFillColor(), + }, + }; + merge(entityState, overrides); + return entityState; +}; diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemSendToCanvas.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemSendToCanvas.tsx index 459d68da34..3232b1f3ec 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemSendToCanvas.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemSendToCanvas.tsx @@ -4,7 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { rasterLayerAdded } from 'features/controlLayers/store/canvasSlice'; import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; import type { CanvasRasterLayerState } from 'features/controlLayers/store/types'; -import { imageDTOToImageObject } from 'features/controlLayers/store/types'; +import { imageDTOToImageObject } from 'features/controlLayers/store/util'; import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer'; import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext'; import { sentImageToCanvas } from 'features/gallery/store/actions'; diff --git a/invokeai/frontend/web/src/features/metadata/util/parsers.ts b/invokeai/frontend/web/src/features/metadata/util/parsers.ts index a4d60e4ec9..998ef41095 100644 --- a/invokeai/frontend/web/src/features/metadata/util/parsers.ts +++ b/invokeai/frontend/web/src/features/metadata/util/parsers.ts @@ -8,13 +8,13 @@ import type { CanvasRegionalGuidanceState, LoRA, } from 'features/controlLayers/store/types'; +import { zCanvasRasterLayerState } from 'features/controlLayers/store/types'; import { imageDTOToImageWithDims, initialControlNet, initialIPAdapter, initialT2IAdapter, - zCanvasRasterLayerState, -} from 'features/controlLayers/store/types'; +} from 'features/controlLayers/store/util'; import type { ControlNetConfigMetadata, IPAdapterConfigMetadata,