mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): wip generation bbox
This commit is contained in:
parent
5ab345ee63
commit
9047f6db30
@ -8,3 +8,7 @@ export const roundDownToMultipleMin = (num: number, multiple: number): number =>
|
|||||||
export const roundToMultiple = (num: number, multiple: number): number => {
|
export const roundToMultiple = (num: number, multiple: number): number => {
|
||||||
return Math.round(num / multiple) * multiple;
|
return Math.round(num / multiple) * multiple;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const roundToMultipleMin = (num: number, multiple: number): number => {
|
||||||
|
return Math.max(Math.round(num / multiple) * multiple, multiple);
|
||||||
|
};
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
||||||
|
import { useStore } from '@nanostores/react';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { $stageScale } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import { round } from 'lodash-es';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
export const HeadsUpDisplay = memo(() => {
|
||||||
|
const stageScale = useStore($stageScale);
|
||||||
|
const layerCount = useAppSelector((s) => s.controlLayers.present.layers.length);
|
||||||
|
const bbox = useAppSelector((s) => s.controlLayers.present.bbox);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex flexDir="column" bg="blackAlpha.400" borderBottomEndRadius="base" p={2} minW={64} gap={2}>
|
||||||
|
<HUDItem label="Scale" value={stageScale} />
|
||||||
|
<HUDItem label="Layer Count" value={layerCount} />
|
||||||
|
<HUDItem label="BBox Size" value={`${bbox.width}×${bbox.height}`} />
|
||||||
|
<HUDItem label="BBox Position" value={`${bbox.x}, ${bbox.y}`} />
|
||||||
|
<HUDItem label="BBox Width % 8" value={round(bbox.width % 8, 3)} />
|
||||||
|
<HUDItem label="BBox Height % 8" value={round(bbox.height % 8, 3)} />
|
||||||
|
<HUDItem label="BBox X % 8" value={round(bbox.x % 8, 3)} />
|
||||||
|
<HUDItem label="BBox Y % 8" value={round(bbox.y % 8, 3)} />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
HeadsUpDisplay.displayName = 'HeadsUpDisplay';
|
||||||
|
|
||||||
|
const HUDItem = memo(({ label, value }: { label: string; value: string | number }) => {
|
||||||
|
return (
|
||||||
|
<Box display="inline-block" lineHeight={1}>
|
||||||
|
<Text as="span">{label}: </Text>
|
||||||
|
<Text as="span" fontWeight="semibold">
|
||||||
|
{value}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
HUDItem.displayName = 'HUDItem';
|
@ -1,9 +1,10 @@
|
|||||||
import { Box, Flex, Heading } from '@invoke-ai/ui-library';
|
import { $alt, $meta, $shift, Box, Flex, Heading } from '@invoke-ai/ui-library';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { HeadsUpDisplay } from 'features/controlLayers/components/HeadsUpDisplay';
|
||||||
import {
|
import {
|
||||||
BRUSH_SPACING_PCT,
|
BRUSH_SPACING_PCT,
|
||||||
MAX_BRUSH_SPACING_PX,
|
MAX_BRUSH_SPACING_PX,
|
||||||
@ -12,12 +13,11 @@ import {
|
|||||||
} from 'features/controlLayers/konva/constants';
|
} from 'features/controlLayers/konva/constants';
|
||||||
import { setStageEventHandlers } from 'features/controlLayers/konva/events';
|
import { setStageEventHandlers } from 'features/controlLayers/konva/events';
|
||||||
import { debouncedRenderers, renderers as normalRenderers } from 'features/controlLayers/konva/renderers/layers';
|
import { debouncedRenderers, renderers as normalRenderers } from 'features/controlLayers/konva/renderers/layers';
|
||||||
import { renderImageDimsPreview } from 'features/controlLayers/konva/renderers/previewLayer';
|
|
||||||
import {
|
import {
|
||||||
|
$bbox,
|
||||||
$brushColor,
|
$brushColor,
|
||||||
$brushSize,
|
$brushSize,
|
||||||
$brushSpacingPx,
|
$brushSpacingPx,
|
||||||
$genBbox,
|
|
||||||
$isDrawing,
|
$isDrawing,
|
||||||
$isMouseDown,
|
$isMouseDown,
|
||||||
$isSpaceDown,
|
$isSpaceDown,
|
||||||
@ -100,10 +100,6 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
() => clamp(state.brushSize / BRUSH_SPACING_PCT, MIN_BRUSH_SPACING_PX, MAX_BRUSH_SPACING_PX),
|
() => clamp(state.brushSize / BRUSH_SPACING_PCT, MIN_BRUSH_SPACING_PX, MAX_BRUSH_SPACING_PX),
|
||||||
[state.brushSize]
|
[state.brushSize]
|
||||||
);
|
);
|
||||||
const bbox = useMemo(
|
|
||||||
() => ({ x: state.x, y: state.y, width: state.size.width, height: state.size.height }),
|
|
||||||
[state.x, state.y, state.size.width, state.size.height]
|
|
||||||
);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
$brushColor.set(brushColor);
|
$brushColor.set(brushColor);
|
||||||
@ -111,7 +107,7 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
$brushSpacingPx.set(brushSpacingPx);
|
$brushSpacingPx.set(brushSpacingPx);
|
||||||
$selectedLayer.set(selectedLayer);
|
$selectedLayer.set(selectedLayer);
|
||||||
$shouldInvertBrushSizeScrollDirection.set(shouldInvertBrushSizeScrollDirection);
|
$shouldInvertBrushSizeScrollDirection.set(shouldInvertBrushSizeScrollDirection);
|
||||||
$genBbox.set(bbox);
|
$bbox.set(state.bbox);
|
||||||
}, [
|
}, [
|
||||||
brushSpacingPx,
|
brushSpacingPx,
|
||||||
brushColor,
|
brushColor,
|
||||||
@ -120,7 +116,7 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
state.brushSize,
|
state.brushSize,
|
||||||
state.selectedLayerId,
|
state.selectedLayerId,
|
||||||
state.brushColor,
|
state.brushColor,
|
||||||
bbox,
|
state.bbox,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const onLayerPosChanged = useCallback(
|
const onLayerPosChanged = useCallback(
|
||||||
@ -257,7 +253,7 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.trace('Rendering tool preview');
|
log.trace('Rendering tool preview');
|
||||||
renderers.renderPreviewLayer(
|
renderers.renderToolPreview(
|
||||||
stage,
|
stage,
|
||||||
tool,
|
tool,
|
||||||
brushColor,
|
brushColor,
|
||||||
@ -267,41 +263,36 @@ const useStageRenderer = (stage: Konva.Stage, container: HTMLDivElement | null,
|
|||||||
lastMouseDownPos,
|
lastMouseDownPos,
|
||||||
state.brushSize,
|
state.brushSize,
|
||||||
isDrawing,
|
isDrawing,
|
||||||
isMouseDown,
|
isMouseDown
|
||||||
$genBbox,
|
|
||||||
onBboxTransformed
|
|
||||||
);
|
);
|
||||||
renderImageDimsPreview(stage, bbox, tool);
|
|
||||||
}, [
|
}, [
|
||||||
asPreview,
|
asPreview,
|
||||||
stage,
|
|
||||||
tool,
|
|
||||||
brushColor,
|
brushColor,
|
||||||
selectedLayer,
|
|
||||||
state.globalMaskLayerOpacity,
|
|
||||||
lastCursorPos,
|
|
||||||
lastMouseDownPos,
|
|
||||||
state.brushSize,
|
|
||||||
renderers,
|
|
||||||
isDrawing,
|
isDrawing,
|
||||||
isMouseDown,
|
isMouseDown,
|
||||||
bbox,
|
lastCursorPos,
|
||||||
stageScale,
|
lastMouseDownPos,
|
||||||
onBboxTransformed,
|
renderers,
|
||||||
|
selectedLayer?.type,
|
||||||
|
stage,
|
||||||
|
state.brushSize,
|
||||||
|
state.globalMaskLayerOpacity,
|
||||||
|
tool,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (asPreview) {
|
||||||
|
// Preview should not display tool
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.trace('Rendering bbox preview');
|
||||||
|
renderers.renderBboxPreview(stage, state.bbox, tool, $bbox.get, onBboxTransformed, $shift.get, $meta.get, $alt.get);
|
||||||
|
}, [asPreview, onBboxTransformed, renderers, stage, state.bbox, tool]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
log.trace('Rendering layers');
|
log.trace('Rendering layers');
|
||||||
renderers.renderLayers(
|
renderers.renderLayers(stage, state.layers, state.globalMaskLayerOpacity, tool, getImageDTO, onLayerPosChanged);
|
||||||
stage,
|
}, [stage, state.layers, state.globalMaskLayerOpacity, tool, onLayerPosChanged, renderers]);
|
||||||
bbox,
|
|
||||||
state.layers,
|
|
||||||
state.globalMaskLayerOpacity,
|
|
||||||
tool,
|
|
||||||
getImageDTO,
|
|
||||||
onLayerPosChanged
|
|
||||||
);
|
|
||||||
}, [stage, state.layers, state.globalMaskLayerOpacity, tool, onLayerPosChanged, renderers, bbox]);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (asPreview) {
|
if (asPreview) {
|
||||||
@ -369,6 +360,11 @@ export const StageComponent = memo(({ asPreview = false }: Props) => {
|
|||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
data-testid="control-layers-canvas"
|
data-testid="control-layers-canvas"
|
||||||
/>
|
/>
|
||||||
|
{!asPreview && (
|
||||||
|
<Flex position="absolute" top={0} insetInlineStart={0}>
|
||||||
|
<HeadsUpDisplay />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,6 @@ import { LightnessToAlphaFilter } from 'features/controlLayers/konva/filters';
|
|||||||
import { CA_LAYER_IMAGE_NAME, CA_LAYER_NAME, getCALayerImageId } from 'features/controlLayers/konva/naming';
|
import { CA_LAYER_IMAGE_NAME, CA_LAYER_NAME, getCALayerImageId } from 'features/controlLayers/konva/naming';
|
||||||
import type { ControlAdapterLayer } from 'features/controlLayers/store/types';
|
import type { ControlAdapterLayer } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect } from 'konva/lib/types';
|
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +51,6 @@ const updateCALayerImageSource = async (
|
|||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
konvaLayer: Konva.Layer,
|
konvaLayer: Konva.Layer,
|
||||||
layerState: ControlAdapterLayer,
|
layerState: ControlAdapterLayer,
|
||||||
bbox: IRect,
|
|
||||||
getImageDTO: (imageName: string) => Promise<ImageDTO | null>
|
getImageDTO: (imageName: string) => Promise<ImageDTO | null>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const image = layerState.controlAdapter.processedImage ?? layerState.controlAdapter.image;
|
const image = layerState.controlAdapter.processedImage ?? layerState.controlAdapter.image;
|
||||||
@ -74,7 +72,7 @@ const updateCALayerImageSource = async (
|
|||||||
id: imageId,
|
id: imageId,
|
||||||
image: imageEl,
|
image: imageEl,
|
||||||
});
|
});
|
||||||
updateCALayerImageAttrs(stage, konvaImage, layerState, bbox);
|
updateCALayerImageAttrs(stage, konvaImage, layerState);
|
||||||
// Must cache after this to apply the filters
|
// Must cache after this to apply the filters
|
||||||
konvaImage.cache();
|
konvaImage.cache();
|
||||||
imageEl.id = imageId;
|
imageEl.id = imageId;
|
||||||
@ -95,8 +93,7 @@ const updateCALayerImageSource = async (
|
|||||||
const updateCALayerImageAttrs = (
|
const updateCALayerImageAttrs = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
konvaImage: Konva.Image,
|
konvaImage: Konva.Image,
|
||||||
layerState: ControlAdapterLayer,
|
layerState: ControlAdapterLayer
|
||||||
bbox: IRect
|
|
||||||
): void => {
|
): void => {
|
||||||
let needsCache = false;
|
let needsCache = false;
|
||||||
// Konva erroneously reports NaN for width and height when the stage is hidden. This causes errors when caching,
|
// Konva erroneously reports NaN for width and height when the stage is hidden. This causes errors when caching,
|
||||||
@ -104,10 +101,8 @@ const updateCALayerImageAttrs = (
|
|||||||
// TODO(psyche): Investigate and report upstream.
|
// TODO(psyche): Investigate and report upstream.
|
||||||
const hasFilter = konvaImage.filters() !== null && konvaImage.filters().length > 0;
|
const hasFilter = konvaImage.filters() !== null && konvaImage.filters().length > 0;
|
||||||
if (
|
if (
|
||||||
konvaImage.x() !== bbox.x ||
|
konvaImage.x() !== layerState.x ||
|
||||||
konvaImage.y() !== bbox.y ||
|
konvaImage.y() !== layerState.y ||
|
||||||
konvaImage.width() !== bbox.width ||
|
|
||||||
konvaImage.height() !== bbox.height ||
|
|
||||||
konvaImage.visible() !== layerState.isEnabled ||
|
konvaImage.visible() !== layerState.isEnabled ||
|
||||||
hasFilter !== layerState.isFilterEnabled
|
hasFilter !== layerState.isFilterEnabled
|
||||||
) {
|
) {
|
||||||
@ -115,7 +110,6 @@ const updateCALayerImageAttrs = (
|
|||||||
opacity: layerState.opacity,
|
opacity: layerState.opacity,
|
||||||
scaleX: 1,
|
scaleX: 1,
|
||||||
scaleY: 1,
|
scaleY: 1,
|
||||||
...bbox,
|
|
||||||
visible: layerState.isEnabled,
|
visible: layerState.isEnabled,
|
||||||
filters: layerState.isFilterEnabled ? [LightnessToAlphaFilter] : [],
|
filters: layerState.isFilterEnabled ? [LightnessToAlphaFilter] : [],
|
||||||
});
|
});
|
||||||
@ -139,7 +133,6 @@ const updateCALayerImageAttrs = (
|
|||||||
export const renderCALayer = (
|
export const renderCALayer = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
layerState: ControlAdapterLayer,
|
layerState: ControlAdapterLayer,
|
||||||
bbox: IRect,
|
|
||||||
zIndex: number,
|
zIndex: number,
|
||||||
getImageDTO: (imageName: string) => Promise<ImageDTO | null>
|
getImageDTO: (imageName: string) => Promise<ImageDTO | null>
|
||||||
): void => {
|
): void => {
|
||||||
@ -164,8 +157,8 @@ export const renderCALayer = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (imageSourceNeedsUpdate) {
|
if (imageSourceNeedsUpdate) {
|
||||||
updateCALayerImageSource(stage, konvaLayer, layerState, bbox, getImageDTO);
|
updateCALayerImageSource(stage, konvaLayer, layerState, getImageDTO);
|
||||||
} else if (konvaImage) {
|
} else if (konvaImage) {
|
||||||
updateCALayerImageAttrs(stage, konvaImage, layerState, bbox);
|
updateCALayerImageAttrs(stage, konvaImage, layerState);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ import { PREVIEW_LAYER_ID } from 'features/controlLayers/konva/naming';
|
|||||||
import { updateBboxes } from 'features/controlLayers/konva/renderers/bbox';
|
import { updateBboxes } from 'features/controlLayers/konva/renderers/bbox';
|
||||||
import { renderCALayer } from 'features/controlLayers/konva/renderers/caLayer';
|
import { renderCALayer } from 'features/controlLayers/konva/renderers/caLayer';
|
||||||
import { renderIILayer } from 'features/controlLayers/konva/renderers/iiLayer';
|
import { renderIILayer } from 'features/controlLayers/konva/renderers/iiLayer';
|
||||||
import { renderPreviewLayer } from 'features/controlLayers/konva/renderers/previewLayer';
|
import { renderBboxPreview, renderToolPreview } from 'features/controlLayers/konva/renderers/previewLayer';
|
||||||
import { renderRasterLayer } from 'features/controlLayers/konva/renderers/rasterLayer';
|
import { renderRasterLayer } from 'features/controlLayers/konva/renderers/rasterLayer';
|
||||||
import { renderRGLayer } from 'features/controlLayers/konva/renderers/rgLayer';
|
import { renderRGLayer } from 'features/controlLayers/konva/renderers/rgLayer';
|
||||||
import { mapId, selectRenderableLayers } from 'features/controlLayers/konva/util';
|
import { mapId, selectRenderableLayers } from 'features/controlLayers/konva/util';
|
||||||
@ -16,7 +16,6 @@ import {
|
|||||||
isRenderableLayer,
|
isRenderableLayer,
|
||||||
} from 'features/controlLayers/store/types';
|
} from 'features/controlLayers/store/types';
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
import type { IRect } from 'konva/lib/types';
|
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
|
|
||||||
@ -35,7 +34,6 @@ import type { ImageDTO } from 'services/api/types';
|
|||||||
*/
|
*/
|
||||||
const renderLayers = (
|
const renderLayers = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
bbox: IRect,
|
|
||||||
layerStates: Layer[],
|
layerStates: Layer[],
|
||||||
globalMaskLayerOpacity: number,
|
globalMaskLayerOpacity: number,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
@ -56,7 +54,7 @@ const renderLayers = (
|
|||||||
renderRGLayer(stage, layer, globalMaskLayerOpacity, tool, zIndex, onLayerPosChanged);
|
renderRGLayer(stage, layer, globalMaskLayerOpacity, tool, zIndex, onLayerPosChanged);
|
||||||
}
|
}
|
||||||
if (isControlAdapterLayer(layer)) {
|
if (isControlAdapterLayer(layer)) {
|
||||||
renderCALayer(stage, layer, bbox, zIndex, getImageDTO);
|
renderCALayer(stage, layer, zIndex, getImageDTO);
|
||||||
}
|
}
|
||||||
if (isInitialImageLayer(layer)) {
|
if (isInitialImageLayer(layer)) {
|
||||||
renderIILayer(stage, layer, zIndex, getImageDTO);
|
renderIILayer(stage, layer, zIndex, getImageDTO);
|
||||||
@ -76,7 +74,8 @@ const renderLayers = (
|
|||||||
* All the renderers for the Konva stage.
|
* All the renderers for the Konva stage.
|
||||||
*/
|
*/
|
||||||
export const renderers = {
|
export const renderers = {
|
||||||
renderPreviewLayer,
|
renderToolPreview,
|
||||||
|
renderBboxPreview,
|
||||||
renderLayers,
|
renderLayers,
|
||||||
updateBboxes,
|
updateBboxes,
|
||||||
};
|
};
|
||||||
@ -87,7 +86,8 @@ export const renderers = {
|
|||||||
* @returns The renderers with debouncing applied
|
* @returns The renderers with debouncing applied
|
||||||
*/
|
*/
|
||||||
const getDebouncedRenderers = (ms = DEBOUNCE_MS): typeof renderers => ({
|
const getDebouncedRenderers = (ms = DEBOUNCE_MS): typeof renderers => ({
|
||||||
renderPreviewLayer: debounce(renderPreviewLayer, ms),
|
renderToolPreview: debounce(renderToolPreview, ms),
|
||||||
|
renderBboxPreview: debounce(renderBboxPreview, ms),
|
||||||
renderLayers: debounce(renderLayers, ms),
|
renderLayers: debounce(renderLayers, ms),
|
||||||
updateBboxes: debounce(updateBboxes, ms),
|
updateBboxes: debounce(updateBboxes, ms),
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
import { roundToMultiple, roundToMultipleMin } from 'common/util/roundDownToMultiple';
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
import {
|
import {
|
||||||
BBOX_SELECTED_STROKE,
|
BBOX_SELECTED_STROKE,
|
||||||
@ -21,18 +21,13 @@ import { selectRenderableLayers, snapPosToStage } from 'features/controlLayers/k
|
|||||||
import type { Layer, RgbaColor, Tool } from 'features/controlLayers/store/types';
|
import type { Layer, RgbaColor, Tool } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect, Vector2d } from 'konva/lib/types';
|
import type { IRect, Vector2d } from 'konva/lib/types';
|
||||||
import type { WritableAtom } from 'nanostores';
|
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the singleton preview layer and all its objects.
|
* Creates the singleton preview layer and all its objects.
|
||||||
* @param stage The konva stage
|
* @param stage The konva stage
|
||||||
*/
|
*/
|
||||||
const getPreviewLayer = (
|
const getPreviewLayer = (stage: Konva.Stage): Konva.Layer => {
|
||||||
stage: Konva.Stage,
|
|
||||||
$genBbox: WritableAtom<IRect>,
|
|
||||||
onBboxTransformed: (bbox: IRect) => void
|
|
||||||
): Konva.Layer => {
|
|
||||||
let previewLayer = stage.findOne<Konva.Layer>(`#${PREVIEW_LAYER_ID}`);
|
let previewLayer = stage.findOne<Konva.Layer>(`#${PREVIEW_LAYER_ID}`);
|
||||||
if (previewLayer) {
|
if (previewLayer) {
|
||||||
return previewLayer;
|
return previewLayer;
|
||||||
@ -40,6 +35,168 @@ const getPreviewLayer = (
|
|||||||
// Initialize the preview layer & add to the stage
|
// Initialize the preview layer & add to the stage
|
||||||
previewLayer = new Konva.Layer({ id: PREVIEW_LAYER_ID, listening: true });
|
previewLayer = new Konva.Layer({ id: PREVIEW_LAYER_ID, listening: true });
|
||||||
stage.add(previewLayer);
|
stage.add(previewLayer);
|
||||||
|
return previewLayer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBboxPreviewGroup = (
|
||||||
|
stage: Konva.Stage,
|
||||||
|
getBbox: () => IRect,
|
||||||
|
onBboxTransformed: (bbox: IRect) => void,
|
||||||
|
getShiftKey: () => boolean,
|
||||||
|
getMetaKey: () => boolean,
|
||||||
|
getAltKey: () => boolean
|
||||||
|
): Konva.Group => {
|
||||||
|
const previewLayer = getPreviewLayer(stage);
|
||||||
|
let bboxPreviewGroup = previewLayer.findOne<Konva.Group>(`#${PREVIEW_GENERATION_BBOX_GROUP}`);
|
||||||
|
|
||||||
|
if (bboxPreviewGroup) {
|
||||||
|
return bboxPreviewGroup;
|
||||||
|
}
|
||||||
|
console.log('creating new bbox');
|
||||||
|
|
||||||
|
// Use a transformer for the generation bbox. Transformers need some shape to transform, we will use a fully
|
||||||
|
// transparent rect for this purpose.
|
||||||
|
bboxPreviewGroup = new Konva.Group({ id: PREVIEW_GENERATION_BBOX_GROUP });
|
||||||
|
const bboxRect = new Konva.Rect({
|
||||||
|
id: PREVIEW_GENERATION_BBOX_DUMMY_RECT,
|
||||||
|
listening: true,
|
||||||
|
strokeEnabled: false,
|
||||||
|
draggable: true,
|
||||||
|
fill: 'rgba(255,0,0,0.3)',
|
||||||
|
...getBbox(),
|
||||||
|
});
|
||||||
|
bboxRect.on('dragmove', () => {
|
||||||
|
const gridSize = getMetaKey() ? 8 : 64;
|
||||||
|
const oldBbox = getBbox();
|
||||||
|
const newBbox: IRect = {
|
||||||
|
...oldBbox,
|
||||||
|
x: roundToMultiple(bboxRect.x(), gridSize),
|
||||||
|
y: roundToMultiple(bboxRect.y(), gridSize),
|
||||||
|
};
|
||||||
|
bboxRect.setAttrs(newBbox);
|
||||||
|
if (oldBbox.x !== newBbox.x || oldBbox.y !== newBbox.y) {
|
||||||
|
onBboxTransformed(newBbox);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const bboxTransformer = new Konva.Transformer({
|
||||||
|
id: PREVIEW_GENERATION_BBOX_TRANSFORMER,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
borderStroke: 'rgba(212,216,234,1)',
|
||||||
|
borderEnabled: true,
|
||||||
|
rotateEnabled: false,
|
||||||
|
keepRatio: false,
|
||||||
|
ignoreStroke: true,
|
||||||
|
listening: false,
|
||||||
|
flipEnabled: false,
|
||||||
|
anchorFill: 'rgba(212,216,234,1)',
|
||||||
|
anchorStroke: 'rgb(42,42,42)',
|
||||||
|
anchorSize: 12,
|
||||||
|
anchorCornerRadius: 3,
|
||||||
|
// shiftBehavior: 'none',
|
||||||
|
centeredScaling: false,
|
||||||
|
anchorStyleFunc: (anchor) => {
|
||||||
|
// Make the x/y resize anchors little bars
|
||||||
|
if (anchor.hasName('top-center') || anchor.hasName('bottom-center')) {
|
||||||
|
anchor.height(8);
|
||||||
|
anchor.offsetY(4);
|
||||||
|
anchor.width(30);
|
||||||
|
anchor.offsetX(15);
|
||||||
|
}
|
||||||
|
if (anchor.hasName('middle-left') || anchor.hasName('middle-right')) {
|
||||||
|
anchor.height(30);
|
||||||
|
anchor.offsetY(15);
|
||||||
|
anchor.width(8);
|
||||||
|
anchor.offsetX(4);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
anchorDragBoundFunc: (oldAbsPos, newAbsPos) => {
|
||||||
|
const gridSize = getMetaKey() ? 8 : 64;
|
||||||
|
const scaledGridSize = gridSize * stage.scaleX();
|
||||||
|
// Calculate the offset of the grid.
|
||||||
|
const stageAbsPos = stage.getAbsolutePosition();
|
||||||
|
const offsetX = stageAbsPos.x % scaledGridSize;
|
||||||
|
const offsetY = stageAbsPos.y % scaledGridSize;
|
||||||
|
const finalPos = {
|
||||||
|
x: roundToMultiple(newAbsPos.x, scaledGridSize) + offsetX,
|
||||||
|
y: roundToMultiple(newAbsPos.y, scaledGridSize) + offsetY,
|
||||||
|
};
|
||||||
|
console.log('scaledGridSize', scaledGridSize);
|
||||||
|
console.log('offsetX', offsetX);
|
||||||
|
console.log('offsetY', offsetY);
|
||||||
|
console.log('newAbsPosX', newAbsPos.x);
|
||||||
|
console.log('newAbsPosY', newAbsPos.y);
|
||||||
|
console.log('finalPos', finalPos);
|
||||||
|
console.log('finalPosScaled', { x: finalPos.x * stage.scaleX(), y: finalPos.y * stage.scaleY() });
|
||||||
|
|
||||||
|
return finalPos;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
bboxTransformer.on('transform', () => {
|
||||||
|
let gridSize = getMetaKey() ? 8 : 64;
|
||||||
|
|
||||||
|
if (getAltKey()) {
|
||||||
|
gridSize = gridSize * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bbox = {
|
||||||
|
x: Math.round(bboxRect.x()),
|
||||||
|
y: Math.round(bboxRect.y()),
|
||||||
|
width: roundToMultipleMin(bboxRect.width() * bboxRect.scaleX(), gridSize),
|
||||||
|
height: roundToMultipleMin(bboxRect.height() * bboxRect.scaleY(), gridSize),
|
||||||
|
};
|
||||||
|
bboxRect.setAttrs({ ...bbox, scaleX: 1, scaleY: 1 });
|
||||||
|
onBboxTransformed(bbox);
|
||||||
|
});
|
||||||
|
|
||||||
|
// The transformer will always be transforming the dummy rect
|
||||||
|
bboxTransformer.nodes([bboxRect]);
|
||||||
|
bboxPreviewGroup.add(bboxRect);
|
||||||
|
bboxPreviewGroup.add(bboxTransformer);
|
||||||
|
previewLayer.add(bboxPreviewGroup);
|
||||||
|
return bboxPreviewGroup;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ALL_ANCHORS: string[] = [
|
||||||
|
'top-left',
|
||||||
|
'top-center',
|
||||||
|
'top-right',
|
||||||
|
'middle-right',
|
||||||
|
'middle-left',
|
||||||
|
'bottom-left',
|
||||||
|
'bottom-center',
|
||||||
|
'bottom-right',
|
||||||
|
];
|
||||||
|
const NO_ANCHORS: string[] = [];
|
||||||
|
|
||||||
|
export const renderBboxPreview = (
|
||||||
|
stage: Konva.Stage,
|
||||||
|
bbox: IRect,
|
||||||
|
tool: Tool,
|
||||||
|
getBbox: () => IRect,
|
||||||
|
onBboxTransformed: (bbox: IRect) => void,
|
||||||
|
getShiftKey: () => boolean,
|
||||||
|
getMetaKey: () => boolean,
|
||||||
|
getAltKey: () => boolean
|
||||||
|
): void => {
|
||||||
|
const bboxGroup = getBboxPreviewGroup(stage, getBbox, onBboxTransformed, getShiftKey, getMetaKey, getAltKey);
|
||||||
|
const bboxRect = bboxGroup.findOne<Konva.Rect>(`#${PREVIEW_GENERATION_BBOX_DUMMY_RECT}`);
|
||||||
|
const bboxTransformer = bboxGroup.findOne<Konva.Transformer>(`#${PREVIEW_GENERATION_BBOX_TRANSFORMER}`);
|
||||||
|
bboxRect?.setAttrs({ ...bbox, listening: tool === 'move' });
|
||||||
|
bboxTransformer?.setAttrs({
|
||||||
|
listening: tool === 'move',
|
||||||
|
enabledAnchors: tool === 'move' ? ALL_ANCHORS : NO_ANCHORS,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getToolPreviewGroup = (stage: Konva.Stage): Konva.Group => {
|
||||||
|
const previewLayer = getPreviewLayer(stage);
|
||||||
|
let toolPreviewGroup = previewLayer.findOne<Konva.Group>(`#${PREVIEW_TOOL_GROUP_ID}`);
|
||||||
|
if (toolPreviewGroup) {
|
||||||
|
return toolPreviewGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
toolPreviewGroup = new Konva.Group({ id: PREVIEW_TOOL_GROUP_ID });
|
||||||
|
|
||||||
// Create the brush preview group & circles
|
// Create the brush preview group & circles
|
||||||
const brushPreviewGroup = new Konva.Group({ id: PREVIEW_BRUSH_GROUP_ID });
|
const brushPreviewGroup = new Konva.Group({ id: PREVIEW_BRUSH_GROUP_ID });
|
||||||
@ -74,114 +231,10 @@ const getPreviewLayer = (
|
|||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const toolGroup = new Konva.Group({ id: PREVIEW_TOOL_GROUP_ID });
|
toolPreviewGroup.add(rectPreview);
|
||||||
|
toolPreviewGroup.add(brushPreviewGroup);
|
||||||
toolGroup.add(rectPreview);
|
previewLayer.add(toolPreviewGroup);
|
||||||
toolGroup.add(brushPreviewGroup);
|
return toolPreviewGroup;
|
||||||
|
|
||||||
// Use a transformer for the generation bbox. Transformers need some shape to transform, we will use a fully
|
|
||||||
// transparent rect for this purpose.
|
|
||||||
const generationBboxGroup = new Konva.Group({ id: PREVIEW_GENERATION_BBOX_GROUP });
|
|
||||||
const generationBboxDummyRect = new Konva.Rect({
|
|
||||||
id: PREVIEW_GENERATION_BBOX_DUMMY_RECT,
|
|
||||||
listening: false,
|
|
||||||
strokeEnabled: false,
|
|
||||||
draggable: true,
|
|
||||||
});
|
|
||||||
generationBboxDummyRect.on('dragmove', (e) => {
|
|
||||||
const bbox: IRect = {
|
|
||||||
x: roundToMultiple(Math.round(generationBboxDummyRect.x()), 64),
|
|
||||||
y: roundToMultiple(Math.round(generationBboxDummyRect.y()), 64),
|
|
||||||
width: Math.round(generationBboxDummyRect.width() * generationBboxDummyRect.scaleX()),
|
|
||||||
height: Math.round(generationBboxDummyRect.height() * generationBboxDummyRect.scaleY()),
|
|
||||||
};
|
|
||||||
generationBboxDummyRect.setAttrs(bbox);
|
|
||||||
const genBbox = $genBbox.get();
|
|
||||||
if (
|
|
||||||
genBbox.x !== bbox.x ||
|
|
||||||
genBbox.y !== bbox.y ||
|
|
||||||
genBbox.width !== bbox.width ||
|
|
||||||
genBbox.height !== bbox.height
|
|
||||||
) {
|
|
||||||
onBboxTransformed(bbox);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const generationBboxTransformer = new Konva.Transformer({
|
|
||||||
id: PREVIEW_GENERATION_BBOX_TRANSFORMER,
|
|
||||||
borderDash: [5, 5],
|
|
||||||
borderStroke: 'rgba(212,216,234,1)',
|
|
||||||
borderEnabled: true,
|
|
||||||
rotateEnabled: false,
|
|
||||||
keepRatio: false,
|
|
||||||
ignoreStroke: true,
|
|
||||||
listening: false,
|
|
||||||
flipEnabled: false,
|
|
||||||
anchorFill: 'rgba(212,216,234,1)',
|
|
||||||
anchorStroke: 'rgb(42,42,42)',
|
|
||||||
anchorSize: 12,
|
|
||||||
anchorCornerRadius: 3,
|
|
||||||
anchorStyleFunc: (anchor) => {
|
|
||||||
// Make the x/y resize anchors little bars
|
|
||||||
if (anchor.hasName('top-center') || anchor.hasName('bottom-center')) {
|
|
||||||
anchor.height(8);
|
|
||||||
anchor.offsetY(4);
|
|
||||||
anchor.width(30);
|
|
||||||
anchor.offsetX(15);
|
|
||||||
}
|
|
||||||
if (anchor.hasName('middle-left') || anchor.hasName('middle-right')) {
|
|
||||||
anchor.height(30);
|
|
||||||
anchor.offsetY(15);
|
|
||||||
anchor.width(8);
|
|
||||||
anchor.offsetX(4);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
generationBboxTransformer.on('transform', (e) => {
|
|
||||||
const bbox: IRect = {
|
|
||||||
x: Math.round(generationBboxDummyRect.x()),
|
|
||||||
y: Math.round(generationBboxDummyRect.y()),
|
|
||||||
width: Math.round(generationBboxDummyRect.width() * generationBboxDummyRect.scaleX()),
|
|
||||||
height: Math.round(generationBboxDummyRect.height() * generationBboxDummyRect.scaleY()),
|
|
||||||
};
|
|
||||||
generationBboxDummyRect.setAttrs({ ...bbox, scaleX: 1, scaleY: 1 });
|
|
||||||
onBboxTransformed(bbox);
|
|
||||||
});
|
|
||||||
// The transformer will always be transforming the dummy rect
|
|
||||||
generationBboxTransformer.nodes([generationBboxDummyRect]);
|
|
||||||
generationBboxGroup.add(generationBboxDummyRect);
|
|
||||||
generationBboxGroup.add(generationBboxTransformer);
|
|
||||||
previewLayer.add(toolGroup);
|
|
||||||
previewLayer.add(generationBboxGroup);
|
|
||||||
|
|
||||||
return previewLayer;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ALL_ANCHORS: string[] = [
|
|
||||||
'top-left',
|
|
||||||
'top-center',
|
|
||||||
'top-right',
|
|
||||||
'middle-right',
|
|
||||||
'middle-left',
|
|
||||||
'bottom-left',
|
|
||||||
'bottom-center',
|
|
||||||
'bottom-right',
|
|
||||||
];
|
|
||||||
const NO_ANCHORS: string[] = [];
|
|
||||||
|
|
||||||
export const renderImageDimsPreview = (stage: Konva.Stage, bbox: IRect, tool: Tool): void => {
|
|
||||||
const previewLayer = stage.findOne<Konva.Layer>(`#${PREVIEW_LAYER_ID}`);
|
|
||||||
const generationBboxGroup = stage.findOne<Konva.Rect>(`#${PREVIEW_GENERATION_BBOX_GROUP}`);
|
|
||||||
const generationBboxDummyRect = stage.findOne<Konva.Rect>(`#${PREVIEW_GENERATION_BBOX_DUMMY_RECT}`);
|
|
||||||
const generationBboxTransformer = stage.findOne<Konva.Transformer>(`#${PREVIEW_GENERATION_BBOX_TRANSFORMER}`);
|
|
||||||
assert(
|
|
||||||
previewLayer && generationBboxGroup && generationBboxDummyRect && generationBboxTransformer,
|
|
||||||
'Generation bbox konva objects not found'
|
|
||||||
);
|
|
||||||
generationBboxDummyRect.setAttrs({ ...bbox, scaleX: 1, scaleY: 1, listening: tool === 'move' });
|
|
||||||
generationBboxTransformer.setAttrs({
|
|
||||||
listening: tool === 'move',
|
|
||||||
enabledAnchors: tool === 'move' ? ALL_ANCHORS : NO_ANCHORS,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,7 +248,7 @@ export const renderImageDimsPreview = (stage: Konva.Stage, bbox: IRect, tool: To
|
|||||||
* @param lastMouseDownPos The position of the last mouse down event - used for the rect tool
|
* @param lastMouseDownPos The position of the last mouse down event - used for the rect tool
|
||||||
* @param brushSize The brush size
|
* @param brushSize The brush size
|
||||||
*/
|
*/
|
||||||
export const renderPreviewLayer = (
|
export const renderToolPreview = (
|
||||||
stage: Konva.Stage,
|
stage: Konva.Stage,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
brushColor: RgbaColor,
|
brushColor: RgbaColor,
|
||||||
@ -205,9 +258,7 @@ export const renderPreviewLayer = (
|
|||||||
lastMouseDownPos: Vector2d | null,
|
lastMouseDownPos: Vector2d | null,
|
||||||
brushSize: number,
|
brushSize: number,
|
||||||
isDrawing: boolean,
|
isDrawing: boolean,
|
||||||
isMouseDown: boolean,
|
isMouseDown: boolean
|
||||||
$genBbox: WritableAtom<IRect>,
|
|
||||||
onBboxTransformed: (bbox: IRect) => void
|
|
||||||
): void => {
|
): void => {
|
||||||
const layerCount = stage.find(selectRenderableLayers).length;
|
const layerCount = stage.find(selectRenderableLayers).length;
|
||||||
// Update the stage's pointer style
|
// Update the stage's pointer style
|
||||||
@ -233,10 +284,7 @@ export const renderPreviewLayer = (
|
|||||||
|
|
||||||
stage.draggable(tool === 'view');
|
stage.draggable(tool === 'view');
|
||||||
|
|
||||||
const previewLayer = getPreviewLayer(stage, $genBbox, onBboxTransformed);
|
const toolPreviewGroup = getToolPreviewGroup(stage);
|
||||||
const toolGroup = previewLayer.findOne<Konva.Group>(`#${PREVIEW_TOOL_GROUP_ID}`);
|
|
||||||
|
|
||||||
assert(toolGroup, 'Tool group not found');
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!cursorPos ||
|
!cursorPos ||
|
||||||
@ -244,9 +292,9 @@ export const renderPreviewLayer = (
|
|||||||
(selectedLayerType !== 'regional_guidance_layer' && selectedLayerType !== 'raster_layer')
|
(selectedLayerType !== 'regional_guidance_layer' && selectedLayerType !== 'raster_layer')
|
||||||
) {
|
) {
|
||||||
// We can bail early if the mouse isn't over the stage or there are no layers
|
// We can bail early if the mouse isn't over the stage or there are no layers
|
||||||
toolGroup.visible(false);
|
toolPreviewGroup.visible(false);
|
||||||
} else {
|
} else {
|
||||||
toolGroup.visible(true);
|
toolPreviewGroup.visible(true);
|
||||||
|
|
||||||
const brushPreviewGroup = stage.findOne<Konva.Group>(`#${PREVIEW_BRUSH_GROUP_ID}`);
|
const brushPreviewGroup = stage.findOne<Konva.Group>(`#${PREVIEW_BRUSH_GROUP_ID}`);
|
||||||
assert(brushPreviewGroup, 'Brush preview group not found');
|
assert(brushPreviewGroup, 'Brush preview group not found');
|
||||||
@ -267,11 +315,11 @@ export const renderPreviewLayer = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Update the inner border of the brush preview
|
// Update the inner border of the brush preview
|
||||||
const brushPreviewInner = previewLayer.findOne<Konva.Circle>(`#${PREVIEW_BRUSH_BORDER_INNER_ID}`);
|
const brushPreviewInner = brushPreviewGroup.findOne<Konva.Circle>(`#${PREVIEW_BRUSH_BORDER_INNER_ID}`);
|
||||||
brushPreviewInner?.setAttrs({ x: cursorPos.x, y: cursorPos.y, radius: brushSize / 2 });
|
brushPreviewInner?.setAttrs({ x: cursorPos.x, y: cursorPos.y, radius: brushSize / 2 });
|
||||||
|
|
||||||
// Update the outer border of the brush preview
|
// Update the outer border of the brush preview
|
||||||
const brushPreviewOuter = previewLayer.findOne<Konva.Circle>(`#${PREVIEW_BRUSH_BORDER_OUTER_ID}`);
|
const brushPreviewOuter = brushPreviewGroup.findOne<Konva.Circle>(`#${PREVIEW_BRUSH_BORDER_OUTER_ID}`);
|
||||||
brushPreviewOuter?.setAttrs({
|
brushPreviewOuter?.setAttrs({
|
||||||
x: cursorPos.x,
|
x: cursorPos.x,
|
||||||
y: cursorPos.y,
|
y: cursorPos.y,
|
||||||
@ -285,7 +333,7 @@ export const renderPreviewLayer = (
|
|||||||
|
|
||||||
if (cursorPos && lastMouseDownPos && tool === 'rect') {
|
if (cursorPos && lastMouseDownPos && tool === 'rect') {
|
||||||
const snappedPos = snapPosToStage(cursorPos, stage);
|
const snappedPos = snapPosToStage(cursorPos, stage);
|
||||||
const rectPreview = previewLayer.findOne<Konva.Rect>(`#${PREVIEW_RECT_ID}`);
|
const rectPreview = brushPreviewGroup.findOne<Konva.Rect>(`#${PREVIEW_RECT_ID}`);
|
||||||
rectPreview?.setAttrs({
|
rectPreview?.setAttrs({
|
||||||
x: Math.min(snappedPos.x, lastMouseDownPos.x),
|
x: Math.min(snappedPos.x, lastMouseDownPos.x),
|
||||||
y: Math.min(snappedPos.y, lastMouseDownPos.y),
|
y: Math.min(snappedPos.y, lastMouseDownPos.y),
|
||||||
|
@ -92,8 +92,12 @@ export const initialControlLayersState: ControlLayersState = {
|
|||||||
height: 512,
|
height: 512,
|
||||||
aspectRatio: deepClone(initialAspectRatioState),
|
aspectRatio: deepClone(initialAspectRatioState),
|
||||||
},
|
},
|
||||||
|
bbox: {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
width: 512,
|
||||||
|
height: 512,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -800,10 +804,7 @@ export const controlLayersSlice = createSlice({
|
|||||||
state.size.aspectRatio = action.payload;
|
state.size.aspectRatio = action.payload;
|
||||||
},
|
},
|
||||||
bboxChanged: (state, action: PayloadAction<IRect>) => {
|
bboxChanged: (state, action: PayloadAction<IRect>) => {
|
||||||
state.x = action.payload.x;
|
state.bbox = action.payload;
|
||||||
state.y = action.payload.y;
|
|
||||||
state.size.width = action.payload.width;
|
|
||||||
state.size.height = action.payload.height;
|
|
||||||
},
|
},
|
||||||
brushSizeChanged: (state, action: PayloadAction<number>) => {
|
brushSizeChanged: (state, action: PayloadAction<number>) => {
|
||||||
state.brushSize = Math.round(action.payload);
|
state.brushSize = Math.round(action.payload);
|
||||||
@ -998,7 +999,6 @@ export const $lastAddedPoint = atom<Vector2d | null>(null);
|
|||||||
export const $isSpaceDown = atom(false);
|
export const $isSpaceDown = atom(false);
|
||||||
export const $stageScale = atom<number>(1);
|
export const $stageScale = atom<number>(1);
|
||||||
export const $stagePos = atom<Vector2d>({ x: 0, y: 0 });
|
export const $stagePos = atom<Vector2d>({ x: 0, y: 0 });
|
||||||
export const $genBbox = atom<IRect>({ x: 0, y: 0, width: 0, height: 0 });
|
|
||||||
|
|
||||||
// Some nanostores that are manually synced to redux state to provide imperative access
|
// Some nanostores that are manually synced to redux state to provide imperative access
|
||||||
// TODO(psyche): This is a hack, figure out another way to handle this...
|
// TODO(psyche): This is a hack, figure out another way to handle this...
|
||||||
@ -1007,12 +1007,13 @@ export const $brushColor = atom<RgbaColor>(DEFAULT_RGBA_COLOR);
|
|||||||
export const $brushSpacingPx = atom<number>(0);
|
export const $brushSpacingPx = atom<number>(0);
|
||||||
export const $selectedLayer = atom<Layer | null>(null);
|
export const $selectedLayer = atom<Layer | null>(null);
|
||||||
export const $shouldInvertBrushSizeScrollDirection = atom(false);
|
export const $shouldInvertBrushSizeScrollDirection = atom(false);
|
||||||
|
export const $bbox = atom<IRect>({ x: 0, y: 0, width: 0, height: 0 });
|
||||||
|
|
||||||
export const controlLayersPersistConfig: PersistConfig<ControlLayersState> = {
|
export const controlLayersPersistConfig: PersistConfig<ControlLayersState> = {
|
||||||
name: controlLayersSlice.name,
|
name: controlLayersSlice.name,
|
||||||
initialState: initialControlLayersState,
|
initialState: initialControlLayersState,
|
||||||
migrate: migrateControlLayersState,
|
migrate: migrateControlLayersState,
|
||||||
persistDenylist: [],
|
persistDenylist: ['bbox'],
|
||||||
};
|
};
|
||||||
|
|
||||||
// These actions are _individually_ grouped together as single undoable actions
|
// These actions are _individually_ grouped together as single undoable actions
|
||||||
|
@ -269,8 +269,7 @@ export type ControlLayersState = {
|
|||||||
height: ParameterHeight;
|
height: ParameterHeight;
|
||||||
aspectRatio: AspectRatioState;
|
aspectRatio: AspectRatioState;
|
||||||
};
|
};
|
||||||
x: number;
|
bbox: IRect;
|
||||||
y: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AddEraserLineArg = { layerId: string; points: [number, number, number, number] };
|
export type AddEraserLineArg = { layerId: string; points: [number, number, number, number] };
|
||||||
|
Loading…
Reference in New Issue
Block a user