mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into mm-ui
This commit is contained in:
commit
1c2144794c
@ -532,7 +532,7 @@
|
||||
"hidePreview": "Hide Preview",
|
||||
"showPreview": "Show Preview",
|
||||
"controlNetControlMode": "Control Mode",
|
||||
"clipSkip": "Clip Skip",
|
||||
"clipSkip": "CLIP Skip",
|
||||
"aspectRatio": "Ratio"
|
||||
},
|
||||
"settings": {
|
||||
@ -597,7 +597,10 @@
|
||||
"metadataLoadFailed": "Failed to load metadata",
|
||||
"initialImageSet": "Initial Image Set",
|
||||
"initialImageNotSet": "Initial Image Not Set",
|
||||
"initialImageNotSetDesc": "Could not load initial image"
|
||||
"initialImageNotSetDesc": "Could not load initial image",
|
||||
"nodesSaved": "Nodes Saved",
|
||||
"nodesLoaded": "Nodes Loaded",
|
||||
"nodesLoadedFailed": "Failed To Load Nodes"
|
||||
},
|
||||
"tooltip": {
|
||||
"feature": {
|
||||
@ -680,6 +683,8 @@
|
||||
"swapSizes": "Swap Sizes"
|
||||
},
|
||||
"nodes": {
|
||||
"reloadSchema": "Reload Schema"
|
||||
"reloadSchema": "Reload Schema",
|
||||
"saveNodes": "Save Nodes",
|
||||
"loadNodes": "Load Nodes"
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
setIsMovingBoundingBox,
|
||||
setIsTransformingBoundingBox,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
import Konva from 'konva';
|
||||
import { GroupConfig } from 'konva/lib/Group';
|
||||
import { KonvaEventObject } from 'konva/lib/Node';
|
||||
@ -22,8 +23,8 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Group, Rect, Transformer } from 'react-konva';
|
||||
|
||||
const boundingBoxPreviewSelector = createSelector(
|
||||
canvasSelector,
|
||||
(canvas) => {
|
||||
[canvasSelector, uiSelector],
|
||||
(canvas, ui) => {
|
||||
const {
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
@ -35,6 +36,8 @@ const boundingBoxPreviewSelector = createSelector(
|
||||
shouldSnapToGrid,
|
||||
} = canvas;
|
||||
|
||||
const { aspectRatio } = ui;
|
||||
|
||||
return {
|
||||
boundingBoxCoordinates,
|
||||
boundingBoxDimensions,
|
||||
@ -45,6 +48,7 @@ const boundingBoxPreviewSelector = createSelector(
|
||||
shouldSnapToGrid,
|
||||
tool,
|
||||
hitStrokeWidth: 20 / stageScale,
|
||||
aspectRatio,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -70,6 +74,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
||||
shouldSnapToGrid,
|
||||
tool,
|
||||
hitStrokeWidth,
|
||||
aspectRatio,
|
||||
} = useAppSelector(boundingBoxPreviewSelector);
|
||||
|
||||
const transformerRef = useRef<Konva.Transformer>(null);
|
||||
@ -137,12 +142,22 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
||||
const x = Math.round(rect.x());
|
||||
const y = Math.round(rect.y());
|
||||
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
width,
|
||||
height,
|
||||
})
|
||||
);
|
||||
if (aspectRatio) {
|
||||
const newHeight = roundToMultiple(width / aspectRatio, 64);
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
width: width,
|
||||
height: newHeight,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
width,
|
||||
height,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
dispatch(
|
||||
setBoundingBoxCoordinates({
|
||||
@ -154,7 +169,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
||||
// Reset the scale now that the coords/dimensions have been un-scaled
|
||||
rect.scaleX(1);
|
||||
rect.scaleY(1);
|
||||
}, [dispatch, shouldSnapToGrid]);
|
||||
}, [dispatch, shouldSnapToGrid, aspectRatio]);
|
||||
|
||||
const anchorDragBoundFunc = useCallback(
|
||||
(
|
||||
|
@ -7,7 +7,14 @@ import {
|
||||
import { IRect, Vector2d } from 'konva/lib/types';
|
||||
import { clamp, cloneDeep } from 'lodash-es';
|
||||
//
|
||||
import {
|
||||
setActiveTab,
|
||||
setAspectRatio,
|
||||
setShouldUseCanvasBetaLayout,
|
||||
} from 'features/ui/store/uiSlice';
|
||||
import { RgbaColor } from 'react-colorful';
|
||||
import { sessionCanceled } from 'services/api/thunks/session';
|
||||
import { ImageDTO } from 'services/api/types';
|
||||
import calculateCoordinates from '../util/calculateCoordinates';
|
||||
import calculateScale from '../util/calculateScale';
|
||||
import { STAGE_PADDING_PERCENTAGE } from '../util/constants';
|
||||
@ -28,13 +35,6 @@ import {
|
||||
isCanvasBaseImage,
|
||||
isCanvasMaskLine,
|
||||
} from './canvasTypes';
|
||||
import { ImageDTO } from 'services/api/types';
|
||||
import { sessionCanceled } from 'services/api/thunks/session';
|
||||
import {
|
||||
setActiveTab,
|
||||
setShouldUseCanvasBetaLayout,
|
||||
} from 'features/ui/store/uiSlice';
|
||||
import { imageUrlsReceived } from 'services/api/thunks/image';
|
||||
|
||||
export const initialLayerState: CanvasLayerState = {
|
||||
objects: [],
|
||||
@ -240,6 +240,16 @@ export const canvasSlice = createSlice({
|
||||
state.scaledBoundingBoxDimensions = scaledDimensions;
|
||||
}
|
||||
},
|
||||
flipBoundingBoxAxes: (state) => {
|
||||
const [currWidth, currHeight] = [
|
||||
state.boundingBoxDimensions.width,
|
||||
state.boundingBoxDimensions.height,
|
||||
];
|
||||
state.boundingBoxDimensions = {
|
||||
width: currHeight,
|
||||
height: currWidth,
|
||||
};
|
||||
},
|
||||
setBoundingBoxCoordinates: (state, action: PayloadAction<Vector2d>) => {
|
||||
state.boundingBoxCoordinates = floorCoordinates(action.payload);
|
||||
},
|
||||
@ -864,6 +874,15 @@ export const canvasSlice = createSlice({
|
||||
builder.addCase(setActiveTab, (state, action) => {
|
||||
state.doesCanvasNeedScaling = true;
|
||||
});
|
||||
builder.addCase(setAspectRatio, (state, action) => {
|
||||
const ratio = action.payload;
|
||||
if (ratio) {
|
||||
state.boundingBoxDimensions.height = roundToMultiple(
|
||||
state.boundingBoxDimensions.width / ratio,
|
||||
64
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
// const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
@ -912,6 +931,7 @@ export const {
|
||||
setBoundingBoxDimensions,
|
||||
setBoundingBoxPreviewFill,
|
||||
setBoundingBoxScaleMethod,
|
||||
flipBoundingBoxAxes,
|
||||
setBrushColor,
|
||||
setBrushSize,
|
||||
setCanvasContainerDimensions,
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
OnInit,
|
||||
OnNodesChange,
|
||||
ReactFlow,
|
||||
ReactFlowInstance,
|
||||
} from 'reactflow';
|
||||
import {
|
||||
connectionEnded,
|
||||
@ -18,6 +17,7 @@ import {
|
||||
connectionStarted,
|
||||
edgesChanged,
|
||||
nodesChanged,
|
||||
setEditorInstance,
|
||||
} from '../store/nodesSlice';
|
||||
import { InvocationComponent } from './InvocationComponent';
|
||||
import ProgressImageNode from './ProgressImageNode';
|
||||
@ -69,11 +69,13 @@ export const Flow = () => {
|
||||
dispatch(connectionEnded());
|
||||
}, [dispatch]);
|
||||
|
||||
const onInit: OnInit = useCallback((v: ReactFlowInstance) => {
|
||||
if (v) {
|
||||
v.fitView();
|
||||
}
|
||||
}, []);
|
||||
const onInit: OnInit = useCallback(
|
||||
(v) => {
|
||||
dispatch(setEditorInstance(v));
|
||||
if (v) v.fitView();
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { HStack } from '@chakra-ui/react';
|
||||
import CancelButton from 'features/parameters/components/ProcessButtons/CancelButton';
|
||||
import { memo } from 'react';
|
||||
import { Panel } from 'reactflow';
|
||||
|
||||
import CancelButton from 'features/parameters/components/ProcessButtons/CancelButton';
|
||||
import LoadNodesButton from '../ui/LoadNodesButton';
|
||||
import NodeInvokeButton from '../ui/NodeInvokeButton';
|
||||
import ReloadSchemaButton from '../ui/ReloadSchemaButton';
|
||||
import SaveNodesButton from '../ui/SaveNodesButton';
|
||||
|
||||
const TopCenterPanel = () => {
|
||||
return (
|
||||
@ -13,6 +14,8 @@ const TopCenterPanel = () => {
|
||||
<NodeInvokeButton />
|
||||
<CancelButton />
|
||||
<ReloadSchemaButton />
|
||||
<SaveNodesButton />
|
||||
<LoadNodesButton />
|
||||
</HStack>
|
||||
</Panel>
|
||||
);
|
||||
|
@ -0,0 +1,79 @@
|
||||
import { FileButton } from '@mantine/core';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { loadFileEdges, loadFileNodes } from 'features/nodes/store/nodesSlice';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaUpload } from 'react-icons/fa';
|
||||
import { useReactFlow } from 'reactflow';
|
||||
|
||||
const LoadNodesButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { fitView } = useReactFlow();
|
||||
|
||||
const uploadedFileRef = useRef<() => void>(null);
|
||||
|
||||
const restoreJSONToEditor = useCallback(
|
||||
(v: File | null) => {
|
||||
if (!v) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
const json = reader.result;
|
||||
const retrievedNodeTree = await JSON.parse(String(json));
|
||||
|
||||
if (!retrievedNodeTree) {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: t('toast.nodesLoadedFailed'),
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (retrievedNodeTree) {
|
||||
dispatch(loadFileNodes(retrievedNodeTree.nodes));
|
||||
dispatch(loadFileEdges(retrievedNodeTree.edges));
|
||||
fitView();
|
||||
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({ title: t('toast.nodesLoaded'), status: 'success' })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
reader.abort();
|
||||
};
|
||||
|
||||
reader.readAsText(v);
|
||||
|
||||
// Cleanup
|
||||
uploadedFileRef.current?.();
|
||||
},
|
||||
[fitView, dispatch, t]
|
||||
);
|
||||
return (
|
||||
<FileButton
|
||||
resetRef={uploadedFileRef}
|
||||
accept="application/json"
|
||||
onChange={restoreJSONToEditor}
|
||||
>
|
||||
{(props) => (
|
||||
<IAIIconButton
|
||||
icon={<FaUpload />}
|
||||
tooltip={t('nodes.loadNodes')}
|
||||
aria-label={t('nodes.loadNodes')}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
</FileButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(LoadNodesButton);
|
@ -0,0 +1,45 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { map, omit } from 'lodash-es';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaSave } from 'react-icons/fa';
|
||||
|
||||
const SaveNodesButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const editorInstance = useAppSelector(
|
||||
(state: RootState) => state.nodes.editorInstance
|
||||
);
|
||||
|
||||
const saveEditorToJSON = useCallback(() => {
|
||||
if (editorInstance) {
|
||||
const editorState = editorInstance.toObject();
|
||||
|
||||
editorState.edges = map(editorState.edges, (edge) => {
|
||||
return omit(edge, ['style']);
|
||||
});
|
||||
|
||||
const nodeSetupJSON = new Blob([JSON.stringify(editorState)]);
|
||||
const nodeDownloadElement = document.createElement('a');
|
||||
nodeDownloadElement.href = URL.createObjectURL(nodeSetupJSON);
|
||||
nodeDownloadElement.download = 'MyNodes.json';
|
||||
document.body.appendChild(nodeDownloadElement);
|
||||
nodeDownloadElement.click();
|
||||
// Cleanup
|
||||
nodeDownloadElement.remove();
|
||||
}
|
||||
}, [editorInstance]);
|
||||
|
||||
return (
|
||||
<IAIIconButton
|
||||
icon={<FaSave />}
|
||||
fontSize={18}
|
||||
tooltip={t('nodes.saveNodes')}
|
||||
aria-label={t('nodes.saveNodes')}
|
||||
onClick={saveEditorToJSON}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(SaveNodesButton);
|
@ -13,6 +13,7 @@ import {
|
||||
Node,
|
||||
NodeChange,
|
||||
OnConnectStartParams,
|
||||
ReactFlowInstance,
|
||||
} from 'reactflow';
|
||||
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
|
||||
import { ImageField } from 'services/api/types';
|
||||
@ -25,6 +26,7 @@ export type NodesState = {
|
||||
invocationTemplates: Record<string, InvocationTemplate>;
|
||||
connectionStartParams: OnConnectStartParams | null;
|
||||
shouldShowGraphOverlay: boolean;
|
||||
editorInstance: ReactFlowInstance | undefined;
|
||||
};
|
||||
|
||||
export const initialNodesState: NodesState = {
|
||||
@ -34,6 +36,7 @@ export const initialNodesState: NodesState = {
|
||||
invocationTemplates: {},
|
||||
connectionStartParams: null,
|
||||
shouldShowGraphOverlay: false,
|
||||
editorInstance: undefined,
|
||||
};
|
||||
|
||||
const nodesSlice = createSlice({
|
||||
@ -121,6 +124,15 @@ const nodesSlice = createSlice({
|
||||
nodeEditorReset: () => {
|
||||
return { ...initialNodesState };
|
||||
},
|
||||
setEditorInstance: (state, action) => {
|
||||
state.editorInstance = action.payload;
|
||||
},
|
||||
loadFileNodes: (state, action: PayloadAction<Node<InvocationValue>[]>) => {
|
||||
state.nodes = action.payload;
|
||||
},
|
||||
loadFileEdges: (state, action: PayloadAction<Edge[]>) => {
|
||||
state.edges = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(receivedOpenAPISchema.fulfilled, (state, action) => {
|
||||
@ -141,6 +153,9 @@ export const {
|
||||
nodeTemplatesBuilt,
|
||||
nodeEditorReset,
|
||||
imageCollectionFieldValueChanged,
|
||||
setEditorInstance,
|
||||
loadFileNodes,
|
||||
loadFileEdges,
|
||||
} = nodesSlice.actions;
|
||||
|
||||
export default nodesSlice.reducer;
|
||||
|
@ -2,22 +2,26 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
(canvas, isStaging) => {
|
||||
[canvasSelector, isStagingSelector, uiSelector],
|
||||
(canvas, isStaging, ui) => {
|
||||
const { boundingBoxDimensions } = canvas;
|
||||
const { aspectRatio } = ui;
|
||||
return {
|
||||
boundingBoxDimensions,
|
||||
isStaging,
|
||||
aspectRatio,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
@ -25,7 +29,8 @@ const selector = createSelector(
|
||||
|
||||
const ParamBoundingBoxWidth = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { boundingBoxDimensions, isStaging } = useAppSelector(selector);
|
||||
const { boundingBoxDimensions, isStaging, aspectRatio } =
|
||||
useAppSelector(selector);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -36,6 +41,15 @@ const ParamBoundingBoxWidth = () => {
|
||||
height: Math.floor(v),
|
||||
})
|
||||
);
|
||||
if (aspectRatio) {
|
||||
const newWidth = roundToMultiple(v * aspectRatio, 64);
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
width: newWidth,
|
||||
height: Math.floor(v),
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResetHeight = () => {
|
||||
@ -45,6 +59,15 @@ const ParamBoundingBoxWidth = () => {
|
||||
height: Math.floor(512),
|
||||
})
|
||||
);
|
||||
if (aspectRatio) {
|
||||
const newWidth = roundToMultiple(512 * aspectRatio, 64);
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
width: newWidth,
|
||||
height: Math.floor(512),
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -0,0 +1,57 @@
|
||||
import { Flex, Spacer, Text } from '@chakra-ui/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { flipBoundingBoxAxes } from 'features/canvas/store/canvasSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MdOutlineSwapVert } from 'react-icons/md';
|
||||
import ParamAspectRatio from '../../Core/ParamAspectRatio';
|
||||
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
|
||||
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
|
||||
|
||||
export default function ParamBoundingBoxSize() {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
gap: 2,
|
||||
p: 4,
|
||||
borderRadius: 4,
|
||||
flexDirection: 'column',
|
||||
w: 'full',
|
||||
bg: 'base.150',
|
||||
_dark: {
|
||||
bg: 'base.750',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex alignItems="center" gap={2}>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
width: 'full',
|
||||
color: 'base.700',
|
||||
_dark: {
|
||||
color: 'base.300',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('parameters.aspectRatio')}
|
||||
</Text>
|
||||
<Spacer />
|
||||
<ParamAspectRatio />
|
||||
<IAIIconButton
|
||||
tooltip={t('ui.swapSizes')}
|
||||
aria-label={t('ui.swapSizes')}
|
||||
size="sm"
|
||||
icon={<MdOutlineSwapVert />}
|
||||
fontSize={20}
|
||||
onClick={() => dispatch(flipBoundingBoxAxes())}
|
||||
/>
|
||||
</Flex>
|
||||
<ParamBoundingBoxWidth />
|
||||
<ParamBoundingBoxHeight />
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -2,22 +2,26 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import {
|
||||
canvasSelector,
|
||||
isStagingSelector,
|
||||
} from 'features/canvas/store/canvasSelectors';
|
||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const selector = createSelector(
|
||||
[canvasSelector, isStagingSelector],
|
||||
(canvas, isStaging) => {
|
||||
[canvasSelector, isStagingSelector, uiSelector],
|
||||
(canvas, isStaging, ui) => {
|
||||
const { boundingBoxDimensions } = canvas;
|
||||
const { aspectRatio } = ui;
|
||||
return {
|
||||
boundingBoxDimensions,
|
||||
isStaging,
|
||||
aspectRatio,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
@ -25,7 +29,8 @@ const selector = createSelector(
|
||||
|
||||
const ParamBoundingBoxWidth = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { boundingBoxDimensions, isStaging } = useAppSelector(selector);
|
||||
const { boundingBoxDimensions, isStaging, aspectRatio } =
|
||||
useAppSelector(selector);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -36,6 +41,15 @@ const ParamBoundingBoxWidth = () => {
|
||||
width: Math.floor(v),
|
||||
})
|
||||
);
|
||||
if (aspectRatio) {
|
||||
const newHeight = roundToMultiple(v / aspectRatio, 64);
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
width: Math.floor(v),
|
||||
height: newHeight,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleResetWidth = () => {
|
||||
@ -45,6 +59,15 @@ const ParamBoundingBoxWidth = () => {
|
||||
width: Math.floor(512),
|
||||
})
|
||||
);
|
||||
if (aspectRatio) {
|
||||
const newHeight = roundToMultiple(512 / aspectRatio, 64);
|
||||
dispatch(
|
||||
setBoundingBoxDimensions({
|
||||
width: Math.floor(512),
|
||||
height: newHeight,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -4,8 +4,7 @@ import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAICollapse from 'common/components/IAICollapse';
|
||||
import ParamBoundingBoxHeight from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxHeight';
|
||||
import ParamBoundingBoxWidth from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxWidth';
|
||||
import ParamBoundingBoxSize from 'features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxSize';
|
||||
import ParamCFGScale from 'features/parameters/components/Parameters/Core/ParamCFGScale';
|
||||
import ParamIterations from 'features/parameters/components/Parameters/Core/ParamIterations';
|
||||
import ParamModelandVAEandScheduler from 'features/parameters/components/Parameters/Core/ParamModelandVAEandScheduler';
|
||||
@ -51,8 +50,7 @@ const UnifiedCanvasCoreParameters = () => {
|
||||
<Box pt={2}>
|
||||
<ParamSeedFull />
|
||||
</Box>
|
||||
<ParamBoundingBoxWidth />
|
||||
<ParamBoundingBoxHeight />
|
||||
<ParamBoundingBoxSize />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@ -65,8 +63,7 @@ const UnifiedCanvasCoreParameters = () => {
|
||||
<Box pt={2}>
|
||||
<ParamSeedFull />
|
||||
</Box>
|
||||
<ParamBoundingBoxWidth />
|
||||
<ParamBoundingBoxHeight />
|
||||
<ParamBoundingBoxSize />
|
||||
</>
|
||||
)}
|
||||
<ImageToImageStrength />
|
||||
|
Loading…
Reference in New Issue
Block a user