feat(ui): use config for all numerical params

Centralize the initial/min/max/etc values for all numerical params. We used this for some but at some point stopped updating it.

All numerical params now use their respective configs. Far fewer hardcoded values throughout the app now.

Also updated the config types a bit to better accommodate slider vs number input constraints.
This commit is contained in:
psychedelicious 2024-01-07 13:21:19 +11:00
parent 6024fc7baf
commit 3428ea1b3c
29 changed files with 530 additions and 445 deletions

View File

@ -43,6 +43,16 @@ export type SDFeature =
| 'vae' | 'vae'
| 'hrf'; | 'hrf';
export type NumericalParameterConfig = {
initial: number;
sliderMin: number;
sliderMax: number;
numberInputMin: number;
numberInputMax: number;
fineStep: number;
coarseStep: number;
};
/** /**
* Configuration options for the InvokeAI UI. * Configuration options for the InvokeAI UI.
* Distinct from system settings which may be changed inside the app. * Distinct from system settings which may be changed inside the app.
@ -66,69 +76,32 @@ export type AppConfig = {
defaultModel?: string; defaultModel?: string;
disabledControlNetModels: string[]; disabledControlNetModels: string[];
disabledControlNetProcessors: (keyof typeof CONTROLNET_PROCESSORS)[]; disabledControlNetProcessors: (keyof typeof CONTROLNET_PROCESSORS)[];
iterations: { // Core parameters
initial: number; iterations: NumericalParameterConfig;
min: number; width: NumericalParameterConfig; // initial value comes from model
sliderMax: number; height: NumericalParameterConfig; // initial value comes from model
inputMax: number; steps: NumericalParameterConfig;
fineStep: number; guidance: NumericalParameterConfig;
coarseStep: number; cfgRescaleMultiplier: NumericalParameterConfig;
}; img2imgStrength: NumericalParameterConfig;
width: { // Canvas
initial: number; boundingBoxHeight: NumericalParameterConfig; // initial value comes from model
min: number; boundingBoxWidth: NumericalParameterConfig; // initial value comes from model
sliderMax: number; scaledBoundingBoxHeight: NumericalParameterConfig; // initial value comes from model
inputMax: number; scaledBoundingBoxWidth: NumericalParameterConfig; // initial value comes from model
fineStep: number; canvasCoherenceStrength: NumericalParameterConfig;
coarseStep: number; canvasCoherenceSteps: NumericalParameterConfig;
}; infillTileSize: NumericalParameterConfig;
height: { infillPatchmatchDownscaleSize: NumericalParameterConfig;
initial: number; // Misc advanced
min: number; clipSkip: NumericalParameterConfig; // slider and input max are ignored for this, because the values depend on the model
sliderMax: number; maskBlur: NumericalParameterConfig;
inputMax: number; hrfStrength: NumericalParameterConfig;
fineStep: number;
coarseStep: number;
};
steps: {
initial: number;
min: number;
sliderMax: number;
inputMax: number;
fineStep: number;
coarseStep: number;
};
guidance: {
initial: number;
min: number;
sliderMax: number;
inputMax: number;
fineStep: number;
coarseStep: number;
};
img2imgStrength: {
initial: number;
min: number;
sliderMax: number;
inputMax: number;
fineStep: number;
coarseStep: number;
};
hrfStrength: {
initial: number;
min: number;
sliderMax: number;
inputMax: number;
fineStep: number;
coarseStep: number;
};
dynamicPrompts: { dynamicPrompts: {
maxPrompts: { maxPrompts: NumericalParameterConfig;
initial: number; };
min: number; ca: {
sliderMax: number; weight: NumericalParameterConfig;
inputMax: number;
};
}; };
}; };
}; };

View File

@ -1,4 +1,4 @@
import { useAppDispatch } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl'; import { InvControl } from 'common/components/InvControl/InvControl';
import { InvControlGroup } from 'common/components/InvControl/InvControlGroup'; import { InvControlGroup } from 'common/components/InvControl/InvControlGroup';
import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput';
@ -17,10 +17,22 @@ type ParamControlAdapterWeightProps = {
const formatValue = (v: number) => v.toFixed(2); const formatValue = (v: number) => v.toFixed(2);
const ParamControlAdapterWeight = ({ id }: ParamControlAdapterWeightProps) => { const ParamControlAdapterWeight = ({ id }: ParamControlAdapterWeightProps) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const isEnabled = useControlAdapterIsEnabled(id); const isEnabled = useControlAdapterIsEnabled(id);
const weight = useControlAdapterWeight(id); const weight = useControlAdapterWeight(id);
const dispatch = useAppDispatch(); const initial = useAppSelector((s) => s.config.sd.ca.weight.initial);
const { t } = useTranslation(); const sliderMin = useAppSelector((s) => s.config.sd.ca.weight.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.ca.weight.sliderMax);
const numberInputMin = useAppSelector(
(s) => s.config.sd.ca.weight.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.ca.weight.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.ca.weight.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.ca.weight.fineStep);
const onChange = useCallback( const onChange = useCallback(
(weight: number) => { (weight: number) => {
dispatch(controlAdapterWeightChanged({ id, weight })); dispatch(controlAdapterWeightChanged({ id, weight }));
@ -43,23 +55,23 @@ const ParamControlAdapterWeight = ({ id }: ParamControlAdapterWeightProps) => {
<InvSlider <InvSlider
value={weight} value={weight}
onChange={onChange} onChange={onChange}
defaultValue={1} defaultValue={initial}
min={0} min={sliderMin}
max={2} max={sliderMax}
step={0.05} step={coarseStep}
fineStep={0.01} fineStep={fineStep}
marks={marks} marks={marks}
formatValue={formatValue} formatValue={formatValue}
/> />
<InvNumberInput <InvNumberInput
value={weight} value={weight}
onChange={onChange} onChange={onChange}
min={-1} min={numberInputMin}
max={2} max={numberInputMax}
step={0.05} step={coarseStep}
fineStep={0.01} fineStep={fineStep}
maxW={20} maxW={20}
defaultValue={1} defaultValue={initial}
/> />
</InvControl> </InvControl>
</InvControlGroup> </InvControlGroup>

View File

@ -7,12 +7,17 @@ import { useTranslation } from 'react-i18next';
const ParamDynamicPromptsMaxPrompts = () => { const ParamDynamicPromptsMaxPrompts = () => {
const maxPrompts = useAppSelector((s) => s.dynamicPrompts.maxPrompts); const maxPrompts = useAppSelector((s) => s.dynamicPrompts.maxPrompts);
const min = useAppSelector((s) => s.config.sd.dynamicPrompts.maxPrompts.min); const sliderMin = useAppSelector(
(s) => s.config.sd.dynamicPrompts.maxPrompts.sliderMin
);
const sliderMax = useAppSelector( const sliderMax = useAppSelector(
(s) => s.config.sd.dynamicPrompts.maxPrompts.sliderMax (s) => s.config.sd.dynamicPrompts.maxPrompts.sliderMax
); );
const inputMax = useAppSelector( const numberInputMin = useAppSelector(
(s) => s.config.sd.dynamicPrompts.maxPrompts.inputMax (s) => s.config.sd.dynamicPrompts.maxPrompts.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.dynamicPrompts.maxPrompts.numberInputMax
); );
const initial = useAppSelector( const initial = useAppSelector(
(s) => s.config.sd.dynamicPrompts.maxPrompts.initial (s) => s.config.sd.dynamicPrompts.maxPrompts.initial
@ -36,14 +41,15 @@ const ParamDynamicPromptsMaxPrompts = () => {
renderInfoPopoverInPortal={false} renderInfoPopoverInPortal={false}
> >
<InvSlider <InvSlider
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
value={maxPrompts} value={maxPrompts}
defaultValue={initial} defaultValue={initial}
onChange={handleChange} onChange={handleChange}
marks marks
withNumberInput withNumberInput
numberInputMax={inputMax} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
/> />
</InvControl> </InvControl>
); );

View File

@ -8,8 +8,14 @@ import { useTranslation } from 'react-i18next';
const ParamHrfStrength = () => { const ParamHrfStrength = () => {
const hrfStrength = useAppSelector((s) => s.hrf.hrfStrength); const hrfStrength = useAppSelector((s) => s.hrf.hrfStrength);
const initial = useAppSelector((s) => s.config.sd.hrfStrength.initial); const initial = useAppSelector((s) => s.config.sd.hrfStrength.initial);
const min = useAppSelector((s) => s.config.sd.hrfStrength.min); const sliderMin = useAppSelector((s) => s.config.sd.hrfStrength.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.hrfStrength.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.hrfStrength.sliderMax);
const numberInputMin = useAppSelector(
(s) => s.config.sd.hrfStrength.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.hrfStrength.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.hrfStrength.coarseStep); const coarseStep = useAppSelector((s) => s.config.sd.hrfStrength.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.hrfStrength.fineStep); const fineStep = useAppSelector((s) => s.config.sd.hrfStrength.fineStep);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -25,7 +31,7 @@ const ParamHrfStrength = () => {
return ( return (
<InvControl label={t('parameters.denoisingStrength')}> <InvControl label={t('parameters.denoisingStrength')}>
<InvSlider <InvSlider
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
step={coarseStep} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
@ -34,6 +40,8 @@ const ParamHrfStrength = () => {
onChange={onChange} onChange={onChange}
marks marks
withNumberInput withNumberInput
numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
/> />
</InvControl> </InvControl>
); );

View File

@ -9,6 +9,28 @@ const ParamCFGRescaleMultiplier = () => {
const cfgRescaleMultiplier = useAppSelector( const cfgRescaleMultiplier = useAppSelector(
(s) => s.generation.cfgRescaleMultiplier (s) => s.generation.cfgRescaleMultiplier
); );
const initial = useAppSelector(
(s) => s.config.sd.cfgRescaleMultiplier.initial
);
const sliderMin = useAppSelector(
(s) => s.config.sd.cfgRescaleMultiplier.sliderMin
);
const sliderMax = useAppSelector(
(s) => s.config.sd.cfgRescaleMultiplier.sliderMax
);
const numberInputMin = useAppSelector(
(s) => s.config.sd.cfgRescaleMultiplier.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.cfgRescaleMultiplier.numberInputMax
);
const coarseStep = useAppSelector(
(s) => s.config.sd.cfgRescaleMultiplier.coarseStep
);
const fineStep = useAppSelector(
(s) => s.config.sd.cfgRescaleMultiplier.fineStep
);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
@ -24,12 +46,14 @@ const ParamCFGRescaleMultiplier = () => {
> >
<InvSlider <InvSlider
value={cfgRescaleMultiplier} value={cfgRescaleMultiplier}
defaultValue={0} defaultValue={initial}
min={0} min={sliderMin}
max={0.99} max={sliderMax}
step={0.1} step={coarseStep}
fineStep={0.01} fineStep={fineStep}
onChange={handleChange} onChange={handleChange}
numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
withNumberInput withNumberInput
marks marks
/> />

View File

@ -8,7 +8,13 @@ import { useTranslation } from 'react-i18next';
const ParamClipSkip = () => { const ParamClipSkip = () => {
const clipSkip = useAppSelector((s) => s.generation.clipSkip); const clipSkip = useAppSelector((s) => s.generation.clipSkip);
const initial = useAppSelector((s) => s.config.sd.clipSkip.initial);
const sliderMin = useAppSelector((s) => s.config.sd.clipSkip.sliderMin);
const numberInputMin = useAppSelector(
(s) => s.config.sd.clipSkip.numberInputMin
);
const coarseStep = useAppSelector((s) => s.config.sd.clipSkip.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.clipSkip.fineStep);
const { model } = useAppSelector((s) => s.generation); const { model } = useAppSelector((s) => s.generation);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -43,12 +49,15 @@ const ParamClipSkip = () => {
<InvControl label={t('parameters.clipSkip')} feature="clipSkip"> <InvControl label={t('parameters.clipSkip')} feature="clipSkip">
<InvSlider <InvSlider
value={clipSkip} value={clipSkip}
defaultValue={0} defaultValue={initial}
min={0} min={sliderMin}
max={max} max={max}
step={1} step={coarseStep}
fineStep={fineStep}
onChange={handleClipSkipChange} onChange={handleClipSkipChange}
withNumberInput withNumberInput
numberInputMin={numberInputMin}
numberInputMax={max}
marks={sliderMarks} marks={sliderMarks}
/> />
</InvControl> </InvControl>

View File

@ -2,21 +2,34 @@ 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';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
CANVAS_GRID_SIZE_COARSE,
CANVAS_GRID_SIZE_FINE,
} 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 { 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 ParamBoundingBoxWidth = () => { const ParamBoundingBoxHeight = () => {
const isStaging = useAppSelector(isStagingSelector);
const initial = useAppSelector(selectOptimalDimension);
const ctx = useImageSizeContext();
const { t } = useTranslation(); const { t } = useTranslation();
const ctx = useImageSizeContext();
const isStaging = useAppSelector(isStagingSelector);
const optimalDimension = useAppSelector(selectOptimalDimension);
const sliderMin = useAppSelector(
(s) => s.config.sd.boundingBoxHeight.sliderMin
);
const sliderMax = useAppSelector(
(s) => s.config.sd.boundingBoxHeight.sliderMax
);
const numberInputMin = useAppSelector(
(s) => s.config.sd.boundingBoxHeight.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.boundingBoxHeight.numberInputMax
);
const coarseStep = useAppSelector(
(s) => s.config.sd.boundingBoxHeight.coarseStep
);
const fineStep = useAppSelector(
(s) => s.config.sd.boundingBoxHeight.fineStep
);
const onChange = useCallback( const onChange = useCallback(
(v: number) => { (v: number) => {
ctx.heightChanged(v); ctx.heightChanged(v);
@ -27,19 +40,20 @@ const ParamBoundingBoxWidth = () => {
return ( return (
<InvControl label={t('parameters.height')} isDisabled={isStaging}> <InvControl label={t('parameters.height')} isDisabled={isStaging}>
<InvSlider <InvSlider
min={64} min={sliderMin}
max={1536} max={sliderMax}
step={CANVAS_GRID_SIZE_COARSE} step={coarseStep}
fineStep={CANVAS_GRID_SIZE_FINE} fineStep={fineStep}
value={ctx.height} value={ctx.height}
defaultValue={initial} defaultValue={optimalDimension}
onChange={onChange} onChange={onChange}
marks marks
withNumberInput withNumberInput
numberInputMax={4096} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
/> />
</InvControl> </InvControl>
); );
}; };
export default memo(ParamBoundingBoxWidth); export default memo(ParamBoundingBoxHeight);

View File

@ -2,21 +2,32 @@ 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';
import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
import {
CANVAS_GRID_SIZE_COARSE,
CANVAS_GRID_SIZE_FINE,
} 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 { 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 ParamBoundingBoxWidth = () => { const ParamBoundingBoxWidth = () => {
const isStaging = useAppSelector(isStagingSelector);
const initial = useAppSelector(selectOptimalDimension);
const ctx = useImageSizeContext();
const { t } = useTranslation(); const { t } = useTranslation();
const ctx = useImageSizeContext();
const isStaging = useAppSelector(isStagingSelector);
const optimalDimension = useAppSelector(selectOptimalDimension);
const sliderMin = useAppSelector(
(s) => s.config.sd.boundingBoxWidth.sliderMin
);
const sliderMax = useAppSelector(
(s) => s.config.sd.boundingBoxWidth.sliderMax
);
const numberInputMin = useAppSelector(
(s) => s.config.sd.boundingBoxWidth.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.boundingBoxWidth.numberInputMax
);
const coarseStep = useAppSelector(
(s) => s.config.sd.boundingBoxWidth.coarseStep
);
const fineStep = useAppSelector((s) => s.config.sd.boundingBoxWidth.fineStep);
const onChange = useCallback( const onChange = useCallback(
(v: number) => { (v: number) => {
ctx.widthChanged(v); ctx.widthChanged(v);
@ -27,15 +38,16 @@ const ParamBoundingBoxWidth = () => {
return ( return (
<InvControl label={t('parameters.width')} isDisabled={isStaging}> <InvControl label={t('parameters.width')} isDisabled={isStaging}>
<InvSlider <InvSlider
min={64} min={sliderMin}
max={1536} max={sliderMax}
step={CANVAS_GRID_SIZE_COARSE} step={coarseStep}
fineStep={CANVAS_GRID_SIZE_FINE} fineStep={fineStep}
value={ctx.width} value={ctx.width}
defaultValue={initial} defaultValue={optimalDimension}
onChange={onChange} onChange={onChange}
withNumberInput withNumberInput
numberInputMax={4096} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
marks marks
/> />
</InvControl> </InvControl>

View File

@ -10,6 +10,28 @@ const ParamCanvasCoherenceSteps = () => {
const canvasCoherenceSteps = useAppSelector( const canvasCoherenceSteps = useAppSelector(
(s) => s.generation.canvasCoherenceSteps (s) => s.generation.canvasCoherenceSteps
); );
const initial = useAppSelector(
(s) => s.config.sd.canvasCoherenceSteps.initial
);
const sliderMin = useAppSelector(
(s) => s.config.sd.canvasCoherenceSteps.sliderMin
);
const sliderMax = useAppSelector(
(s) => s.config.sd.canvasCoherenceSteps.sliderMax
);
const numberInputMin = useAppSelector(
(s) => s.config.sd.canvasCoherenceSteps.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.canvasCoherenceSteps.numberInputMax
);
const coarseStep = useAppSelector(
(s) => s.config.sd.canvasCoherenceSteps.coarseStep
);
const fineStep = useAppSelector(
(s) => s.config.sd.canvasCoherenceSteps.fineStep
);
const { t } = useTranslation(); const { t } = useTranslation();
const handleChange = useCallback( const handleChange = useCallback(
@ -25,14 +47,16 @@ const ParamCanvasCoherenceSteps = () => {
feature="compositingCoherenceSteps" feature="compositingCoherenceSteps"
> >
<InvSlider <InvSlider
min={1} min={sliderMin}
max={100} max={sliderMax}
step={1} step={coarseStep}
fineStep={fineStep}
value={canvasCoherenceSteps} value={canvasCoherenceSteps}
defaultValue={20} defaultValue={initial}
onChange={handleChange} onChange={handleChange}
withNumberInput withNumberInput
numberInputMax={999} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
marks marks
/> />
</InvControl> </InvControl>

View File

@ -6,9 +6,20 @@ import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const ParamMaskBlur = () => { const ParamMaskBlur = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const maskBlur = useAppSelector((s) => s.generation.maskBlur); const maskBlur = useAppSelector((s) => s.generation.maskBlur);
const { t } = useTranslation(); const initial = useAppSelector((s) => s.config.sd.maskBlur.initial);
const sliderMin = useAppSelector((s) => s.config.sd.maskBlur.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.maskBlur.sliderMax);
const numberInputMin = useAppSelector(
(s) => s.config.sd.maskBlur.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.maskBlur.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.maskBlur.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.maskBlur.fineStep);
const handleChange = useCallback( const handleChange = useCallback(
(v: number) => { (v: number) => {
@ -20,14 +31,17 @@ const ParamMaskBlur = () => {
return ( return (
<InvControl label={t('parameters.maskBlur')} feature="compositingBlur"> <InvControl label={t('parameters.maskBlur')} feature="compositingBlur">
<InvSlider <InvSlider
min={0} min={sliderMin}
max={64} max={sliderMax}
value={maskBlur} value={maskBlur}
defaultValue={16} defaultValue={initial}
onChange={handleChange} onChange={handleChange}
marks marks
withNumberInput withNumberInput
numberInputMax={512} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
step={coarseStep}
fineStep={fineStep}
/> />
</InvControl> </InvControl>
); );

View File

@ -11,6 +11,27 @@ const ParamInfillPatchmatchDownscaleSize = () => {
const infillPatchmatchDownscaleSize = useAppSelector( const infillPatchmatchDownscaleSize = useAppSelector(
(s) => s.generation.infillPatchmatchDownscaleSize (s) => s.generation.infillPatchmatchDownscaleSize
); );
const initial = useAppSelector(
(s) => s.config.sd.infillPatchmatchDownscaleSize.initial
);
const sliderMin = useAppSelector(
(s) => s.config.sd.infillPatchmatchDownscaleSize.sliderMin
);
const sliderMax = useAppSelector(
(s) => s.config.sd.infillPatchmatchDownscaleSize.sliderMax
);
const numberInputMin = useAppSelector(
(s) => s.config.sd.infillPatchmatchDownscaleSize.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.infillPatchmatchDownscaleSize.numberInputMax
);
const coarseStep = useAppSelector(
(s) => s.config.sd.infillPatchmatchDownscaleSize.coarseStep
);
const fineStep = useAppSelector(
(s) => s.config.sd.infillPatchmatchDownscaleSize.fineStep
);
const { t } = useTranslation(); const { t } = useTranslation();
@ -27,12 +48,16 @@ const ParamInfillPatchmatchDownscaleSize = () => {
label={t('parameters.patchmatchDownScaleSize')} label={t('parameters.patchmatchDownScaleSize')}
> >
<InvSlider <InvSlider
min={1} min={sliderMin}
max={10} max={sliderMax}
step={coarseStep}
fineStep={fineStep}
value={infillPatchmatchDownscaleSize} value={infillPatchmatchDownscaleSize}
defaultValue={1} defaultValue={initial}
onChange={handleChange} onChange={handleChange}
withNumberInput withNumberInput
numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
marks marks
/> />
</InvControl> </InvControl>

View File

@ -8,6 +8,20 @@ import { useTranslation } from 'react-i18next';
const ParamInfillTileSize = () => { const ParamInfillTileSize = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const infillTileSize = useAppSelector((s) => s.generation.infillTileSize); const infillTileSize = useAppSelector((s) => s.generation.infillTileSize);
const initial = useAppSelector((s) => s.config.sd.infillTileSize.initial);
const sliderMin = useAppSelector((s) => s.config.sd.infillTileSize.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.infillTileSize.sliderMax);
const numberInputMin = useAppSelector(
(s) => s.config.sd.infillTileSize.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.infillTileSize.numberInputMax
);
const coarseStep = useAppSelector(
(s) => s.config.sd.infillTileSize.coarseStep
);
const fineStep = useAppSelector((s) => s.config.sd.infillTileSize.fineStep);
const infillMethod = useAppSelector((s) => s.generation.infillMethod); const infillMethod = useAppSelector((s) => s.generation.infillMethod);
const { t } = useTranslation(); const { t } = useTranslation();
@ -25,12 +39,15 @@ const ParamInfillTileSize = () => {
label={t('parameters.tileSize')} label={t('parameters.tileSize')}
> >
<InvSlider <InvSlider
min={16} min={sliderMin}
max={64} max={sliderMax}
numberInputMax={256} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
value={infillTileSize} value={infillTileSize}
defaultValue={32} defaultValue={initial}
onChange={handleChange} onChange={handleChange}
step={coarseStep}
fineStep={fineStep}
withNumberInput withNumberInput
marks marks
/> />

View File

@ -2,15 +2,12 @@ import { useAppDispatch, 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';
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import {
CANVAS_GRID_SIZE_COARSE,
CANVAS_GRID_SIZE_FINE,
} from 'features/canvas/store/constants';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice'; 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 ParamScaledHeight = () => { const ParamScaledHeight = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const optimalDimension = useAppSelector(selectOptimalDimension); const optimalDimension = useAppSelector(selectOptimalDimension);
const isManual = useAppSelector( const isManual = useAppSelector(
@ -19,8 +16,24 @@ const ParamScaledHeight = () => {
const height = useAppSelector( const height = useAppSelector(
(s) => s.canvas.scaledBoundingBoxDimensions.height (s) => s.canvas.scaledBoundingBoxDimensions.height
); );
const sliderMin = useAppSelector(
const { t } = useTranslation(); (s) => s.config.sd.scaledBoundingBoxHeight.sliderMin
);
const sliderMax = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxHeight.sliderMax
);
const numberInputMin = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxHeight.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxHeight.numberInputMax
);
const coarseStep = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxHeight.coarseStep
);
const fineStep = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxHeight.fineStep
);
const onChange = useCallback( const onChange = useCallback(
(height: number) => { (height: number) => {
@ -29,23 +42,20 @@ const ParamScaledHeight = () => {
[dispatch] [dispatch]
); );
const onReset = useCallback(() => {
dispatch(setScaledBoundingBoxDimensions({ height: optimalDimension }));
}, [dispatch, optimalDimension]);
return ( return (
<InvControl isDisabled={!isManual} label={t('parameters.scaledHeight')}> <InvControl isDisabled={!isManual} label={t('parameters.scaledHeight')}>
<InvSlider <InvSlider
min={64} min={sliderMin}
max={1536} max={sliderMax}
step={CANVAS_GRID_SIZE_COARSE} step={coarseStep}
fineStep={CANVAS_GRID_SIZE_FINE} fineStep={fineStep}
value={height} value={height}
onChange={onChange} onChange={onChange}
marks marks
withNumberInput withNumberInput
numberInputMax={4096} numberInputMin={numberInputMin}
onReset={onReset} numberInputMax={numberInputMax}
defaultValue={optimalDimension}
/> />
</InvControl> </InvControl>
); );

View File

@ -2,17 +2,13 @@ import { useAppDispatch, 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';
import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import {
CANVAS_GRID_SIZE_COARSE,
CANVAS_GRID_SIZE_FINE,
} from 'features/canvas/store/constants';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice'; 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 ParamScaledWidth = () => { const ParamScaledWidth = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch();
const optimalDimension = useAppSelector(selectOptimalDimension); const optimalDimension = useAppSelector(selectOptimalDimension);
const isManual = useAppSelector( const isManual = useAppSelector(
(s) => s.canvas.boundingBoxScaleMethod === 'manual' (s) => s.canvas.boundingBoxScaleMethod === 'manual'
@ -20,7 +16,24 @@ const ParamScaledWidth = () => {
const width = useAppSelector( const width = useAppSelector(
(s) => s.canvas.scaledBoundingBoxDimensions.width (s) => s.canvas.scaledBoundingBoxDimensions.width
); );
const sliderMin = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxWidth.sliderMin
);
const sliderMax = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxWidth.sliderMax
);
const numberInputMin = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxWidth.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxWidth.numberInputMax
);
const coarseStep = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxWidth.coarseStep
);
const fineStep = useAppSelector(
(s) => s.config.sd.scaledBoundingBoxWidth.fineStep
);
const onChange = useCallback( const onChange = useCallback(
(width: number) => { (width: number) => {
dispatch(setScaledBoundingBoxDimensions({ width })); dispatch(setScaledBoundingBoxDimensions({ width }));
@ -28,23 +41,20 @@ const ParamScaledWidth = () => {
[dispatch] [dispatch]
); );
const onReset = useCallback(() => {
dispatch(setScaledBoundingBoxDimensions({ width: optimalDimension }));
}, [dispatch, optimalDimension]);
return ( return (
<InvControl isDisabled={!isManual} label={t('parameters.scaledWidth')}> <InvControl isDisabled={!isManual} label={t('parameters.scaledWidth')}>
<InvSlider <InvSlider
min={64} min={sliderMin}
max={1536} max={sliderMax}
step={CANVAS_GRID_SIZE_COARSE} step={coarseStep}
fineStep={CANVAS_GRID_SIZE_FINE} fineStep={fineStep}
value={width} value={width}
onChange={onChange} onChange={onChange}
numberInputMax={4096} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
defaultValue={optimalDimension}
marks marks
withNumberInput withNumberInput
onReset={onReset}
/> />
</InvControl> </InvControl>
); );

View File

@ -7,17 +7,22 @@ import { useTranslation } from 'react-i18next';
const ParamCFGScale = () => { const ParamCFGScale = () => {
const cfgScale = useAppSelector((s) => s.generation.cfgScale); const cfgScale = useAppSelector((s) => s.generation.cfgScale);
const min = useAppSelector((s) => s.config.sd.guidance.min); const sliderMin = useAppSelector((s) => s.config.sd.guidance.sliderMin);
const inputMax = useAppSelector((s) => s.config.sd.guidance.inputMax);
const sliderMax = useAppSelector((s) => s.config.sd.guidance.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.guidance.sliderMax);
const numberInputMin = useAppSelector(
(s) => s.config.sd.guidance.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.guidance.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.guidance.coarseStep); const coarseStep = useAppSelector((s) => s.config.sd.guidance.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.guidance.fineStep); const fineStep = useAppSelector((s) => s.config.sd.guidance.fineStep);
const initial = useAppSelector((s) => s.config.sd.guidance.initial); const initial = useAppSelector((s) => s.config.sd.guidance.initial);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const marks = useMemo( const marks = useMemo(
() => [min, Math.floor(sliderMax / 2), sliderMax], () => [sliderMin, Math.floor(sliderMax / 2), sliderMax],
[sliderMax, min] [sliderMax, sliderMin]
); );
const onChange = useCallback( const onChange = useCallback(
(v: number) => dispatch(setCfgScale(v)), (v: number) => dispatch(setCfgScale(v)),
@ -29,14 +34,15 @@ const ParamCFGScale = () => {
<InvSlider <InvSlider
value={cfgScale} value={cfgScale}
defaultValue={initial} defaultValue={initial}
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
step={coarseStep} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
onChange={onChange} onChange={onChange}
withNumberInput withNumberInput
marks={marks} marks={marks}
numberInputMax={inputMax} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,6 +1,5 @@
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 { 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 { selectOptimalDimension } from 'features/parameters/store/generationSlice';
@ -11,9 +10,14 @@ export const ParamHeight = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const ctx = useImageSizeContext(); const ctx = useImageSizeContext();
const optimalDimension = useAppSelector(selectOptimalDimension); const optimalDimension = useAppSelector(selectOptimalDimension);
const min = useAppSelector((s) => s.config.sd.height.min); const sliderMin = useAppSelector((s) => s.config.sd.height.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.height.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.height.sliderMax);
const inputMax = useAppSelector((s) => s.config.sd.height.inputMax); const numberInputMin = useAppSelector(
(s) => s.config.sd.height.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.height.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.height.coarseStep); const coarseStep = useAppSelector((s) => s.config.sd.height.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.height.fineStep); const fineStep = useAppSelector((s) => s.config.sd.height.fineStep);
@ -25,8 +29,8 @@ export const ParamHeight = memo(() => {
); );
const marks = useMemo( const marks = useMemo(
() => [min, optimalDimension, sliderMax], () => [sliderMin, optimalDimension, sliderMax],
[min, optimalDimension, sliderMax] [sliderMin, optimalDimension, sliderMax]
); );
return ( return (
@ -35,20 +39,14 @@ export const ParamHeight = memo(() => {
value={ctx.height} value={ctx.height}
defaultValue={optimalDimension} defaultValue={optimalDimension}
onChange={onChange} onChange={onChange}
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
step={coarseStep} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
marks={marks} marks={marks}
/> withNumberInput
<InvNumberInput numberInputMin={numberInputMin}
value={ctx.height} numberInputMax={numberInputMax}
onChange={onChange}
min={min}
max={inputMax}
step={coarseStep}
fineStep={fineStep}
defaultValue={optimalDimension}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,26 +1,28 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, 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';
import { import { setSteps } from 'features/parameters/store/generationSlice';
clampSymmetrySteps,
setSteps,
} 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 ParamSteps = () => { const ParamSteps = () => {
const steps = useAppSelector((s) => s.generation.steps); const steps = useAppSelector((s) => s.generation.steps);
const initial = useAppSelector((s) => s.config.sd.steps.initial); const initial = useAppSelector((s) => s.config.sd.steps.initial);
const min = useAppSelector((s) => s.config.sd.steps.min); const sliderMin = useAppSelector((s) => s.config.sd.steps.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.steps.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.steps.sliderMax);
const inputMax = useAppSelector((s) => s.config.sd.steps.inputMax); const numberInputMin = useAppSelector(
(s) => s.config.sd.steps.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.steps.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.steps.coarseStep); const coarseStep = useAppSelector((s) => s.config.sd.steps.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.steps.fineStep); const fineStep = useAppSelector((s) => s.config.sd.steps.fineStep);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const marks = useMemo( const marks = useMemo(
() => [min, Math.floor(sliderMax / 2), sliderMax], () => [sliderMin, Math.floor(sliderMax / 2), sliderMax],
[sliderMax, min] [sliderMax, sliderMin]
); );
const onChange = useCallback( const onChange = useCallback(
(v: number) => { (v: number) => {
@ -29,24 +31,20 @@ const ParamSteps = () => {
[dispatch] [dispatch]
); );
const onBlur = useCallback(() => {
dispatch(clampSymmetrySteps());
}, [dispatch]);
return ( return (
<InvControl label={t('parameters.steps')} feature="paramSteps"> <InvControl label={t('parameters.steps')} feature="paramSteps">
<InvSlider <InvSlider
value={steps} value={steps}
defaultValue={initial} defaultValue={initial}
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
step={coarseStep} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
onChange={onChange} onChange={onChange}
onBlur={onBlur}
withNumberInput withNumberInput
marks={marks} marks={marks}
numberInputMax={inputMax} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,6 +1,5 @@
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 { 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 { selectOptimalDimension } from 'features/parameters/store/generationSlice';
@ -11,9 +10,14 @@ export const ParamWidth = memo(() => {
const { t } = useTranslation(); const { t } = useTranslation();
const ctx = useImageSizeContext(); const ctx = useImageSizeContext();
const optimalDimension = useAppSelector(selectOptimalDimension); const optimalDimension = useAppSelector(selectOptimalDimension);
const min = useAppSelector((s) => s.config.sd.width.min); const sliderMin = useAppSelector((s) => s.config.sd.width.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.width.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.width.sliderMax);
const inputMax = useAppSelector((s) => s.config.sd.width.inputMax); const numberInputMin = useAppSelector(
(s) => s.config.sd.width.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.width.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.width.coarseStep); const coarseStep = useAppSelector((s) => s.config.sd.width.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.width.fineStep); const fineStep = useAppSelector((s) => s.config.sd.width.fineStep);
@ -25,8 +29,8 @@ export const ParamWidth = memo(() => {
); );
const marks = useMemo( const marks = useMemo(
() => [min, optimalDimension, sliderMax], () => [sliderMin, optimalDimension, sliderMax],
[min, optimalDimension, sliderMax] [sliderMin, optimalDimension, sliderMax]
); );
return ( return (
@ -35,20 +39,14 @@ export const ParamWidth = memo(() => {
value={ctx.width} value={ctx.width}
onChange={onChange} onChange={onChange}
defaultValue={optimalDimension} defaultValue={optimalDimension}
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
step={coarseStep} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
marks={marks} marks={marks}
/> withNumberInput
<InvNumberInput numberInputMin={numberInputMin}
value={ctx.width} numberInputMax={numberInputMax}
onChange={onChange}
min={min}
max={inputMax}
step={coarseStep}
fineStep={fineStep}
defaultValue={optimalDimension}
/> />
</InvControl> </InvControl>
); );

View File

@ -10,11 +10,18 @@ const marks = [0, 0.5, 1];
const ImageToImageStrength = () => { const ImageToImageStrength = () => {
const img2imgStrength = useAppSelector((s) => s.generation.img2imgStrength); const img2imgStrength = useAppSelector((s) => s.generation.img2imgStrength);
const initial = useAppSelector((s) => s.config.sd.img2imgStrength.initial); const initial = useAppSelector((s) => s.config.sd.img2imgStrength.initial);
const min = useAppSelector((s) => s.config.sd.img2imgStrength.min); const sliderMin = useAppSelector(
(s) => s.config.sd.img2imgStrength.sliderMin
);
const sliderMax = useAppSelector( const sliderMax = useAppSelector(
(s) => s.config.sd.img2imgStrength.sliderMax (s) => s.config.sd.img2imgStrength.sliderMax
); );
const inputMax = useAppSelector((s) => s.config.sd.img2imgStrength.inputMax); const numberInputMin = useAppSelector(
(s) => s.config.sd.img2imgStrength.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.img2imgStrength.numberInputMax
);
const coarseStep = useAppSelector( const coarseStep = useAppSelector(
(s) => s.config.sd.img2imgStrength.coarseStep (s) => s.config.sd.img2imgStrength.coarseStep
); );
@ -35,14 +42,15 @@ const ImageToImageStrength = () => {
<InvSlider <InvSlider
step={coarseStep} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
onChange={handleChange} onChange={handleChange}
value={img2imgStrength} value={img2imgStrength}
defaultValue={initial} defaultValue={initial}
marks={marks} marks={marks}
withNumberInput withNumberInput
numberInputMax={inputMax} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,42 +0,0 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { setHorizontalSymmetrySteps } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSymmetryHorizontal = () => {
const horizontalSymmetrySteps = useAppSelector(
(s) => s.generation.horizontalSymmetrySteps
);
const steps = useAppSelector((s) => s.generation.steps);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleChange = useCallback(
(v: number) => {
dispatch(setHorizontalSymmetrySteps(v));
},
[dispatch]
);
return (
<InvControl label={t('parameters.hSymmetryStep')}>
<InvSlider
value={horizontalSymmetrySteps}
defaultValue={0}
onChange={handleChange}
min={0}
max={steps}
step={1}
withNumberInput
marks
/>
</InvControl>
);
};
export default memo(ParamSymmetryHorizontal);

View File

@ -1,28 +0,0 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSwitch } from 'common/components/InvSwitch/wrapper';
import { setShouldUseSymmetry } from 'features/parameters/store/generationSlice';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
const ParamSymmetryToggle = () => {
const shouldUseSymmetry = useAppSelector(
(s) => s.generation.shouldUseSymmetry
);
const dispatch = useAppDispatch();
const handleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
dispatch(setShouldUseSymmetry(e.target.checked));
},
[dispatch]
);
return (
<InvControl label="Enable Symmetry">
<InvSwitch isChecked={shouldUseSymmetry} onChange={handleChange} />
</InvControl>
);
};
export default memo(ParamSymmetryToggle);

View File

@ -1,42 +0,0 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InvControl } from 'common/components/InvControl/InvControl';
import { InvSlider } from 'common/components/InvSlider/InvSlider';
import { setVerticalSymmetrySteps } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const ParamSymmetryVertical = () => {
const verticalSymmetrySteps = useAppSelector(
(s) => s.generation.verticalSymmetrySteps
);
const steps = useAppSelector((s) => s.generation.steps);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleChange = useCallback(
(v: number) => {
dispatch(setVerticalSymmetrySteps(v));
},
[dispatch]
);
return (
<InvControl label={t('parameters.vSymmetryStep')}>
<InvSlider
value={verticalSymmetrySteps}
defaultValue={0}
onChange={handleChange}
min={0}
max={steps}
step={1}
withNumberInput
marks
/>
</InvControl>
);
};
export default memo(ParamSymmetryVertical);

View File

@ -35,7 +35,6 @@ export const initialGenerationState: GenerationState = {
img2imgStrength: 0.75, img2imgStrength: 0.75,
infillMethod: 'patchmatch', infillMethod: 'patchmatch',
iterations: 1, iterations: 1,
perlin: 0,
positivePrompt: '', positivePrompt: '',
negativePrompt: '', negativePrompt: '',
scheduler: 'euler', scheduler: 'euler',
@ -45,19 +44,12 @@ export const initialGenerationState: GenerationState = {
canvasCoherenceSteps: 20, canvasCoherenceSteps: 20,
canvasCoherenceStrength: 0.3, canvasCoherenceStrength: 0.3,
seed: 0, seed: 0,
seedWeights: '',
shouldFitToWidthHeight: true, shouldFitToWidthHeight: true,
shouldGenerateVariations: false,
shouldRandomizeSeed: true, shouldRandomizeSeed: true,
steps: 50, steps: 50,
threshold: 0,
infillTileSize: 32, infillTileSize: 32,
infillPatchmatchDownscaleSize: 1, infillPatchmatchDownscaleSize: 1,
variationAmount: 0.1,
width: 512, width: 512,
shouldUseSymmetry: false,
horizontalSymmetrySteps: 0,
verticalSymmetrySteps: 0,
model: null, model: null,
vae: null, vae: null,
vaePrecision: 'fp32', vaePrecision: 'fp32',
@ -85,18 +77,6 @@ export const generationSlice = createSlice({
setSteps: (state, action: PayloadAction<number>) => { setSteps: (state, action: PayloadAction<number>) => {
state.steps = action.payload; state.steps = action.payload;
}, },
clampSymmetrySteps: (state) => {
state.horizontalSymmetrySteps = clamp(
state.horizontalSymmetrySteps,
0,
state.steps
);
state.verticalSymmetrySteps = clamp(
state.verticalSymmetrySteps,
0,
state.steps
);
},
setCfgScale: (state, action: PayloadAction<ParameterCFGScale>) => { setCfgScale: (state, action: PayloadAction<ParameterCFGScale>) => {
state.cfgScale = action.payload; state.cfgScale = action.payload;
}, },
@ -106,12 +86,6 @@ export const generationSlice = createSlice({
) => { ) => {
state.cfgRescaleMultiplier = action.payload; state.cfgRescaleMultiplier = action.payload;
}, },
setThreshold: (state, action: PayloadAction<number>) => {
state.threshold = action.payload;
},
setPerlin: (state, action: PayloadAction<number>) => {
state.perlin = action.payload;
},
setScheduler: (state, action: PayloadAction<ParameterScheduler>) => { setScheduler: (state, action: PayloadAction<ParameterScheduler>) => {
state.scheduler = action.payload; state.scheduler = action.payload;
}, },
@ -134,17 +108,6 @@ export const generationSlice = createSlice({
resetSeed: (state) => { resetSeed: (state) => {
state.seed = -1; state.seed = -1;
}, },
setShouldGenerateVariations: (state, action: PayloadAction<boolean>) => {
state.shouldGenerateVariations = action.payload;
},
setVariationAmount: (state, action: PayloadAction<number>) => {
state.variationAmount = action.payload;
},
setSeedWeights: (state, action: PayloadAction<string>) => {
state.seedWeights = action.payload;
state.shouldGenerateVariations = true;
state.variationAmount = 0;
},
resetParametersState: (state) => { resetParametersState: (state) => {
return { return {
...state, ...state,
@ -190,15 +153,6 @@ export const generationSlice = createSlice({
) => { ) => {
state.infillPatchmatchDownscaleSize = action.payload; state.infillPatchmatchDownscaleSize = action.payload;
}, },
setShouldUseSymmetry: (state, action: PayloadAction<boolean>) => {
state.shouldUseSymmetry = action.payload;
},
setHorizontalSymmetrySteps: (state, action: PayloadAction<number>) => {
state.horizontalSymmetrySteps = action.payload;
},
setVerticalSymmetrySteps: (state, action: PayloadAction<number>) => {
state.verticalSymmetrySteps = action.payload;
},
initialImageChanged: (state, action: PayloadAction<ImageDTO>) => { initialImageChanged: (state, action: PayloadAction<ImageDTO>) => {
const { image_name, width, height } = action.payload; const { image_name, width, height } = action.payload;
state.initialImage = { imageName: image_name, width, height }; state.initialImage = { imageName: image_name, width, height };
@ -282,7 +236,6 @@ export const generationSlice = createSlice({
}); });
export const { export const {
clampSymmetrySteps,
clearInitialImage, clearInitialImage,
resetParametersState, resetParametersState,
resetSeed, resetSeed,
@ -291,7 +244,6 @@ export const {
setImg2imgStrength, setImg2imgStrength,
setInfillMethod, setInfillMethod,
setIterations, setIterations,
setPerlin,
setPositivePrompt, setPositivePrompt,
setNegativePrompt, setNegativePrompt,
setScheduler, setScheduler,
@ -301,18 +253,11 @@ export const {
setCanvasCoherenceSteps, setCanvasCoherenceSteps,
setCanvasCoherenceStrength, setCanvasCoherenceStrength,
setSeed, setSeed,
setSeedWeights,
setShouldFitToWidthHeight, setShouldFitToWidthHeight,
setShouldGenerateVariations,
setShouldRandomizeSeed, setShouldRandomizeSeed,
setSteps, setSteps,
setThreshold,
setInfillTileSize, setInfillTileSize,
setInfillPatchmatchDownscaleSize, setInfillPatchmatchDownscaleSize,
setVariationAmount,
setShouldUseSymmetry,
setHorizontalSymmetrySteps,
setVerticalSymmetrySteps,
initialImageChanged, initialImageChanged,
modelChanged, modelChanged,
vaeSelected, vaeSelected,

View File

@ -26,7 +26,6 @@ export interface GenerationState {
infillMethod: string; infillMethod: string;
initialImage?: { imageName: string; width: number; height: number }; initialImage?: { imageName: string; width: number; height: number };
iterations: number; iterations: number;
perlin: number;
positivePrompt: ParameterPositivePrompt; positivePrompt: ParameterPositivePrompt;
negativePrompt: ParameterNegativePrompt; negativePrompt: ParameterNegativePrompt;
scheduler: ParameterScheduler; scheduler: ParameterScheduler;
@ -36,19 +35,12 @@ export interface GenerationState {
canvasCoherenceSteps: number; canvasCoherenceSteps: number;
canvasCoherenceStrength: ParameterStrength; canvasCoherenceStrength: ParameterStrength;
seed: ParameterSeed; seed: ParameterSeed;
seedWeights: string;
shouldFitToWidthHeight: boolean; shouldFitToWidthHeight: boolean;
shouldGenerateVariations: boolean;
shouldRandomizeSeed: boolean; shouldRandomizeSeed: boolean;
steps: ParameterSteps; steps: ParameterSteps;
threshold: number;
infillTileSize: number; infillTileSize: number;
infillPatchmatchDownscaleSize: number; infillPatchmatchDownscaleSize: number;
variationAmount: number;
width: ParameterWidth; width: ParameterWidth;
shouldUseSymmetry: boolean;
horizontalSymmetrySteps: number;
verticalSymmetrySteps: number;
model: ParameterModel | null; model: ParameterModel | null;
vae: ParameterVAEModel | null; vae: ParameterVAEModel | null;
vaePrecision: ParameterPrecision; vaePrecision: ParameterPrecision;

View File

@ -1,7 +1,6 @@
import { enqueueRequested } from 'app/store/actions'; import { enqueueRequested } from 'app/store/actions';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue'; import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
import { clampSymmetrySteps } from 'features/parameters/store/generationSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useEnqueueBatchMutation } from 'services/api/endpoints/queue'; import { useEnqueueBatchMutation } from 'services/api/endpoints/queue';
@ -18,7 +17,6 @@ export const useQueueBack = () => {
if (isDisabled) { if (isDisabled) {
return; return;
} }
dispatch(clampSymmetrySteps());
dispatch(enqueueRequested({ tabName, prepend: false })); dispatch(enqueueRequested({ tabName, prepend: false }));
}, [dispatch, isDisabled, tabName]); }, [dispatch, isDisabled, tabName]);

View File

@ -1,7 +1,6 @@
import { enqueueRequested } from 'app/store/actions'; import { enqueueRequested } from 'app/store/actions';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue'; import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue';
import { clampSymmetrySteps } from 'features/parameters/store/generationSlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
@ -24,7 +23,6 @@ export const useQueueFront = () => {
if (isDisabled) { if (isDisabled) {
return; return;
} }
dispatch(clampSymmetrySteps());
dispatch(enqueueRequested({ tabName, prepend: true })); dispatch(enqueueRequested({ tabName, prepend: true }));
}, [dispatch, isDisabled, tabName]); }, [dispatch, isDisabled, tabName]);

View File

@ -9,15 +9,20 @@ const ParamSDXLRefinerCFGScale = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const refinerCFGScale = useAppSelector((s) => s.sdxl.refinerCFGScale); const refinerCFGScale = useAppSelector((s) => s.sdxl.refinerCFGScale);
const min = useAppSelector((s) => s.config.sd.guidance.min); const sliderMin = useAppSelector((s) => s.config.sd.guidance.sliderMin);
const inputMax = useAppSelector((s) => s.config.sd.guidance.inputMax);
const sliderMax = useAppSelector((s) => s.config.sd.guidance.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.guidance.sliderMax);
const numberInputMin = useAppSelector(
(s) => s.config.sd.guidance.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.guidance.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.guidance.coarseStep); const coarseStep = useAppSelector((s) => s.config.sd.guidance.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.guidance.fineStep); const fineStep = useAppSelector((s) => s.config.sd.guidance.fineStep);
const initial = useAppSelector((s) => s.config.sd.guidance.initial); const initial = useAppSelector((s) => s.config.sd.guidance.initial);
const marks = useMemo( const marks = useMemo(
() => [min, Math.floor(sliderMax / 2), sliderMax], () => [sliderMin, Math.floor(sliderMax / 2), sliderMax],
[sliderMax, min] [sliderMax, sliderMin]
); );
const onChange = useCallback( const onChange = useCallback(
@ -30,13 +35,14 @@ const ParamSDXLRefinerCFGScale = () => {
<InvSlider <InvSlider
value={refinerCFGScale} value={refinerCFGScale}
defaultValue={initial} defaultValue={initial}
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
step={coarseStep} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
onChange={onChange} onChange={onChange}
withNumberInput withNumberInput
numberInputMax={inputMax} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
marks={marks} marks={marks}
/> />
</InvControl> </InvControl>

View File

@ -10,15 +10,20 @@ const ParamSDXLRefinerSteps = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const refinerSteps = useAppSelector((s) => s.sdxl.refinerSteps); const refinerSteps = useAppSelector((s) => s.sdxl.refinerSteps);
const initial = useAppSelector((s) => s.config.sd.steps.initial); const initial = useAppSelector((s) => s.config.sd.steps.initial);
const min = useAppSelector((s) => s.config.sd.steps.min); const sliderMin = useAppSelector((s) => s.config.sd.steps.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.steps.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.steps.sliderMax);
const inputMax = useAppSelector((s) => s.config.sd.steps.inputMax); const numberInputMin = useAppSelector(
(s) => s.config.sd.steps.numberInputMin
);
const numberInputMax = useAppSelector(
(s) => s.config.sd.steps.numberInputMax
);
const coarseStep = useAppSelector((s) => s.config.sd.steps.coarseStep); const coarseStep = useAppSelector((s) => s.config.sd.steps.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.steps.fineStep); const fineStep = useAppSelector((s) => s.config.sd.steps.fineStep);
const marks = useMemo( const marks = useMemo(
() => [min, Math.floor(sliderMax / 2), sliderMax], () => [sliderMin, Math.floor(sliderMax / 2), sliderMax],
[sliderMax, min] [sliderMax, sliderMin]
); );
const onChange = useCallback( const onChange = useCallback(
@ -33,14 +38,15 @@ const ParamSDXLRefinerSteps = () => {
<InvSlider <InvSlider
value={refinerSteps} value={refinerSteps}
defaultValue={initial} defaultValue={initial}
min={min} min={sliderMin}
max={sliderMax} max={sliderMax}
step={coarseStep} step={coarseStep}
fineStep={fineStep} fineStep={fineStep}
onChange={onChange} onChange={onChange}
withNumberInput withNumberInput
marks={marks} marks={marks}
numberInputMax={inputMax} numberInputMin={numberInputMin}
numberInputMax={numberInputMax}
/> />
</InvControl> </InvControl>
); );

View File

@ -1,9 +1,23 @@
import type { PayloadAction } from '@reduxjs/toolkit'; import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit';
import type { RootState } from 'app/store/store'; import type { RootState } from 'app/store/store';
import type { AppConfig, PartialAppConfig } from 'app/types/invokeai'; import type {
AppConfig,
NumericalParameterConfig,
PartialAppConfig,
} from 'app/types/invokeai';
import { merge } from 'lodash-es'; import { merge } from 'lodash-es';
const baseDimensionConfig: NumericalParameterConfig = {
initial: 512, // determined by model selection, unused in practice
sliderMin: 64,
sliderMax: 1536,
numberInputMin: 64,
numberInputMax: 4096,
fineStep: 8,
coarseStep: 64,
};
export const initialConfigState: AppConfig = { export const initialConfigState: AppConfig = {
shouldUpdateImagesOnConnect: false, shouldUpdateImagesOnConnect: false,
shouldFetchMetadataFromApi: false, shouldFetchMetadataFromApi: false,
@ -24,66 +38,138 @@ export const initialConfigState: AppConfig = {
disabledControlNetProcessors: [], disabledControlNetProcessors: [],
iterations: { iterations: {
initial: 1, initial: 1,
min: 1, sliderMin: 1,
sliderMax: 1000, sliderMax: 1000,
inputMax: 10000, numberInputMin: 1,
numberInputMax: 10000,
fineStep: 1, fineStep: 1,
coarseStep: 1, coarseStep: 1,
}, },
width: { width: { ...baseDimensionConfig },
initial: 512, height: { ...baseDimensionConfig },
min: 64, boundingBoxWidth: { ...baseDimensionConfig },
sliderMax: 1536, boundingBoxHeight: { ...baseDimensionConfig },
inputMax: 4096, scaledBoundingBoxWidth: { ...baseDimensionConfig },
fineStep: 8, scaledBoundingBoxHeight: { ...baseDimensionConfig },
coarseStep: 64,
},
height: {
initial: 512,
min: 64,
sliderMax: 1536,
inputMax: 4096,
fineStep: 8,
coarseStep: 64,
},
steps: { steps: {
initial: 30, initial: 30,
min: 1, sliderMin: 1,
sliderMax: 100, sliderMax: 100,
inputMax: 500, numberInputMin: 1,
numberInputMax: 500,
fineStep: 1, fineStep: 1,
coarseStep: 1, coarseStep: 1,
}, },
guidance: { guidance: {
initial: 7, initial: 7,
min: 1, sliderMin: 1,
sliderMax: 20, sliderMax: 20,
inputMax: 200, numberInputMin: 1,
numberInputMax: 200,
fineStep: 0.1, fineStep: 0.1,
coarseStep: 0.5, coarseStep: 0.5,
}, },
img2imgStrength: { img2imgStrength: {
initial: 0.7, initial: 0.7,
min: 0, sliderMin: 0,
sliderMax: 1, sliderMax: 1,
inputMax: 1, numberInputMin: 0,
numberInputMax: 1,
fineStep: 0.01,
coarseStep: 0.05,
},
canvasCoherenceStrength: {
initial: 0.3,
sliderMin: 0,
sliderMax: 1,
numberInputMin: 0,
numberInputMax: 1,
fineStep: 0.01, fineStep: 0.01,
coarseStep: 0.05, coarseStep: 0.05,
}, },
hrfStrength: { hrfStrength: {
initial: 0.45, initial: 0.45,
min: 0, sliderMin: 0,
sliderMax: 1, sliderMax: 1,
inputMax: 1, numberInputMin: 0,
numberInputMax: 1,
fineStep: 0.01, fineStep: 0.01,
coarseStep: 0.05, coarseStep: 0.05,
}, },
canvasCoherenceSteps: {
initial: 20,
sliderMin: 1,
sliderMax: 100,
numberInputMin: 1,
numberInputMax: 999,
fineStep: 1,
coarseStep: 1,
},
cfgRescaleMultiplier: {
initial: 0,
sliderMin: 0,
sliderMax: 0.99,
numberInputMin: 0,
numberInputMax: 0.99,
fineStep: 0.05,
coarseStep: 0.1,
},
clipSkip: {
initial: 0,
sliderMin: 0,
sliderMax: 12, // determined by model selection, unused in practice
numberInputMin: 0,
numberInputMax: 12, // determined by model selection, unused in practice
fineStep: 1,
coarseStep: 1,
},
infillPatchmatchDownscaleSize: {
initial: 1,
sliderMin: 1,
sliderMax: 10,
numberInputMin: 1,
numberInputMax: 10,
fineStep: 1,
coarseStep: 1,
},
infillTileSize: {
initial: 32,
sliderMin: 16,
sliderMax: 64,
numberInputMin: 16,
numberInputMax: 256,
fineStep: 1,
coarseStep: 1,
},
maskBlur: {
initial: 16,
sliderMin: 0,
sliderMax: 64,
numberInputMin: 0,
numberInputMax: 512,
fineStep: 1,
coarseStep: 1,
},
ca: {
weight: {
initial: 1,
sliderMin: 0,
sliderMax: 2,
numberInputMin: -1,
numberInputMax: 2,
fineStep: 0.01,
coarseStep: 0.05,
},
},
dynamicPrompts: { dynamicPrompts: {
maxPrompts: { maxPrompts: {
initial: 100, initial: 100,
min: 1, sliderMin: 1,
sliderMax: 1000, sliderMax: 1000,
inputMax: 10000, numberInputMin: 1,
numberInputMax: 10000,
fineStep: 1,
coarseStep: 10,
}, },
}, },
}, },