mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): add optimal size handling
This commit is contained in:
parent
1a4be78013
commit
4fdc4c15f9
@ -1079,6 +1079,11 @@
|
|||||||
"aspect": "Aspect",
|
"aspect": "Aspect",
|
||||||
"aspectRatio": "Aspect Ratio",
|
"aspectRatio": "Aspect Ratio",
|
||||||
"aspectRatioFree": "Free",
|
"aspectRatioFree": "Free",
|
||||||
|
"lockAspectRatio": "Lock Aspect Ratio",
|
||||||
|
"swapDimensions": "Swap Dimensions",
|
||||||
|
"setToOptimalSize": "Optimize size for model",
|
||||||
|
"setToOptimalSizeTooSmall": "$t(parameters.setToOptimalSize) (may be too small)",
|
||||||
|
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (may be too large)",
|
||||||
"boundingBoxHeader": "Bounding Box",
|
"boundingBoxHeader": "Bounding Box",
|
||||||
"boundingBoxHeight": "Bounding Box Height",
|
"boundingBoxHeight": "Bounding Box Height",
|
||||||
"boundingBoxWidth": "Bounding Box Width",
|
"boundingBoxWidth": "Bounding Box Width",
|
||||||
|
@ -13,7 +13,10 @@ import type {
|
|||||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { workflowExposedFieldAdded } from 'features/nodes/store/workflowSlice';
|
import { workflowExposedFieldAdded } from 'features/nodes/store/workflowSlice';
|
||||||
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
import {
|
||||||
|
initialImageChanged,
|
||||||
|
selectOptimalDimension,
|
||||||
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
|
|
||||||
import { startAppListening } from '../';
|
import { startAppListening } from '../';
|
||||||
@ -26,7 +29,7 @@ export const dndDropped = createAction<{
|
|||||||
export const addImageDroppedListener = () => {
|
export const addImageDroppedListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
actionCreator: dndDropped,
|
actionCreator: dndDropped,
|
||||||
effect: async (action, { dispatch }) => {
|
effect: async (action, { dispatch, getState }) => {
|
||||||
const log = logger('dnd');
|
const log = logger('dnd');
|
||||||
const { activeData, overData } = action.payload;
|
const { activeData, overData } = action.payload;
|
||||||
|
|
||||||
@ -115,7 +118,12 @@ export const addImageDroppedListener = () => {
|
|||||||
activeData.payloadType === 'IMAGE_DTO' &&
|
activeData.payloadType === 'IMAGE_DTO' &&
|
||||||
activeData.payload.imageDTO
|
activeData.payload.imageDTO
|
||||||
) {
|
) {
|
||||||
dispatch(setInitialCanvasImage(activeData.payload.imageDTO));
|
dispatch(
|
||||||
|
setInitialCanvasImage(
|
||||||
|
activeData.payload.imageDTO,
|
||||||
|
selectOptimalDimension(getState())
|
||||||
|
)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@ import {
|
|||||||
controlAdapterIsEnabledChanged,
|
controlAdapterIsEnabledChanged,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
import {
|
||||||
|
initialImageChanged,
|
||||||
|
selectOptimalDimension,
|
||||||
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { omit } from 'lodash-es';
|
import { omit } from 'lodash-es';
|
||||||
@ -76,7 +79,9 @@ export const addImageUploadedFulfilledListener = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_CANVAS_INITIAL_IMAGE') {
|
if (postUploadAction?.type === 'SET_CANVAS_INITIAL_IMAGE') {
|
||||||
dispatch(setInitialCanvasImage(imageDTO));
|
dispatch(
|
||||||
|
setInitialCanvasImage(imageDTO, selectOptimalDimension(state))
|
||||||
|
);
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
|
||||||
import {
|
import {
|
||||||
controlAdapterIsEnabledChanged,
|
controlAdapterIsEnabledChanged,
|
||||||
selectControlAdapterAll,
|
selectControlAdapterAll,
|
||||||
@ -7,10 +6,8 @@ import {
|
|||||||
import { loraRemoved } from 'features/lora/store/loraSlice';
|
import { loraRemoved } from 'features/lora/store/loraSlice';
|
||||||
import { modelSelected } from 'features/parameters/store/actions';
|
import { modelSelected } from 'features/parameters/store/actions';
|
||||||
import {
|
import {
|
||||||
heightChanged,
|
|
||||||
modelChanged,
|
modelChanged,
|
||||||
vaeSelected,
|
vaeSelected,
|
||||||
widthChanged,
|
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
@ -84,22 +81,6 @@ export const addModelSelectedListener = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Width / Height / Bounding Box Dimensions on Model Change
|
|
||||||
if (
|
|
||||||
state.generation.model?.base_model !== newModel.base_model &&
|
|
||||||
state.ui.shouldAutoChangeDimensions
|
|
||||||
) {
|
|
||||||
if (['sdxl', 'sdxl-refiner'].includes(newModel.base_model)) {
|
|
||||||
dispatch(widthChanged(1024));
|
|
||||||
dispatch(heightChanged(1024));
|
|
||||||
dispatch(setBoundingBoxDimensions({ width: 1024, height: 1024 }));
|
|
||||||
} else {
|
|
||||||
dispatch(widthChanged(512));
|
|
||||||
dispatch(heightChanged(512));
|
|
||||||
dispatch(setBoundingBoxDimensions({ width: 512, height: 512 }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(modelChanged(newModel));
|
dispatch(modelChanged(newModel));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -26,6 +26,8 @@ import {
|
|||||||
CANVAS_GRID_SIZE_COARSE,
|
CANVAS_GRID_SIZE_COARSE,
|
||||||
CANVAS_GRID_SIZE_FINE,
|
CANVAS_GRID_SIZE_FINE,
|
||||||
} from 'features/canvas/store/constants';
|
} from 'features/canvas/store/constants';
|
||||||
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
import type { GroupConfig } from 'konva/lib/Group';
|
import type { GroupConfig } from 'konva/lib/Group';
|
||||||
import type { KonvaEventObject } from 'konva/lib/Node';
|
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||||
@ -37,8 +39,8 @@ import { Group, Rect, Transformer } from 'react-konva';
|
|||||||
const borderDash = [4, 4];
|
const borderDash = [4, 4];
|
||||||
|
|
||||||
const boundingBoxPreviewSelector = createMemoizedSelector(
|
const boundingBoxPreviewSelector = createMemoizedSelector(
|
||||||
[stateSelector],
|
[stateSelector, selectOptimalDimension],
|
||||||
({ canvas }) => {
|
({ canvas }, optimalDimension) => {
|
||||||
const {
|
const {
|
||||||
boundingBoxCoordinates,
|
boundingBoxCoordinates,
|
||||||
boundingBoxDimensions,
|
boundingBoxDimensions,
|
||||||
@ -56,6 +58,7 @@ const boundingBoxPreviewSelector = createMemoizedSelector(
|
|||||||
tool,
|
tool,
|
||||||
hitStrokeWidth: 20 / stageScale,
|
hitStrokeWidth: 20 / stageScale,
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
|
optimalDimension,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -73,6 +76,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
tool,
|
tool,
|
||||||
hitStrokeWidth,
|
hitStrokeWidth,
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
|
optimalDimension,
|
||||||
} = useAppSelector(boundingBoxPreviewSelector);
|
} = useAppSelector(boundingBoxPreviewSelector);
|
||||||
|
|
||||||
const transformerRef = useRef<Konva.Transformer>(null);
|
const transformerRef = useRef<Konva.Transformer>(null);
|
||||||
@ -163,22 +167,25 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
const y = Math.round(rect.y());
|
const y = Math.round(rect.y());
|
||||||
|
|
||||||
if (aspectRatio.isLocked) {
|
if (aspectRatio.isLocked) {
|
||||||
const newHeight = roundDownToMultipleMin(
|
const newDimensions = calculateNewSize(aspectRatio.value, width * height);
|
||||||
width / aspectRatio.value,
|
|
||||||
gridSize
|
|
||||||
);
|
|
||||||
dispatch(
|
dispatch(
|
||||||
setBoundingBoxDimensions({
|
setBoundingBoxDimensions(
|
||||||
width: roundDownToMultipleMin(width, gridSize),
|
{
|
||||||
height: newHeight,
|
width: roundDownToMultipleMin(newDimensions.width, gridSize),
|
||||||
})
|
height: roundDownToMultipleMin(newDimensions.height, gridSize),
|
||||||
|
},
|
||||||
|
optimalDimension
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
dispatch(
|
dispatch(
|
||||||
setBoundingBoxDimensions({
|
setBoundingBoxDimensions(
|
||||||
width: roundDownToMultipleMin(width, gridSize),
|
{
|
||||||
height: roundDownToMultipleMin(height, gridSize),
|
width: roundDownToMultipleMin(width, gridSize),
|
||||||
})
|
height: roundDownToMultipleMin(height, gridSize),
|
||||||
|
},
|
||||||
|
optimalDimension
|
||||||
|
)
|
||||||
);
|
);
|
||||||
dispatch(
|
dispatch(
|
||||||
aspectRatioChanged({
|
aspectRatioChanged({
|
||||||
@ -205,6 +212,7 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
dispatch,
|
dispatch,
|
||||||
shouldSnapToGrid,
|
shouldSnapToGrid,
|
||||||
gridSize,
|
gridSize,
|
||||||
|
optimalDimension,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const anchorDragBoundFunc = useCallback(
|
const anchorDragBoundFunc = useCallback(
|
||||||
@ -233,7 +241,6 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
|
|||||||
x: roundDownToMultiple(newPos.x, scaledStep) + offsetX,
|
x: roundDownToMultiple(newPos.x, scaledStep) + offsetX,
|
||||||
y: roundDownToMultiple(newPos.y, scaledStep) + offsetY,
|
y: roundDownToMultiple(newPos.y, scaledStep) + offsetY,
|
||||||
};
|
};
|
||||||
console.log({ oldPos, newPos, newCoordinates });
|
|
||||||
|
|
||||||
return newCoordinates;
|
return newCoordinates;
|
||||||
},
|
},
|
||||||
|
@ -11,6 +11,12 @@ import floorCoordinates from 'features/canvas/util/floorCoordinates';
|
|||||||
import getScaledBoundingBoxDimensions from 'features/canvas/util/getScaledBoundingBoxDimensions';
|
import getScaledBoundingBoxDimensions from 'features/canvas/util/getScaledBoundingBoxDimensions';
|
||||||
import roundDimensionsToMultiple from 'features/canvas/util/roundDimensionsToMultiple';
|
import roundDimensionsToMultiple from 'features/canvas/util/roundDimensionsToMultiple';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
|
import { modelChanged } from 'features/parameters/store/generationSlice';
|
||||||
|
import type { PayloadActionWithOptimalDimension } from 'features/parameters/store/types';
|
||||||
|
import {
|
||||||
|
getIsSizeOptimal,
|
||||||
|
getOptimalDimension,
|
||||||
|
} from 'features/parameters/util/optimalDimension';
|
||||||
import type { IRect, Vector2d } from 'konva/lib/types';
|
import type { IRect, Vector2d } from 'konva/lib/types';
|
||||||
import { clamp, cloneDeep } from 'lodash-es';
|
import { clamp, cloneDeep } from 'lodash-es';
|
||||||
import type { RgbaColor } from 'react-colorful';
|
import type { RgbaColor } from 'react-colorful';
|
||||||
@ -86,6 +92,29 @@ export const initialCanvasState: CanvasState = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setBoundingBoxDimensionsReducer = (
|
||||||
|
state: CanvasState,
|
||||||
|
payload: Partial<Dimensions>,
|
||||||
|
optimalDimension: number
|
||||||
|
) => {
|
||||||
|
const boundingBoxDimensions = payload;
|
||||||
|
const newDimensions = roundDimensionsToMultiple(
|
||||||
|
{
|
||||||
|
...state.boundingBoxDimensions,
|
||||||
|
...boundingBoxDimensions,
|
||||||
|
},
|
||||||
|
CANVAS_GRID_SIZE_FINE
|
||||||
|
);
|
||||||
|
state.boundingBoxDimensions = newDimensions;
|
||||||
|
if (state.boundingBoxScaleMethod === 'auto') {
|
||||||
|
const scaledDimensions = getScaledBoundingBoxDimensions(
|
||||||
|
newDimensions,
|
||||||
|
optimalDimension
|
||||||
|
);
|
||||||
|
state.scaledBoundingBoxDimensions = scaledDimensions;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const canvasSlice = createSlice({
|
export const canvasSlice = createSlice({
|
||||||
name: 'canvas',
|
name: 'canvas',
|
||||||
initialState: initialCanvasState,
|
initialState: initialCanvasState,
|
||||||
@ -132,117 +161,90 @@ export const canvasSlice = createSlice({
|
|||||||
state.isMaskEnabled = action.payload;
|
state.isMaskEnabled = action.payload;
|
||||||
state.layer = action.payload ? 'mask' : 'base';
|
state.layer = action.payload ? 'mask' : 'base';
|
||||||
},
|
},
|
||||||
setInitialCanvasImage: (state, action: PayloadAction<ImageDTO>) => {
|
setInitialCanvasImage: {
|
||||||
const image = action.payload;
|
reducer: (state, action: PayloadActionWithOptimalDimension<ImageDTO>) => {
|
||||||
const { width, height } = image;
|
const { width, height, image_name } = action.payload;
|
||||||
const { stageDimensions } = state;
|
const { optimalDimension } = action.meta;
|
||||||
|
const { stageDimensions } = state;
|
||||||
|
|
||||||
const newBoundingBoxDimensions = {
|
const newBoundingBoxDimensions = {
|
||||||
width: roundDownToMultiple(
|
width: roundDownToMultiple(
|
||||||
clamp(width, CANVAS_GRID_SIZE_FINE, 512),
|
clamp(width, CANVAS_GRID_SIZE_FINE, optimalDimension),
|
||||||
CANVAS_GRID_SIZE_FINE
|
CANVAS_GRID_SIZE_FINE
|
||||||
),
|
),
|
||||||
height: roundDownToMultiple(
|
height: roundDownToMultiple(
|
||||||
clamp(height, CANVAS_GRID_SIZE_FINE, 512),
|
clamp(height, CANVAS_GRID_SIZE_FINE, optimalDimension),
|
||||||
CANVAS_GRID_SIZE_FINE
|
CANVAS_GRID_SIZE_FINE
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const newBoundingBoxCoordinates = {
|
const newBoundingBoxCoordinates = {
|
||||||
x: roundToMultiple(
|
x: roundToMultiple(
|
||||||
width / 2 - newBoundingBoxDimensions.width / 2,
|
width / 2 - newBoundingBoxDimensions.width / 2,
|
||||||
CANVAS_GRID_SIZE_FINE
|
CANVAS_GRID_SIZE_FINE
|
||||||
),
|
),
|
||||||
y: roundToMultiple(
|
y: roundToMultiple(
|
||||||
height / 2 - newBoundingBoxDimensions.height / 2,
|
height / 2 - newBoundingBoxDimensions.height / 2,
|
||||||
CANVAS_GRID_SIZE_FINE
|
CANVAS_GRID_SIZE_FINE
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (state.boundingBoxScaleMethod === 'auto') {
|
if (state.boundingBoxScaleMethod === 'auto') {
|
||||||
const scaledDimensions = getScaledBoundingBoxDimensions(
|
const scaledDimensions = getScaledBoundingBoxDimensions(
|
||||||
newBoundingBoxDimensions
|
newBoundingBoxDimensions,
|
||||||
|
optimalDimension
|
||||||
|
);
|
||||||
|
state.scaledBoundingBoxDimensions = scaledDimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.boundingBoxDimensions = newBoundingBoxDimensions;
|
||||||
|
state.boundingBoxCoordinates = newBoundingBoxCoordinates;
|
||||||
|
|
||||||
|
state.pastLayerStates.push(cloneDeep(state.layerState));
|
||||||
|
|
||||||
|
state.layerState = {
|
||||||
|
...cloneDeep(initialLayerState),
|
||||||
|
objects: [
|
||||||
|
{
|
||||||
|
kind: 'image',
|
||||||
|
layer: 'base',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
imageName: image_name,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
state.futureLayerStates = [];
|
||||||
|
state.batchIds = [];
|
||||||
|
|
||||||
|
const newScale = calculateScale(
|
||||||
|
stageDimensions.width,
|
||||||
|
stageDimensions.height,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
STAGE_PADDING_PERCENTAGE
|
||||||
);
|
);
|
||||||
state.scaledBoundingBoxDimensions = scaledDimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.boundingBoxDimensions = newBoundingBoxDimensions;
|
const newCoordinates = calculateCoordinates(
|
||||||
state.boundingBoxCoordinates = newBoundingBoxCoordinates;
|
stageDimensions.width,
|
||||||
|
stageDimensions.height,
|
||||||
state.pastLayerStates.push(cloneDeep(state.layerState));
|
0,
|
||||||
|
0,
|
||||||
state.layerState = {
|
width,
|
||||||
...cloneDeep(initialLayerState),
|
height,
|
||||||
objects: [
|
newScale
|
||||||
{
|
);
|
||||||
kind: 'image',
|
state.stageScale = newScale;
|
||||||
layer: 'base',
|
state.stageCoordinates = newCoordinates;
|
||||||
x: 0,
|
},
|
||||||
y: 0,
|
prepare: (payload: ImageDTO, optimalDimension: number) => ({
|
||||||
width: width,
|
payload,
|
||||||
height: height,
|
meta: {
|
||||||
imageName: image.image_name,
|
optimalDimension,
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
state.futureLayerStates = [];
|
|
||||||
state.batchIds = [];
|
|
||||||
|
|
||||||
const newScale = calculateScale(
|
|
||||||
stageDimensions.width,
|
|
||||||
stageDimensions.height,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
STAGE_PADDING_PERCENTAGE
|
|
||||||
);
|
|
||||||
|
|
||||||
const newCoordinates = calculateCoordinates(
|
|
||||||
stageDimensions.width,
|
|
||||||
stageDimensions.height,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
newScale
|
|
||||||
);
|
|
||||||
state.stageScale = newScale;
|
|
||||||
state.stageCoordinates = newCoordinates;
|
|
||||||
},
|
|
||||||
setBoundingBoxDimensions: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<Partial<Dimensions>>
|
|
||||||
) => {
|
|
||||||
const newDimensions = roundDimensionsToMultiple(
|
|
||||||
{
|
|
||||||
...state.boundingBoxDimensions,
|
|
||||||
...action.payload,
|
|
||||||
},
|
},
|
||||||
CANVAS_GRID_SIZE_FINE
|
}),
|
||||||
);
|
|
||||||
state.boundingBoxDimensions = newDimensions;
|
|
||||||
|
|
||||||
if (state.boundingBoxScaleMethod === 'auto') {
|
|
||||||
const scaledDimensions = getScaledBoundingBoxDimensions(newDimensions);
|
|
||||||
state.scaledBoundingBoxDimensions = scaledDimensions;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
flipBoundingBoxAxes: (state) => {
|
|
||||||
const [currWidth, currHeight] = [
|
|
||||||
state.boundingBoxDimensions.width,
|
|
||||||
state.boundingBoxDimensions.height,
|
|
||||||
];
|
|
||||||
const [currScaledWidth, currScaledHeight] = [
|
|
||||||
state.scaledBoundingBoxDimensions.width,
|
|
||||||
state.scaledBoundingBoxDimensions.height,
|
|
||||||
];
|
|
||||||
state.boundingBoxDimensions = {
|
|
||||||
width: currHeight,
|
|
||||||
height: currWidth,
|
|
||||||
};
|
|
||||||
state.scaledBoundingBoxDimensions = {
|
|
||||||
width: currScaledHeight,
|
|
||||||
height: currScaledWidth,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
setBoundingBoxCoordinates: (state, action: PayloadAction<Vector2d>) => {
|
setBoundingBoxCoordinates: (state, action: PayloadAction<Vector2d>) => {
|
||||||
state.boundingBoxCoordinates = floorCoordinates(action.payload);
|
state.boundingBoxCoordinates = floorCoordinates(action.payload);
|
||||||
@ -518,38 +520,6 @@ export const canvasSlice = createSlice({
|
|||||||
|
|
||||||
state.stageScale = newScale;
|
state.stageScale = newScale;
|
||||||
state.stageCoordinates = newCoordinates;
|
state.stageCoordinates = newCoordinates;
|
||||||
} else {
|
|
||||||
const newScale = calculateScale(
|
|
||||||
stageWidth,
|
|
||||||
stageHeight,
|
|
||||||
512,
|
|
||||||
512,
|
|
||||||
STAGE_PADDING_PERCENTAGE
|
|
||||||
);
|
|
||||||
|
|
||||||
const newCoordinates = calculateCoordinates(
|
|
||||||
stageWidth,
|
|
||||||
stageHeight,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
512,
|
|
||||||
512,
|
|
||||||
newScale
|
|
||||||
);
|
|
||||||
|
|
||||||
const newBoundingBoxDimensions = { width: 512, height: 512 };
|
|
||||||
|
|
||||||
state.stageScale = newScale;
|
|
||||||
state.stageCoordinates = newCoordinates;
|
|
||||||
state.boundingBoxCoordinates = { x: 0, y: 0 };
|
|
||||||
state.boundingBoxDimensions = newBoundingBoxDimensions;
|
|
||||||
|
|
||||||
if (state.boundingBoxScaleMethod === 'auto') {
|
|
||||||
const scaledDimensions = getScaledBoundingBoxDimensions(
|
|
||||||
newBoundingBoxDimensions
|
|
||||||
);
|
|
||||||
state.scaledBoundingBoxDimensions = scaledDimensions;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
nextStagingAreaImage: (state) => {
|
nextStagingAreaImage: (state) => {
|
||||||
@ -601,69 +571,29 @@ export const canvasSlice = createSlice({
|
|||||||
state.shouldShowStagingImage = true;
|
state.shouldShowStagingImage = true;
|
||||||
state.batchIds = [];
|
state.batchIds = [];
|
||||||
},
|
},
|
||||||
fitBoundingBoxToStage: (state) => {
|
setBoundingBoxScaleMethod: {
|
||||||
const {
|
reducer: (
|
||||||
boundingBoxDimensions,
|
state,
|
||||||
boundingBoxCoordinates,
|
action: PayloadActionWithOptimalDimension<BoundingBoxScaleMethod>
|
||||||
stageDimensions,
|
) => {
|
||||||
stageScale,
|
const boundingBoxScaleMethod = action.payload;
|
||||||
} = state;
|
const { optimalDimension } = action.meta;
|
||||||
const scaledStageWidth = stageDimensions.width / stageScale;
|
state.boundingBoxScaleMethod = boundingBoxScaleMethod;
|
||||||
const scaledStageHeight = stageDimensions.height / stageScale;
|
|
||||||
|
|
||||||
if (
|
if (boundingBoxScaleMethod === 'auto') {
|
||||||
boundingBoxCoordinates.x < 0 ||
|
|
||||||
boundingBoxCoordinates.x + boundingBoxDimensions.width >
|
|
||||||
scaledStageWidth ||
|
|
||||||
boundingBoxCoordinates.y < 0 ||
|
|
||||||
boundingBoxCoordinates.y + boundingBoxDimensions.height >
|
|
||||||
scaledStageHeight
|
|
||||||
) {
|
|
||||||
const newBoundingBoxDimensions = {
|
|
||||||
width: roundDownToMultiple(
|
|
||||||
clamp(scaledStageWidth, CANVAS_GRID_SIZE_FINE, 512),
|
|
||||||
CANVAS_GRID_SIZE_FINE
|
|
||||||
),
|
|
||||||
height: roundDownToMultiple(
|
|
||||||
clamp(scaledStageHeight, CANVAS_GRID_SIZE_FINE, 512),
|
|
||||||
CANVAS_GRID_SIZE_FINE
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
const newBoundingBoxCoordinates = {
|
|
||||||
x: roundToMultiple(
|
|
||||||
scaledStageWidth / 2 - newBoundingBoxDimensions.width / 2,
|
|
||||||
CANVAS_GRID_SIZE_FINE
|
|
||||||
),
|
|
||||||
y: roundToMultiple(
|
|
||||||
scaledStageHeight / 2 - newBoundingBoxDimensions.height / 2,
|
|
||||||
CANVAS_GRID_SIZE_FINE
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
state.boundingBoxDimensions = newBoundingBoxDimensions;
|
|
||||||
state.boundingBoxCoordinates = newBoundingBoxCoordinates;
|
|
||||||
|
|
||||||
if (state.boundingBoxScaleMethod === 'auto') {
|
|
||||||
const scaledDimensions = getScaledBoundingBoxDimensions(
|
const scaledDimensions = getScaledBoundingBoxDimensions(
|
||||||
newBoundingBoxDimensions
|
state.boundingBoxDimensions,
|
||||||
|
optimalDimension
|
||||||
);
|
);
|
||||||
state.scaledBoundingBoxDimensions = scaledDimensions;
|
state.scaledBoundingBoxDimensions = scaledDimensions;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
prepare: (payload: BoundingBoxScaleMethod, optimalDimension: number) => ({
|
||||||
setBoundingBoxScaleMethod: (
|
payload,
|
||||||
state,
|
meta: {
|
||||||
action: PayloadAction<BoundingBoxScaleMethod>
|
optimalDimension,
|
||||||
) => {
|
},
|
||||||
state.boundingBoxScaleMethod = action.payload;
|
}),
|
||||||
|
|
||||||
if (action.payload === 'auto') {
|
|
||||||
const scaledDimensions = getScaledBoundingBoxDimensions(
|
|
||||||
state.boundingBoxDimensions
|
|
||||||
);
|
|
||||||
state.scaledBoundingBoxDimensions = scaledDimensions;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setScaledBoundingBoxDimensions: (
|
setScaledBoundingBoxDimensions: (
|
||||||
state,
|
state,
|
||||||
@ -671,6 +601,37 @@ export const canvasSlice = createSlice({
|
|||||||
) => {
|
) => {
|
||||||
state.scaledBoundingBoxDimensions = action.payload;
|
state.scaledBoundingBoxDimensions = action.payload;
|
||||||
},
|
},
|
||||||
|
setBoundingBoxDimensions: {
|
||||||
|
reducer: (
|
||||||
|
state,
|
||||||
|
action: PayloadActionWithOptimalDimension<Partial<Dimensions>>
|
||||||
|
) => {
|
||||||
|
setBoundingBoxDimensionsReducer(
|
||||||
|
state,
|
||||||
|
action.payload,
|
||||||
|
action.meta.optimalDimension
|
||||||
|
);
|
||||||
|
},
|
||||||
|
prepare: (payload: Partial<Dimensions>, optimalDimension: number) => ({
|
||||||
|
payload,
|
||||||
|
meta: {
|
||||||
|
optimalDimension,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
scaledBoundingBoxDimensionsReset: {
|
||||||
|
reducer: (state, action: PayloadActionWithOptimalDimension) => {
|
||||||
|
const scaledDimensions = getScaledBoundingBoxDimensions(
|
||||||
|
state.boundingBoxDimensions,
|
||||||
|
action.meta.optimalDimension
|
||||||
|
);
|
||||||
|
state.scaledBoundingBoxDimensions = scaledDimensions;
|
||||||
|
},
|
||||||
|
prepare: (payload: void, optimalDimension: number) => ({
|
||||||
|
payload: undefined,
|
||||||
|
meta: { optimalDimension },
|
||||||
|
}),
|
||||||
|
},
|
||||||
setShouldShowStagingImage: (state, action: PayloadAction<boolean>) => {
|
setShouldShowStagingImage: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldShowStagingImage = action.payload;
|
state.shouldShowStagingImage = action.payload;
|
||||||
},
|
},
|
||||||
@ -714,6 +675,22 @@ export const canvasSlice = createSlice({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(modelChanged, (state, action) => {
|
||||||
|
const optimalDimension = getOptimalDimension(action.payload);
|
||||||
|
const { width, height } = state.boundingBoxDimensions;
|
||||||
|
if (getIsSizeOptimal(width, height, optimalDimension)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setBoundingBoxDimensionsReducer(
|
||||||
|
state,
|
||||||
|
{
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
},
|
||||||
|
optimalDimension
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
builder.addCase(appSocketQueueItemStatusChanged, (state, action) => {
|
builder.addCase(appSocketQueueItemStatusChanged, (state, action) => {
|
||||||
const batch_status = action.payload.data.batch_status;
|
const batch_status = action.payload.data.batch_status;
|
||||||
if (!state.batchIds.includes(batch_status.batch_id)) {
|
if (!state.batchIds.includes(batch_status.batch_id)) {
|
||||||
@ -754,7 +731,6 @@ export const {
|
|||||||
commitColorPickerColor,
|
commitColorPickerColor,
|
||||||
commitStagingAreaImage,
|
commitStagingAreaImage,
|
||||||
discardStagedImages,
|
discardStagedImages,
|
||||||
fitBoundingBoxToStage,
|
|
||||||
nextStagingAreaImage,
|
nextStagingAreaImage,
|
||||||
prevStagingAreaImage,
|
prevStagingAreaImage,
|
||||||
redo,
|
redo,
|
||||||
@ -764,7 +740,6 @@ export const {
|
|||||||
setBoundingBoxDimensions,
|
setBoundingBoxDimensions,
|
||||||
setBoundingBoxPreviewFill,
|
setBoundingBoxPreviewFill,
|
||||||
setBoundingBoxScaleMethod,
|
setBoundingBoxScaleMethod,
|
||||||
flipBoundingBoxAxes,
|
|
||||||
setBrushColor,
|
setBrushColor,
|
||||||
setBrushSize,
|
setBrushSize,
|
||||||
setColorPickerColor,
|
setColorPickerColor,
|
||||||
@ -799,6 +774,7 @@ export const {
|
|||||||
canvasBatchIdAdded,
|
canvasBatchIdAdded,
|
||||||
canvasBatchIdsReset,
|
canvasBatchIdsReset,
|
||||||
aspectRatioChanged,
|
aspectRatioChanged,
|
||||||
|
scaledBoundingBoxDimensionsReset,
|
||||||
} = canvasSlice.actions;
|
} = canvasSlice.actions;
|
||||||
|
|
||||||
export default canvasSlice.reducer;
|
export default canvasSlice.reducer;
|
||||||
|
@ -2,19 +2,22 @@ import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
|||||||
import type { Dimensions } from 'features/canvas/store/canvasTypes';
|
import type { Dimensions } from 'features/canvas/store/canvasTypes';
|
||||||
import { CANVAS_GRID_SIZE_FINE } from 'features/canvas/store/constants';
|
import { CANVAS_GRID_SIZE_FINE } from 'features/canvas/store/constants';
|
||||||
|
|
||||||
const getScaledBoundingBoxDimensions = (dimensions: Dimensions) => {
|
const getScaledBoundingBoxDimensions = (
|
||||||
|
dimensions: Dimensions,
|
||||||
|
optimalDimension: number
|
||||||
|
) => {
|
||||||
const { width, height } = dimensions;
|
const { width, height } = dimensions;
|
||||||
|
|
||||||
const scaledDimensions = { width, height };
|
const scaledDimensions = { width, height };
|
||||||
const targetArea = 512 * 512;
|
const targetArea = optimalDimension * optimalDimension;
|
||||||
const aspectRatio = width / height;
|
const aspectRatio = width / height;
|
||||||
let currentArea = width * height;
|
let currentArea = width * height;
|
||||||
let maxDimension = 448;
|
let maxDimension = optimalDimension - CANVAS_GRID_SIZE_FINE;
|
||||||
while (currentArea < targetArea) {
|
while (currentArea < targetArea) {
|
||||||
maxDimension += CANVAS_GRID_SIZE_FINE;
|
maxDimension += CANVAS_GRID_SIZE_FINE;
|
||||||
if (width === height) {
|
if (width === height) {
|
||||||
scaledDimensions.width = 512;
|
scaledDimensions.width = optimalDimension;
|
||||||
scaledDimensions.height = 512;
|
scaledDimensions.height = optimalDimension;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
if (aspectRatio > 1) {
|
if (aspectRatio > 1) {
|
||||||
|
@ -17,6 +17,7 @@ import type {
|
|||||||
} from 'features/dnd/types';
|
} from 'features/dnd/types';
|
||||||
import {
|
import {
|
||||||
heightChanged,
|
heightChanged,
|
||||||
|
selectOptimalDimension,
|
||||||
widthChanged,
|
widthChanged,
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
@ -37,8 +38,8 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
stateSelector,
|
[stateSelector, activeTabNameSelector, selectOptimalDimension],
|
||||||
({ controlAdapters, gallery, system }) => {
|
({ controlAdapters, gallery, system }, activeTabName, optimalDimension) => {
|
||||||
const { pendingControlImages } = controlAdapters;
|
const { pendingControlImages } = controlAdapters;
|
||||||
const { autoAddBoardId } = gallery;
|
const { autoAddBoardId } = gallery;
|
||||||
const { isConnected } = system;
|
const { isConnected } = system;
|
||||||
@ -47,6 +48,8 @@ const selector = createMemoizedSelector(
|
|||||||
pendingControlImages,
|
pendingControlImages,
|
||||||
autoAddBoardId,
|
autoAddBoardId,
|
||||||
isConnected,
|
isConnected,
|
||||||
|
activeTabName,
|
||||||
|
optimalDimension,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -55,13 +58,15 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
|||||||
const controlImageName = useControlAdapterControlImage(id);
|
const controlImageName = useControlAdapterControlImage(id);
|
||||||
const processedControlImageName = useControlAdapterProcessedControlImage(id);
|
const processedControlImageName = useControlAdapterProcessedControlImage(id);
|
||||||
const processorType = useControlAdapterProcessorType(id);
|
const processorType = useControlAdapterProcessorType(id);
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const {
|
||||||
const { pendingControlImages, autoAddBoardId, isConnected } =
|
pendingControlImages,
|
||||||
useAppSelector(selector);
|
autoAddBoardId,
|
||||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
isConnected,
|
||||||
|
activeTabName,
|
||||||
|
optimalDimension,
|
||||||
|
} = useAppSelector(selector);
|
||||||
|
|
||||||
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
|
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
|
||||||
|
|
||||||
@ -113,16 +118,19 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
|||||||
|
|
||||||
if (activeTabName === 'unifiedCanvas') {
|
if (activeTabName === 'unifiedCanvas') {
|
||||||
dispatch(
|
dispatch(
|
||||||
setBoundingBoxDimensions({
|
setBoundingBoxDimensions(
|
||||||
width: controlImage.width,
|
{
|
||||||
height: controlImage.height,
|
width: controlImage.width,
|
||||||
})
|
height: controlImage.height,
|
||||||
|
},
|
||||||
|
optimalDimension
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
dispatch(widthChanged(controlImage.width));
|
dispatch(widthChanged(controlImage.width));
|
||||||
dispatch(heightChanged(controlImage.height));
|
dispatch(heightChanged(controlImage.height));
|
||||||
}
|
}
|
||||||
}, [controlImage, activeTabName, dispatch]);
|
}, [controlImage, activeTabName, dispatch, optimalDimension]);
|
||||||
|
|
||||||
const handleMouseEnter = useCallback(() => {
|
const handleMouseEnter = useCallback(() => {
|
||||||
setIsMouseOverImage(true);
|
setIsMouseOverImage(true);
|
||||||
|
@ -2,7 +2,7 @@ import { Flex, Spinner } from '@chakra-ui/react';
|
|||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { $customStarUI } from 'app/store/nanostores/customStarUI';
|
import { $customStarUI } from 'app/store/nanostores/customStarUI';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InvMenuItem } from 'common/components/InvMenu/InvMenuItem';
|
import { InvMenuItem } from 'common/components/InvMenu/InvMenuItem';
|
||||||
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
|
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
@ -17,6 +17,7 @@ import {
|
|||||||
} from 'features/gallery/store/actions';
|
} from 'features/gallery/store/actions';
|
||||||
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
import { useGetAndLoadEmbeddedWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow';
|
import { useGetAndLoadEmbeddedWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow';
|
||||||
@ -49,12 +50,10 @@ type SingleSelectionMenuItemsProps = {
|
|||||||
|
|
||||||
const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
||||||
const { imageDTO } = props;
|
const { imageDTO } = props;
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const toaster = useAppToaster();
|
const toaster = useAppToaster();
|
||||||
|
|
||||||
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
||||||
const customStarUi = useStore($customStarUI);
|
const customStarUi = useStore($customStarUI);
|
||||||
|
|
||||||
@ -115,7 +114,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
flushSync(() => {
|
flushSync(() => {
|
||||||
dispatch(setActiveTab('unifiedCanvas'));
|
dispatch(setActiveTab('unifiedCanvas'));
|
||||||
});
|
});
|
||||||
dispatch(setInitialCanvasImage(imageDTO));
|
dispatch(setInitialCanvasImage(imageDTO, optimalDimension));
|
||||||
|
|
||||||
toaster({
|
toaster({
|
||||||
title: t('toast.sentToUnifiedCanvas'),
|
title: t('toast.sentToUnifiedCanvas'),
|
||||||
@ -123,7 +122,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
duration: 2500,
|
duration: 2500,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
});
|
});
|
||||||
}, [dispatch, imageDTO, t, toaster]);
|
}, [dispatch, imageDTO, t, toaster, optimalDimension]);
|
||||||
|
|
||||||
const handleUseAllParameters = useCallback(() => {
|
const handleUseAllParameters = useCallback(() => {
|
||||||
recallAllParameters(metadata);
|
recallAllParameters(metadata);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import type { RootState } from 'app/store/store';
|
import type { RootState } from 'app/store/store';
|
||||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import type {
|
import type {
|
||||||
DenoiseLatentsInvocation,
|
DenoiseLatentsInvocation,
|
||||||
Edge,
|
Edge,
|
||||||
@ -66,26 +67,20 @@ function copyConnectionsToDenoiseLatentsHrf(graph: NonNullableGraph): void {
|
|||||||
* Adjusts the width and height to maintain the aspect ratio and constrains them by the model's dimension limits,
|
* Adjusts the width and height to maintain the aspect ratio and constrains them by the model's dimension limits,
|
||||||
* rounding down to the nearest multiple of 8.
|
* rounding down to the nearest multiple of 8.
|
||||||
*
|
*
|
||||||
* @param {string} baseModel The base model type, which determines the base dimension used in calculations.
|
* @param {number} optimalDimension The optimal dimension for the base model.
|
||||||
* @param {number} width The current width to be adjusted for HRF.
|
* @param {number} width The current width to be adjusted for HRF.
|
||||||
* @param {number} height The current height to be adjusted for HRF.
|
* @param {number} height The current height to be adjusted for HRF.
|
||||||
* @return {{newWidth: number, newHeight: number}} The new width and height, adjusted and rounded as needed.
|
* @return {{newWidth: number, newHeight: number}} The new width and height, adjusted and rounded as needed.
|
||||||
*/
|
*/
|
||||||
function calculateHrfRes(
|
function calculateHrfRes(
|
||||||
baseModel: string,
|
optimalDimension: number,
|
||||||
width: number,
|
width: number,
|
||||||
height: number
|
height: number
|
||||||
): { newWidth: number; newHeight: number } {
|
): { newWidth: number; newHeight: number } {
|
||||||
const aspect = width / height;
|
const aspect = width / height;
|
||||||
let dimension;
|
|
||||||
if (baseModel == 'sdxl') {
|
|
||||||
dimension = 1024;
|
|
||||||
} else {
|
|
||||||
dimension = 512;
|
|
||||||
}
|
|
||||||
|
|
||||||
const minDimension = Math.floor(dimension * 0.5);
|
const minDimension = Math.floor(optimalDimension * 0.5);
|
||||||
const modelArea = dimension * dimension; // Assuming square images for model_area
|
const modelArea = optimalDimension * optimalDimension; // Assuming square images for model_area
|
||||||
|
|
||||||
let initWidth;
|
let initWidth;
|
||||||
let initHeight;
|
let initHeight;
|
||||||
@ -126,11 +121,9 @@ export const addHrfToGraph = (
|
|||||||
const isAutoVae = !vae;
|
const isAutoVae = !vae;
|
||||||
const width = state.generation.width;
|
const width = state.generation.width;
|
||||||
const height = state.generation.height;
|
const height = state.generation.height;
|
||||||
const baseModel = state.generation.model
|
const optimalDimension = selectOptimalDimension(state);
|
||||||
? state.generation.model.base_model
|
|
||||||
: 'sd1';
|
|
||||||
const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(
|
const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(
|
||||||
baseModel,
|
optimalDimension,
|
||||||
width,
|
width,
|
||||||
height
|
height
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { stateSelector } from 'app/store/store';
|
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||||
@ -9,21 +8,15 @@ import {
|
|||||||
CANVAS_GRID_SIZE_FINE,
|
CANVAS_GRID_SIZE_FINE,
|
||||||
} from 'features/canvas/store/constants';
|
} from 'features/canvas/store/constants';
|
||||||
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
[stateSelector, isStagingSelector],
|
[selectOptimalDimension, isStagingSelector],
|
||||||
({ generation }, isStaging) => {
|
(optimalDimension, isStaging) => {
|
||||||
const { model } = generation;
|
|
||||||
const initial = ['sdxl', 'sdxl-refiner'].includes(
|
|
||||||
model?.base_model as string
|
|
||||||
)
|
|
||||||
? 1024
|
|
||||||
: 512;
|
|
||||||
return {
|
return {
|
||||||
initial,
|
initial: optimalDimension,
|
||||||
model,
|
|
||||||
isStaging,
|
isStaging,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { stateSelector } from 'app/store/store';
|
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||||
@ -9,21 +8,15 @@ import {
|
|||||||
CANVAS_GRID_SIZE_FINE,
|
CANVAS_GRID_SIZE_FINE,
|
||||||
} from 'features/canvas/store/constants';
|
} from 'features/canvas/store/constants';
|
||||||
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
[stateSelector, isStagingSelector],
|
[selectOptimalDimension, isStagingSelector],
|
||||||
({ generation }, isStaging) => {
|
(optimalDimension, isStaging) => {
|
||||||
const { model } = generation;
|
|
||||||
const initial = ['sdxl', 'sdxl-refiner'].includes(
|
|
||||||
model?.base_model as string
|
|
||||||
)
|
|
||||||
? 1024
|
|
||||||
: 512;
|
|
||||||
return {
|
return {
|
||||||
initial,
|
initial: optimalDimension,
|
||||||
model,
|
|
||||||
isStaging,
|
isStaging,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import type {
|
|||||||
} from 'common/components/InvSelect/types';
|
} from 'common/components/InvSelect/types';
|
||||||
import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
|
import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
|
||||||
import { isBoundingBoxScaleMethod } from 'features/canvas/store/canvasTypes';
|
import { isBoundingBoxScaleMethod } from 'features/canvas/store/canvasTypes';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -18,20 +19,20 @@ export const OPTIONS: InvSelectOption[] = [
|
|||||||
|
|
||||||
const ParamScaleBeforeProcessing = () => {
|
const ParamScaleBeforeProcessing = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const { t } = useTranslation();
|
||||||
const boundingBoxScaleMethod = useAppSelector(
|
const boundingBoxScaleMethod = useAppSelector(
|
||||||
(state) => state.canvas.boundingBoxScaleMethod
|
(state) => state.canvas.boundingBoxScaleMethod
|
||||||
);
|
);
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const onChange = useCallback<InvSelectOnChange>(
|
const onChange = useCallback<InvSelectOnChange>(
|
||||||
(v) => {
|
(v) => {
|
||||||
if (!isBoundingBoxScaleMethod(v?.value)) {
|
if (!isBoundingBoxScaleMethod(v?.value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dispatch(setBoundingBoxScaleMethod(v.value));
|
dispatch(setBoundingBoxScaleMethod(v.value, optimalDimension));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch, optimalDimension]
|
||||||
);
|
);
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
|
@ -5,18 +5,18 @@ import { InvControl } from 'common/components/InvControl/InvControl';
|
|||||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||||
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
[stateSelector],
|
[stateSelector, selectOptimalDimension],
|
||||||
({ generation, canvas }) => {
|
({ canvas }, optimalDimension) => {
|
||||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod, aspectRatio } =
|
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod, aspectRatio } =
|
||||||
canvas;
|
canvas;
|
||||||
const { model } = generation;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
model,
|
optimalDimension,
|
||||||
scaledBoundingBoxDimensions,
|
scaledBoundingBoxDimensions,
|
||||||
isManual: boundingBoxScaleMethod === 'manual',
|
isManual: boundingBoxScaleMethod === 'manual',
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
@ -26,12 +26,12 @@ const selector = createMemoizedSelector(
|
|||||||
|
|
||||||
const ParamScaledHeight = () => {
|
const ParamScaledHeight = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { model, isManual, scaledBoundingBoxDimensions, aspectRatio } =
|
const {
|
||||||
useAppSelector(selector);
|
isManual,
|
||||||
|
scaledBoundingBoxDimensions,
|
||||||
const initial = ['sdxl', 'sdxl-refiner'].includes(model?.base_model as string)
|
aspectRatio,
|
||||||
? 1024
|
optimalDimension,
|
||||||
: 512;
|
} = useAppSelector(selector);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ const ParamScaledHeight = () => {
|
|||||||
|
|
||||||
const handleResetScaledHeight = useCallback(() => {
|
const handleResetScaledHeight = useCallback(() => {
|
||||||
let resetWidth = scaledBoundingBoxDimensions.width;
|
let resetWidth = scaledBoundingBoxDimensions.width;
|
||||||
const resetHeight = Math.floor(initial);
|
const resetHeight = Math.floor(optimalDimension);
|
||||||
|
|
||||||
if (aspectRatio) {
|
if (aspectRatio) {
|
||||||
resetWidth = roundToMultiple(resetHeight * aspectRatio.value, 64);
|
resetWidth = roundToMultiple(resetHeight * aspectRatio.value, 64);
|
||||||
@ -68,7 +68,12 @@ const ParamScaledHeight = () => {
|
|||||||
height: resetHeight,
|
height: resetHeight,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}, [aspectRatio, dispatch, initial, scaledBoundingBoxDimensions.width]);
|
}, [
|
||||||
|
aspectRatio,
|
||||||
|
dispatch,
|
||||||
|
optimalDimension,
|
||||||
|
scaledBoundingBoxDimensions.width,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvControl isDisabled={!isManual} label={t('parameters.scaledHeight')}>
|
<InvControl isDisabled={!isManual} label={t('parameters.scaledHeight')}>
|
||||||
|
@ -5,18 +5,18 @@ import { InvControl } from 'common/components/InvControl/InvControl';
|
|||||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||||
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
[stateSelector],
|
[stateSelector, selectOptimalDimension],
|
||||||
({ canvas, generation }) => {
|
({ canvas }, optimalDimension) => {
|
||||||
const { boundingBoxScaleMethod, scaledBoundingBoxDimensions, aspectRatio } =
|
const { boundingBoxScaleMethod, scaledBoundingBoxDimensions, aspectRatio } =
|
||||||
canvas;
|
canvas;
|
||||||
const { model } = generation;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
model,
|
initial: optimalDimension,
|
||||||
scaledBoundingBoxDimensions,
|
scaledBoundingBoxDimensions,
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
isManual: boundingBoxScaleMethod === 'manual',
|
isManual: boundingBoxScaleMethod === 'manual',
|
||||||
@ -26,13 +26,9 @@ const selector = createMemoizedSelector(
|
|||||||
|
|
||||||
const ParamScaledWidth = () => {
|
const ParamScaledWidth = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { model, isManual, scaledBoundingBoxDimensions, aspectRatio } =
|
const { initial, isManual, scaledBoundingBoxDimensions, aspectRatio } =
|
||||||
useAppSelector(selector);
|
useAppSelector(selector);
|
||||||
|
|
||||||
const initial = ['sdxl', 'sdxl-refiner'].includes(model?.base_model as string)
|
|
||||||
? 1024
|
|
||||||
: 512;
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleChangeScaledWidth = useCallback(
|
const handleChangeScaledWidth = useCallback(
|
||||||
|
@ -5,23 +5,17 @@ import { InvControl } from 'common/components/InvControl/InvControl';
|
|||||||
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
|
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
|
||||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||||
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
[stateSelector],
|
[stateSelector, selectOptimalDimension],
|
||||||
({ generation, config }) => {
|
({ config }, optimalDimension) => {
|
||||||
const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.height;
|
const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.height;
|
||||||
const { model } = generation;
|
|
||||||
|
|
||||||
const initial = ['sdxl', 'sdxl-refiner'].includes(
|
|
||||||
model?.base_model as string
|
|
||||||
)
|
|
||||||
? 1024
|
|
||||||
: 512;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initial,
|
initial: optimalDimension,
|
||||||
min,
|
min,
|
||||||
max: sliderMax,
|
max: sliderMax,
|
||||||
inputMax,
|
inputMax,
|
||||||
|
@ -5,23 +5,17 @@ import { InvControl } from 'common/components/InvControl/InvControl';
|
|||||||
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
|
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
|
||||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||||
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
[stateSelector],
|
[stateSelector, selectOptimalDimension],
|
||||||
({ generation, config }) => {
|
({ config }, optimalDimension) => {
|
||||||
const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.width;
|
const { min, sliderMax, inputMax, fineStep, coarseStep } = config.sd.width;
|
||||||
const { model } = generation;
|
|
||||||
|
|
||||||
const initial = ['sdxl', 'sdxl-refiner'].includes(
|
|
||||||
model?.base_model as string
|
|
||||||
)
|
|
||||||
? 1024
|
|
||||||
: 512;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initial,
|
initial: optimalDimension,
|
||||||
min,
|
min,
|
||||||
max: sliderMax,
|
max: sliderMax,
|
||||||
step: coarseStep,
|
step: coarseStep,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
import {
|
import {
|
||||||
@ -8,6 +9,7 @@ import type {
|
|||||||
AspectRatioID,
|
AspectRatioID,
|
||||||
AspectRatioState,
|
AspectRatioState,
|
||||||
} from 'features/parameters/components/ImageSize/types';
|
} from 'features/parameters/components/ImageSize/types';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { createContext, useCallback, useContext, useMemo } from 'react';
|
import { createContext, useCallback, useContext, useMemo } from 'react';
|
||||||
|
|
||||||
export type ImageSizeContextInnerValue = {
|
export type ImageSizeContextInnerValue = {
|
||||||
@ -28,7 +30,7 @@ export type ImageSizeContext = {
|
|||||||
widthChanged: (width: number) => void;
|
widthChanged: (width: number) => void;
|
||||||
heightChanged: (height: number) => void;
|
heightChanged: (height: number) => void;
|
||||||
isLockedToggled: () => void;
|
isLockedToggled: () => void;
|
||||||
sizeReset: (width: number, height: number) => void;
|
setOptimalSize: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ImageSizeContext =
|
export const ImageSizeContext =
|
||||||
@ -36,6 +38,7 @@ export const ImageSizeContext =
|
|||||||
|
|
||||||
export const useImageSizeContext = (): ImageSizeContext => {
|
export const useImageSizeContext = (): ImageSizeContext => {
|
||||||
const _ctx = useContext(ImageSizeContext);
|
const _ctx = useContext(ImageSizeContext);
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
|
|
||||||
if (!_ctx) {
|
if (!_ctx) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -58,8 +61,7 @@ export const useImageSizeContext = (): ImageSizeContext => {
|
|||||||
state.value = ASPECT_RATIO_MAP[state.id].ratio;
|
state.value = ASPECT_RATIO_MAP[state.id].ratio;
|
||||||
const { width, height } = calculateNewSize(
|
const { width, height } = calculateNewSize(
|
||||||
state.value,
|
state.value,
|
||||||
_ctx.width,
|
_ctx.width * _ctx.height
|
||||||
_ctx.height
|
|
||||||
);
|
);
|
||||||
_ctx.onChangeWidth(width);
|
_ctx.onChangeWidth(width);
|
||||||
_ctx.onChangeHeight(height);
|
_ctx.onChangeHeight(height);
|
||||||
@ -84,8 +86,7 @@ export const useImageSizeContext = (): ImageSizeContext => {
|
|||||||
// Else we need to calculate the new size
|
// Else we need to calculate the new size
|
||||||
const { width, height } = calculateNewSize(
|
const { width, height } = calculateNewSize(
|
||||||
state.value,
|
state.value,
|
||||||
_ctx.width,
|
_ctx.width * _ctx.height
|
||||||
_ctx.height
|
|
||||||
);
|
);
|
||||||
_ctx.onChangeWidth(width);
|
_ctx.onChangeWidth(width);
|
||||||
_ctx.onChangeHeight(height);
|
_ctx.onChangeHeight(height);
|
||||||
@ -141,15 +142,20 @@ export const useImageSizeContext = (): ImageSizeContext => {
|
|||||||
_ctx.onChangeAspectRatioState(state);
|
_ctx.onChangeAspectRatioState(state);
|
||||||
}, [_ctx]);
|
}, [_ctx]);
|
||||||
|
|
||||||
const sizeReset = useCallback(
|
const setOptimalSize = useCallback(() => {
|
||||||
(width: number, height: number) => {
|
if (_ctx.aspectRatioState.isLocked) {
|
||||||
const state = { ...initialAspectRatioState };
|
const { width, height } = calculateNewSize(
|
||||||
_ctx.onChangeAspectRatioState(state);
|
_ctx.aspectRatioState.value,
|
||||||
|
optimalDimension * optimalDimension
|
||||||
|
);
|
||||||
_ctx.onChangeWidth(width);
|
_ctx.onChangeWidth(width);
|
||||||
_ctx.onChangeHeight(height);
|
_ctx.onChangeHeight(height);
|
||||||
},
|
} else {
|
||||||
[_ctx]
|
_ctx.onChangeAspectRatioState({ ...initialAspectRatioState });
|
||||||
);
|
_ctx.onChangeWidth(optimalDimension);
|
||||||
|
_ctx.onChangeHeight(optimalDimension);
|
||||||
|
}
|
||||||
|
}, [_ctx, optimalDimension]);
|
||||||
|
|
||||||
const ctx = useMemo(
|
const ctx = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@ -159,7 +165,7 @@ export const useImageSizeContext = (): ImageSizeContext => {
|
|||||||
widthChanged,
|
widthChanged,
|
||||||
heightChanged,
|
heightChanged,
|
||||||
isLockedToggled,
|
isLockedToggled,
|
||||||
sizeReset,
|
setOptimalSize,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
_ctx,
|
_ctx,
|
||||||
@ -167,7 +173,7 @@ export const useImageSizeContext = (): ImageSizeContext => {
|
|||||||
dimensionsSwapped,
|
dimensionsSwapped,
|
||||||
heightChanged,
|
heightChanged,
|
||||||
isLockedToggled,
|
isLockedToggled,
|
||||||
sizeReset,
|
setOptimalSize,
|
||||||
widthChanged,
|
widthChanged,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -13,6 +13,7 @@ export const LockAspectRatioButton = memo(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InvIconButton
|
<InvIconButton
|
||||||
|
tooltip={t('parameters.lockAspectRatio')}
|
||||||
aria-label={t('parameters.lockAspectRatio')}
|
aria-label={t('parameters.lockAspectRatio')}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
variant={ctx.aspectRatioState.isLocked ? 'outline' : 'ghost'}
|
variant={ctx.aspectRatioState.isLocked ? 'outline' : 'ghost'}
|
||||||
|
@ -1,27 +1,49 @@
|
|||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
||||||
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
import { useImageSizeContext } from 'features/parameters/components/ImageSize/ImageSizeContext';
|
||||||
import { memo, useCallback } from 'react';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
|
import {
|
||||||
|
getIsSizeTooLarge,
|
||||||
|
getIsSizeTooSmall,
|
||||||
|
} from 'features/parameters/util/optimalDimension';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { IoSparkles } from 'react-icons/io5';
|
import { IoSparkles } from 'react-icons/io5';
|
||||||
|
|
||||||
export const SetOptimalSizeButton = memo(() => {
|
export const SetOptimalSizeButton = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const ctx = useImageSizeContext();
|
const ctx = useImageSizeContext();
|
||||||
const optimalDimension = useAppSelector((state) =>
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
state.generation.model?.base_model === 'sdxl' ? 1024 : 512
|
const isSizeTooSmall = useMemo(
|
||||||
|
() => getIsSizeTooSmall(ctx.width, ctx.height, optimalDimension),
|
||||||
|
[ctx.height, ctx.width, optimalDimension]
|
||||||
|
);
|
||||||
|
const isSizeTooLarge = useMemo(
|
||||||
|
() => getIsSizeTooLarge(ctx.width, ctx.height, optimalDimension),
|
||||||
|
[ctx.height, ctx.width, optimalDimension]
|
||||||
);
|
);
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
ctx.sizeReset(optimalDimension, optimalDimension);
|
ctx.setOptimalSize();
|
||||||
}, [ctx, optimalDimension]);
|
}, [ctx]);
|
||||||
|
const tooltip = useMemo(() => {
|
||||||
|
if (isSizeTooSmall) {
|
||||||
|
return t('parameters.setToOptimalSizeTooSmall');
|
||||||
|
}
|
||||||
|
if (isSizeTooLarge) {
|
||||||
|
return t('parameters.setToOptimalSizeTooLarge');
|
||||||
|
}
|
||||||
|
return t('parameters.setToOptimalSize');
|
||||||
|
}, [isSizeTooLarge, isSizeTooSmall, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvIconButton
|
<InvIconButton
|
||||||
aria-label={t('parameters.lockAspectRatio')}
|
tooltip={tooltip}
|
||||||
|
aria-label={t('parameters.setToOptimalSize')}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
icon={<IoSparkles />}
|
icon={<IoSparkles />}
|
||||||
|
colorScheme={isSizeTooSmall || isSizeTooLarge ? 'warning' : 'base'}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -12,6 +12,7 @@ export const SwapDimensionsButton = memo(() => {
|
|||||||
}, [ctx]);
|
}, [ctx]);
|
||||||
return (
|
return (
|
||||||
<InvIconButton
|
<InvIconButton
|
||||||
|
tooltip={t('parameters.swapDimensions')}
|
||||||
aria-label={t('parameters.swapDimensions')}
|
aria-label={t('parameters.swapDimensions')}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
@ -3,17 +3,18 @@ import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
|||||||
/**
|
/**
|
||||||
* Calculate the new width and height that will fit the given aspect ratio, retaining the input area
|
* Calculate the new width and height that will fit the given aspect ratio, retaining the input area
|
||||||
* @param ratio The aspect ratio to calculate the new size for
|
* @param ratio The aspect ratio to calculate the new size for
|
||||||
* @param width The input width
|
* @param area The input area
|
||||||
* @param height The input height
|
|
||||||
* @returns The width and height that will fit the given aspect ratio, retaining the input area
|
* @returns The width and height that will fit the given aspect ratio, retaining the input area
|
||||||
*/
|
*/
|
||||||
export const calculateNewSize = (
|
export const calculateNewSize = (
|
||||||
ratio: number,
|
ratio: number,
|
||||||
width: number,
|
area: number
|
||||||
height: number
|
|
||||||
): { width: number; height: number } => {
|
): { width: number; height: number } => {
|
||||||
const area = width * height;
|
const exactWidth = Math.sqrt(area * ratio);
|
||||||
const newWidth = roundToMultiple(Math.sqrt(area * ratio), 8);
|
const exactHeight = exactWidth / ratio;
|
||||||
const newHeight = roundToMultiple(area / newWidth, 8);
|
|
||||||
return { width: newWidth, height: newHeight };
|
return {
|
||||||
|
width: roundToMultiple(exactWidth, 8),
|
||||||
|
height: roundToMultiple(exactHeight, 8),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { skipToken } from '@reduxjs/toolkit/query';
|
import { skipToken } from '@reduxjs/toolkit/query';
|
||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
@ -18,8 +19,8 @@ export const usePreselectedImage = (selectedImage?: {
|
|||||||
action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters';
|
action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters';
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { recallAllParameters } = useRecallParameters();
|
const { recallAllParameters } = useRecallParameters();
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
const toaster = useAppToaster();
|
const toaster = useAppToaster();
|
||||||
|
|
||||||
const { currentData: selectedImageDto } = useGetImageDTOQuery(
|
const { currentData: selectedImageDto } = useGetImageDTOQuery(
|
||||||
@ -32,7 +33,7 @@ export const usePreselectedImage = (selectedImage?: {
|
|||||||
|
|
||||||
const handleSendToCanvas = useCallback(() => {
|
const handleSendToCanvas = useCallback(() => {
|
||||||
if (selectedImageDto) {
|
if (selectedImageDto) {
|
||||||
dispatch(setInitialCanvasImage(selectedImageDto));
|
dispatch(setInitialCanvasImage(selectedImageDto, optimalDimension));
|
||||||
dispatch(setActiveTab('unifiedCanvas'));
|
dispatch(setActiveTab('unifiedCanvas'));
|
||||||
toaster({
|
toaster({
|
||||||
title: t('toast.sentToUnifiedCanvas'),
|
title: t('toast.sentToUnifiedCanvas'),
|
||||||
@ -41,7 +42,7 @@ export const usePreselectedImage = (selectedImage?: {
|
|||||||
isClosable: true,
|
isClosable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [dispatch, toaster, selectedImageDto]);
|
}, [selectedImageDto, dispatch, optimalDimension, toaster]);
|
||||||
|
|
||||||
const handleSendToImg2Img = useCallback(() => {
|
const handleSendToImg2Img = useCallback(() => {
|
||||||
if (selectedImageDto) {
|
if (selectedImageDto) {
|
||||||
|
@ -2,6 +2,7 @@ import type { PayloadAction } from '@reduxjs/toolkit';
|
|||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||||
import { isAnyControlAdapterAdded } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { isAnyControlAdapterAdded } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
|
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
|
||||||
@ -16,6 +17,10 @@ import type {
|
|||||||
ParameterVAEModel,
|
ParameterVAEModel,
|
||||||
} from 'features/parameters/types/parameterSchemas';
|
} from 'features/parameters/types/parameterSchemas';
|
||||||
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
||||||
|
import {
|
||||||
|
getIsSizeOptimal,
|
||||||
|
getOptimalDimension,
|
||||||
|
} from 'features/parameters/util/optimalDimension';
|
||||||
import { configChanged } from 'features/system/store/configSlice';
|
import { configChanged } from 'features/system/store/configSlice';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
@ -198,15 +203,26 @@ export const generationSlice = createSlice({
|
|||||||
state.initialImage = { imageName: image_name, width, height };
|
state.initialImage = { imageName: image_name, width, height };
|
||||||
},
|
},
|
||||||
modelChanged: (state, action: PayloadAction<ParameterModel | null>) => {
|
modelChanged: (state, action: PayloadAction<ParameterModel | null>) => {
|
||||||
state.model = action.payload;
|
const newModel = action.payload;
|
||||||
|
state.model = newModel;
|
||||||
|
|
||||||
if (state.model === null) {
|
if (newModel === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp ClipSkip Based On Selected Model
|
// Clamp ClipSkip Based On Selected Model
|
||||||
const { maxClip } = CLIP_SKIP_MAP[state.model.base_model];
|
const { maxClip } = CLIP_SKIP_MAP[newModel.base_model];
|
||||||
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
|
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
|
||||||
|
const optimalDimension = getOptimalDimension(newModel);
|
||||||
|
if (getIsSizeOptimal(state.width, state.height, optimalDimension)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { width, height } = calculateNewSize(
|
||||||
|
state.aspectRatio.value,
|
||||||
|
optimalDimension * optimalDimension
|
||||||
|
);
|
||||||
|
state.width = width;
|
||||||
|
state.height = height;
|
||||||
},
|
},
|
||||||
vaeSelected: (state, action: PayloadAction<ParameterVAEModel | null>) => {
|
vaeSelected: (state, action: PayloadAction<ParameterVAEModel | null>) => {
|
||||||
// null is a valid VAE!
|
// null is a valid VAE!
|
||||||
@ -259,6 +275,9 @@ export const generationSlice = createSlice({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
selectors: {
|
||||||
|
selectOptimalDimension: (slice) => getOptimalDimension(slice.model),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
@ -306,4 +325,6 @@ export const {
|
|||||||
heightChanged,
|
heightChanged,
|
||||||
} = generationSlice.actions;
|
} = generationSlice.actions;
|
||||||
|
|
||||||
|
export const { selectOptimalDimension } = generationSlice.selectors;
|
||||||
|
|
||||||
export default generationSlice.reducer;
|
export default generationSlice.reducer;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
import type {
|
import type {
|
||||||
ParameterCanvasCoherenceMode,
|
ParameterCanvasCoherenceMode,
|
||||||
@ -58,3 +59,9 @@ export interface GenerationState {
|
|||||||
shouldShowAdvancedOptions: boolean;
|
shouldShowAdvancedOptions: boolean;
|
||||||
aspectRatio: AspectRatioState;
|
aspectRatio: AspectRatioState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PayloadActionWithOptimalDimension<T = void> = PayloadAction<
|
||||||
|
T,
|
||||||
|
string,
|
||||||
|
{ optimalDimension: number }
|
||||||
|
>;
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
import type { ModelIdentifier } from 'features/nodes/types/common';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the optimal dimension for a givel model, based on the model's base_model
|
||||||
|
* @param model The model identifier
|
||||||
|
* @returns The optimal dimension for the model
|
||||||
|
*/
|
||||||
|
export const getOptimalDimension = (model?: ModelIdentifier | null): number =>
|
||||||
|
model?.base_model === 'sdxl' ? 1024 : 512;
|
||||||
|
|
||||||
|
const MIN_AREA_FACTOR = 0.8;
|
||||||
|
const MAX_AREA_FACTOR = 1.2;
|
||||||
|
|
||||||
|
export const getIsSizeTooSmall = (
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
optimalDimension: number
|
||||||
|
): boolean => {
|
||||||
|
const currentArea = width * height;
|
||||||
|
const optimalArea = optimalDimension * optimalDimension;
|
||||||
|
if (currentArea < optimalArea * MIN_AREA_FACTOR) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getIsSizeTooLarge = (
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
optimalDimension: number
|
||||||
|
): boolean => {
|
||||||
|
const currentArea = width * height;
|
||||||
|
const optimalArea = optimalDimension * optimalDimension;
|
||||||
|
if (currentArea > optimalArea * MAX_AREA_FACTOR) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the current width and height needs to be resized to the optimal dimension.
|
||||||
|
* The current width and height needs to be resized if the current area is not within 20% of the optimal area.
|
||||||
|
* @param width The width to compare with the optimal dimension
|
||||||
|
* @param height The height to compare with the optimal dimension
|
||||||
|
* @param optimalDimension The optimal dimension
|
||||||
|
* @returns Whether the current width and height needs to be resized to the optimal dimension
|
||||||
|
*/
|
||||||
|
export const getIsSizeOptimal = (
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
optimalDimension: number
|
||||||
|
): boolean => {
|
||||||
|
return (
|
||||||
|
!getIsSizeTooSmall(width, height, optimalDimension) &&
|
||||||
|
!getIsSizeTooLarge(width, height, optimalDimension)
|
||||||
|
);
|
||||||
|
};
|
@ -7,6 +7,7 @@ import ParamBoundingBoxHeight from 'features/parameters/components/Canvas/Boundi
|
|||||||
import ParamBoundingBoxWidth from 'features/parameters/components/Canvas/BoundingBox/ParamBoundingBoxWidth';
|
import ParamBoundingBoxWidth from 'features/parameters/components/Canvas/BoundingBox/ParamBoundingBoxWidth';
|
||||||
import { ImageSize } from 'features/parameters/components/ImageSize/ImageSize';
|
import { ImageSize } from 'features/parameters/components/ImageSize/ImageSize';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
export const ImageSizeCanvas = memo(() => {
|
export const ImageSizeCanvas = memo(() => {
|
||||||
@ -15,19 +16,20 @@ export const ImageSizeCanvas = memo(() => {
|
|||||||
(state) => state.canvas.boundingBoxDimensions
|
(state) => state.canvas.boundingBoxDimensions
|
||||||
);
|
);
|
||||||
const aspectRatioState = useAppSelector((state) => state.canvas.aspectRatio);
|
const aspectRatioState = useAppSelector((state) => state.canvas.aspectRatio);
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
|
|
||||||
const onChangeWidth = useCallback(
|
const onChangeWidth = useCallback(
|
||||||
(width: number) => {
|
(width: number) => {
|
||||||
dispatch(setBoundingBoxDimensions({ width }));
|
dispatch(setBoundingBoxDimensions({ width }, optimalDimension));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch, optimalDimension]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeHeight = useCallback(
|
const onChangeHeight = useCallback(
|
||||||
(height: number) => {
|
(height: number) => {
|
||||||
dispatch(setBoundingBoxDimensions({ height }));
|
dispatch(setBoundingBoxDimensions({ height }, optimalDimension));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch, optimalDimension]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeAspectRatioState = useCallback(
|
const onChangeAspectRatioState = useCallback(
|
||||||
|
@ -27,10 +27,7 @@ import {
|
|||||||
shouldUseNSFWCheckerChanged,
|
shouldUseNSFWCheckerChanged,
|
||||||
shouldUseWatermarkerChanged,
|
shouldUseWatermarkerChanged,
|
||||||
} from 'features/system/store/systemSlice';
|
} from 'features/system/store/systemSlice';
|
||||||
import {
|
import { setShouldShowProgressInViewer } from 'features/ui/store/uiSlice';
|
||||||
setShouldAutoChangeDimensions,
|
|
||||||
setShouldShowProgressInViewer,
|
|
||||||
} from 'features/ui/store/uiSlice';
|
|
||||||
import type { ChangeEvent, ReactElement } from 'react';
|
import type { ChangeEvent, ReactElement } from 'react';
|
||||||
import { cloneElement, memo, useCallback, useEffect, useState } from 'react';
|
import { cloneElement, memo, useCallback, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -54,7 +51,7 @@ const selector = createMemoizedSelector(
|
|||||||
shouldEnableInformationalPopovers,
|
shouldEnableInformationalPopovers,
|
||||||
} = system;
|
} = system;
|
||||||
const { shouldUseCpuNoise } = generation;
|
const { shouldUseCpuNoise } = generation;
|
||||||
const { shouldShowProgressInViewer, shouldAutoChangeDimensions } = ui;
|
const { shouldShowProgressInViewer } = ui;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shouldUseCpuNoise,
|
shouldUseCpuNoise,
|
||||||
@ -65,7 +62,6 @@ const selector = createMemoizedSelector(
|
|||||||
shouldAntialiasProgressImage,
|
shouldAntialiasProgressImage,
|
||||||
shouldUseNSFWChecker,
|
shouldUseNSFWChecker,
|
||||||
shouldUseWatermarker,
|
shouldUseWatermarker,
|
||||||
shouldAutoChangeDimensions,
|
|
||||||
shouldEnableInformationalPopovers,
|
shouldEnableInformationalPopovers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -135,7 +131,6 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
|||||||
shouldAntialiasProgressImage,
|
shouldAntialiasProgressImage,
|
||||||
shouldUseNSFWChecker,
|
shouldUseNSFWChecker,
|
||||||
shouldUseWatermarker,
|
shouldUseWatermarker,
|
||||||
shouldAutoChangeDimensions,
|
|
||||||
shouldEnableInformationalPopovers,
|
shouldEnableInformationalPopovers,
|
||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
|
|
||||||
@ -191,12 +186,6 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
|||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
const handleChangeShouldAutoChangeDimensions = useCallback(
|
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
dispatch(setShouldAutoChangeDimensions(e.target.checked));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
const handleChangeShouldEnableInformationalPopovers = useCallback(
|
const handleChangeShouldEnableInformationalPopovers = useCallback(
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
dispatch(setShouldEnableInformationalPopovers(e.target.checked));
|
dispatch(setShouldEnableInformationalPopovers(e.target.checked));
|
||||||
@ -280,12 +269,6 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
|||||||
onChange={handleChangeShouldAntialiasProgressImage}
|
onChange={handleChangeShouldAntialiasProgressImage}
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('settings.autoChangeDimensions')}>
|
|
||||||
<InvSwitch
|
|
||||||
isChecked={shouldAutoChangeDimensions}
|
|
||||||
onChange={handleChangeShouldAutoChangeDimensions}
|
|
||||||
/>
|
|
||||||
</InvControl>
|
|
||||||
<InvControl
|
<InvControl
|
||||||
label={t('parameters.useCpuNoise')}
|
label={t('parameters.useCpuNoise')}
|
||||||
feature="noiseUseCPU"
|
feature="noiseUseCPU"
|
||||||
|
@ -11,7 +11,6 @@ export const initialUIState: UIState = {
|
|||||||
shouldShowExistingModelsInSearch: false,
|
shouldShowExistingModelsInSearch: false,
|
||||||
shouldHidePreview: false,
|
shouldHidePreview: false,
|
||||||
shouldShowProgressInViewer: true,
|
shouldShowProgressInViewer: true,
|
||||||
shouldAutoChangeDimensions: false,
|
|
||||||
panels: {},
|
panels: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,9 +36,6 @@ export const uiSlice = createSlice({
|
|||||||
setShouldShowProgressInViewer: (state, action: PayloadAction<boolean>) => {
|
setShouldShowProgressInViewer: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldShowProgressInViewer = action.payload;
|
state.shouldShowProgressInViewer = action.payload;
|
||||||
},
|
},
|
||||||
setShouldAutoChangeDimensions: (state, action: PayloadAction<boolean>) => {
|
|
||||||
state.shouldAutoChangeDimensions = action.payload;
|
|
||||||
},
|
|
||||||
panelsChanged: (
|
panelsChanged: (
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<{ name: string; value: string }>
|
action: PayloadAction<{ name: string; value: string }>
|
||||||
@ -60,7 +56,6 @@ export const {
|
|||||||
setShouldShowExistingModelsInSearch,
|
setShouldShowExistingModelsInSearch,
|
||||||
setShouldHidePreview,
|
setShouldHidePreview,
|
||||||
setShouldShowProgressInViewer,
|
setShouldShowProgressInViewer,
|
||||||
setShouldAutoChangeDimensions,
|
|
||||||
panelsChanged,
|
panelsChanged,
|
||||||
} = uiSlice.actions;
|
} = uiSlice.actions;
|
||||||
|
|
||||||
|
@ -1,23 +1,10 @@
|
|||||||
import type { InvokeTabName } from './tabMap';
|
import type { InvokeTabName } from './tabMap';
|
||||||
|
|
||||||
export type Coordinates = {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Dimensions = {
|
|
||||||
width: number | string;
|
|
||||||
height: number | string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Rect = Coordinates & Dimensions;
|
|
||||||
|
|
||||||
export interface UIState {
|
export interface UIState {
|
||||||
activeTab: InvokeTabName;
|
activeTab: InvokeTabName;
|
||||||
shouldShowImageDetails: boolean;
|
shouldShowImageDetails: boolean;
|
||||||
shouldShowExistingModelsInSearch: boolean;
|
shouldShowExistingModelsInSearch: boolean;
|
||||||
shouldHidePreview: boolean;
|
shouldHidePreview: boolean;
|
||||||
shouldShowProgressInViewer: boolean;
|
shouldShowProgressInViewer: boolean;
|
||||||
shouldAutoChangeDimensions: boolean;
|
|
||||||
panels: Record<string, string>;
|
panels: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user