mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): raster layer reset, object group util
This commit is contained in:
parent
7595d05191
commit
3870ebdf29
@ -29,6 +29,9 @@ export const LayerMenu = memo(({ layerId }: Props) => {
|
||||
layerType === 'raster_layer'
|
||||
);
|
||||
}, [layerType]);
|
||||
const shouldShowResetAction = useMemo(() => {
|
||||
return layerType === 'regional_guidance_layer' || layerType === 'raster_layer';
|
||||
}, [layerType]);
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
@ -52,7 +55,7 @@ export const LayerMenu = memo(({ layerId }: Props) => {
|
||||
<MenuDivider />
|
||||
</>
|
||||
)}
|
||||
{layerType === 'regional_guidance_layer' && (
|
||||
{shouldShowResetAction && (
|
||||
<MenuItem onClick={resetLayer} icon={<PiArrowCounterClockwiseBold />}>
|
||||
{t('accessibility.reset')}
|
||||
</MenuItem>
|
||||
|
@ -14,21 +14,27 @@ export const BACKGROUND_RECT_ID = 'background_layer.rect';
|
||||
export const NO_LAYERS_MESSAGE_LAYER_ID = 'no_layers_message';
|
||||
|
||||
// Names for Konva layers and objects (comparable to CSS classes)
|
||||
export const LAYER_BBOX_NAME = 'layer.bbox';
|
||||
export const COMPOSITING_RECT_NAME = 'compositing-rect';
|
||||
|
||||
export const CA_LAYER_NAME = 'control_adapter_layer';
|
||||
export const CA_LAYER_IMAGE_NAME = 'control_adapter_layer.image';
|
||||
export const RG_LAYER_NAME = 'regional_guidance_layer';
|
||||
export const RG_LAYER_LINE_NAME = 'regional_guidance_layer.line';
|
||||
export const RG_LAYER_OBJECT_GROUP_NAME = 'regional_guidance_layer.object_group';
|
||||
export const RG_LAYER_RECT_NAME = 'regional_guidance_layer.rect';
|
||||
|
||||
export const INITIAL_IMAGE_LAYER_ID = 'singleton_initial_image_layer';
|
||||
export const INITIAL_IMAGE_LAYER_NAME = 'initial_image_layer';
|
||||
export const INITIAL_IMAGE_LAYER_IMAGE_NAME = 'initial_image_layer.image';
|
||||
export const LAYER_BBOX_NAME = 'layer.bbox';
|
||||
export const COMPOSITING_RECT_NAME = 'compositing-rect';
|
||||
|
||||
export const RG_LAYER_NAME = 'regional_guidance_layer';
|
||||
export const RG_LAYER_OBJECT_GROUP_NAME = 'regional_guidance_layer.object_group';
|
||||
export const RG_LAYER_BRUSH_LINE_NAME = 'regional_guidance_layer.brush_line';
|
||||
export const RG_LAYER_ERASER_LINE_NAME = 'regional_guidance_layer.eraser_line';
|
||||
export const RG_LAYER_RECT_SHAPE_NAME = 'regional_guidance_layer.rect_shape';
|
||||
|
||||
export const RASTER_LAYER_NAME = 'raster_layer';
|
||||
export const RASTER_LAYER_LINE_NAME = 'raster_layer.line';
|
||||
export const RASTER_LAYER_OBJECT_GROUP_NAME = 'raster_layer.object_group';
|
||||
export const RASTER_LAYER_RECT_NAME = 'raster_layer.rect';
|
||||
export const RASTER_LAYER_BRUSH_LINE_NAME = 'raster_layer.brush_line';
|
||||
export const RASTER_LAYER_ERASER_LINE_NAME = 'raster_layer.eraser_line';
|
||||
export const RASTER_LAYER_RECT_SHAPE_NAME = 'raster_layer.rect_shape';
|
||||
|
||||
// Getters for non-singleton layer and object IDs
|
||||
export const getRGLayerId = (layerId: string) => `${RG_LAYER_NAME}_${layerId}`;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||
import { RG_LAYER_LINE_NAME, RG_LAYER_RECT_NAME } from 'features/controlLayers/konva/naming';
|
||||
import { getObjectGroupId } from 'features/controlLayers/konva/naming';
|
||||
import type { BrushLine, EraserLine, RectShape } from 'features/controlLayers/store/types';
|
||||
import { DEFAULT_RGBA_COLOR } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
/**
|
||||
* Utilities to create various konva objects from layer state. These are used by both the raster and regional guidance
|
||||
@ -13,12 +14,13 @@ import Konva from 'konva';
|
||||
* Creates a konva line for a brush line.
|
||||
* @param brushLine The brush line state
|
||||
* @param layerObjectGroup The konva layer's object group to add the line to
|
||||
* @param name The konva name for the line
|
||||
*/
|
||||
export const createBrushLine = (brushLine: BrushLine, layerObjectGroup: Konva.Group): Konva.Line => {
|
||||
export const createBrushLine = (brushLine: BrushLine, layerObjectGroup: Konva.Group, name: string): Konva.Line => {
|
||||
const konvaLine = new Konva.Line({
|
||||
id: brushLine.id,
|
||||
key: brushLine.id,
|
||||
name: RG_LAYER_LINE_NAME,
|
||||
name,
|
||||
strokeWidth: brushLine.strokeWidth,
|
||||
tension: 0,
|
||||
lineCap: 'round',
|
||||
@ -36,12 +38,13 @@ export const createBrushLine = (brushLine: BrushLine, layerObjectGroup: Konva.Gr
|
||||
* Creates a konva line for a eraser line.
|
||||
* @param eraserLine The eraser line state
|
||||
* @param layerObjectGroup The konva layer's object group to add the line to
|
||||
* @param name The konva name for the line
|
||||
*/
|
||||
export const createEraserLine = (eraserLine: EraserLine, layerObjectGroup: Konva.Group): Konva.Line => {
|
||||
export const createEraserLine = (eraserLine: EraserLine, layerObjectGroup: Konva.Group, name: string): Konva.Line => {
|
||||
const konvaLine = new Konva.Line({
|
||||
id: eraserLine.id,
|
||||
key: eraserLine.id,
|
||||
name: RG_LAYER_LINE_NAME,
|
||||
name,
|
||||
strokeWidth: eraserLine.strokeWidth,
|
||||
tension: 0,
|
||||
lineCap: 'round',
|
||||
@ -58,13 +61,14 @@ export const createEraserLine = (eraserLine: EraserLine, layerObjectGroup: Konva
|
||||
/**
|
||||
* Creates a konva rect for a rect shape.
|
||||
* @param rectShape The rect shape state
|
||||
* @param layerObjectGroup The konva layer's object group to add the line to
|
||||
* @param layerObjectGroup The konva layer's object group to add the rect to
|
||||
* @param name The konva name for the rect
|
||||
*/
|
||||
export const createRectShape = (rectShape: RectShape, layerObjectGroup: Konva.Group): Konva.Rect => {
|
||||
export const createRectShape = (rectShape: RectShape, layerObjectGroup: Konva.Group, name: string): Konva.Rect => {
|
||||
const konvaRect = new Konva.Rect({
|
||||
id: rectShape.id,
|
||||
key: rectShape.id,
|
||||
name: RG_LAYER_RECT_NAME,
|
||||
name,
|
||||
x: rectShape.x,
|
||||
y: rectShape.y,
|
||||
width: rectShape.width,
|
||||
@ -75,3 +79,19 @@ export const createRectShape = (rectShape: RectShape, layerObjectGroup: Konva.Gr
|
||||
layerObjectGroup.add(konvaRect);
|
||||
return konvaRect;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a konva group for a layer's objects.
|
||||
* @param konvaLayer The konva layer to add the object group to
|
||||
* @param name The konva name for the group
|
||||
* @returns
|
||||
*/
|
||||
export const createObjectGroup = (konvaLayer: Konva.Layer, name: string): Konva.Group => {
|
||||
const konvaObjectGroup = new Konva.Group({
|
||||
id: getObjectGroupId(konvaLayer.id(), uuidv4()),
|
||||
name,
|
||||
listening: false,
|
||||
});
|
||||
konvaLayer.add(konvaObjectGroup);
|
||||
return konvaObjectGroup;
|
||||
};
|
||||
|
@ -1,14 +1,19 @@
|
||||
import {
|
||||
getObjectGroupId,
|
||||
RASTER_LAYER_BRUSH_LINE_NAME,
|
||||
RASTER_LAYER_ERASER_LINE_NAME,
|
||||
RASTER_LAYER_NAME,
|
||||
RASTER_LAYER_OBJECT_GROUP_NAME,
|
||||
RASTER_LAYER_RECT_SHAPE_NAME,
|
||||
} from 'features/controlLayers/konva/naming';
|
||||
import { createBrushLine, createEraserLine, createRectShape } from 'features/controlLayers/konva/renderers/objects';
|
||||
import { getScaledFlooredCursorPosition, mapId } from 'features/controlLayers/konva/util';
|
||||
import {
|
||||
createBrushLine,
|
||||
createEraserLine,
|
||||
createObjectGroup,
|
||||
createRectShape,
|
||||
} from 'features/controlLayers/konva/renderers/objects';
|
||||
import { getScaledFlooredCursorPosition, mapId, selectRasterObjects } from 'features/controlLayers/konva/util';
|
||||
import type { RasterLayer, Tool } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import { assert } from 'tsafe';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
/**
|
||||
* Logic for creating and rendering raster layers.
|
||||
@ -59,14 +64,6 @@ const createRasterLayer = (
|
||||
return pos;
|
||||
});
|
||||
|
||||
// The object group holds all of the layer's objects (e.g. lines and rects)
|
||||
const konvaObjectGroup = new Konva.Group({
|
||||
id: getObjectGroupId(layerState.id, uuidv4()),
|
||||
name: RASTER_LAYER_OBJECT_GROUP_NAME,
|
||||
listening: false,
|
||||
});
|
||||
konvaLayer.add(konvaObjectGroup);
|
||||
|
||||
stage.add(konvaLayer);
|
||||
|
||||
return konvaLayer;
|
||||
@ -95,12 +92,15 @@ export const renderRasterLayer = (
|
||||
y: Math.floor(layerState.y),
|
||||
});
|
||||
|
||||
const konvaObjectGroup = konvaLayer.findOne<Konva.Group>(`.${RASTER_LAYER_OBJECT_GROUP_NAME}`);
|
||||
assert(konvaObjectGroup, `Object group not found for layer ${layerState.id}`);
|
||||
const konvaObjectGroup =
|
||||
konvaLayer.findOne<Konva.Group>(`.${RASTER_LAYER_OBJECT_GROUP_NAME}`) ??
|
||||
createObjectGroup(konvaLayer, RASTER_LAYER_OBJECT_GROUP_NAME);
|
||||
|
||||
const objectIds = layerState.objects.map(mapId);
|
||||
// Destroy any objects that are no longer in the redux state
|
||||
for (const objectNode of konvaObjectGroup.getChildren()) {
|
||||
// TODO(psyche): `konvaObjectGroup.getChildren()` seems to return a stale array of children, but find is never stale.
|
||||
// Should report upstream
|
||||
for (const objectNode of konvaObjectGroup.find(selectRasterObjects)) {
|
||||
if (!objectIds.includes(objectNode.id())) {
|
||||
objectNode.destroy();
|
||||
}
|
||||
@ -108,20 +108,23 @@ export const renderRasterLayer = (
|
||||
|
||||
for (const obj of layerState.objects) {
|
||||
if (obj.type === 'brush_line') {
|
||||
const konvaBrushLine = stage.findOne<Konva.Line>(`#${obj.id}`) ?? createBrushLine(obj, konvaObjectGroup);
|
||||
const konvaBrushLine =
|
||||
stage.findOne<Konva.Line>(`#${obj.id}`) ?? createBrushLine(obj, konvaObjectGroup, RASTER_LAYER_BRUSH_LINE_NAME);
|
||||
// Only update the points if they have changed.
|
||||
if (konvaBrushLine.points().length !== obj.points.length) {
|
||||
konvaBrushLine.points(obj.points);
|
||||
}
|
||||
} else if (obj.type === 'eraser_line') {
|
||||
const konvaEraserLine = stage.findOne<Konva.Line>(`#${obj.id}`) ?? createEraserLine(obj, konvaObjectGroup);
|
||||
const konvaEraserLine =
|
||||
stage.findOne<Konva.Line>(`#${obj.id}`) ??
|
||||
createEraserLine(obj, konvaObjectGroup, RASTER_LAYER_ERASER_LINE_NAME);
|
||||
// Only update the points if they have changed.
|
||||
if (konvaEraserLine.points().length !== obj.points.length) {
|
||||
konvaEraserLine.points(obj.points);
|
||||
}
|
||||
} else if (obj.type === 'rect_shape') {
|
||||
if (!stage.findOne<Konva.Rect>(`#${obj.id}`)) {
|
||||
createRectShape(obj, konvaObjectGroup);
|
||||
createRectShape(obj, konvaObjectGroup, RASTER_LAYER_RECT_SHAPE_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,22 @@
|
||||
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
||||
import {
|
||||
COMPOSITING_RECT_NAME,
|
||||
getObjectGroupId,
|
||||
RG_LAYER_BRUSH_LINE_NAME,
|
||||
RG_LAYER_ERASER_LINE_NAME,
|
||||
RG_LAYER_NAME,
|
||||
RG_LAYER_OBJECT_GROUP_NAME,
|
||||
RG_LAYER_RECT_SHAPE_NAME,
|
||||
} from 'features/controlLayers/konva/naming';
|
||||
import { getLayerBboxFast } from 'features/controlLayers/konva/renderers/bbox';
|
||||
import { createBrushLine, createEraserLine, createRectShape } from 'features/controlLayers/konva/renderers/objects';
|
||||
import {
|
||||
createBrushLine,
|
||||
createEraserLine,
|
||||
createObjectGroup,
|
||||
createRectShape,
|
||||
} from 'features/controlLayers/konva/renderers/objects';
|
||||
import { getScaledFlooredCursorPosition, mapId, selectVectorMaskObjects } from 'features/controlLayers/konva/util';
|
||||
import type { RegionalGuidanceLayer, Tool } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import { assert } from 'tsafe';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
/**
|
||||
* Logic for creating and rendering regional guidance layers.
|
||||
@ -75,14 +80,6 @@ const createRGLayer = (
|
||||
return pos;
|
||||
});
|
||||
|
||||
// The object group holds all of the layer's objects (e.g. lines and rects)
|
||||
const konvaObjectGroup = new Konva.Group({
|
||||
id: getObjectGroupId(layerState.id, uuidv4()),
|
||||
name: RG_LAYER_OBJECT_GROUP_NAME,
|
||||
listening: false,
|
||||
});
|
||||
konvaLayer.add(konvaObjectGroup);
|
||||
|
||||
stage.add(konvaLayer);
|
||||
|
||||
return konvaLayer;
|
||||
@ -116,8 +113,9 @@ export const renderRGLayer = (
|
||||
// Convert the color to a string, stripping the alpha - the object group will handle opacity.
|
||||
const rgbColor = rgbColorToString(layerState.previewColor);
|
||||
|
||||
const konvaObjectGroup = konvaLayer.findOne<Konva.Group>(`.${RG_LAYER_OBJECT_GROUP_NAME}`);
|
||||
assert(konvaObjectGroup, `Object group not found for layer ${layerState.id}`);
|
||||
const konvaObjectGroup =
|
||||
konvaLayer.findOne<Konva.Group>(`.${RG_LAYER_OBJECT_GROUP_NAME}`) ??
|
||||
createObjectGroup(konvaLayer, RG_LAYER_OBJECT_GROUP_NAME);
|
||||
|
||||
// We use caching to handle "global" layer opacity, but caching is expensive and we should only do it when required.
|
||||
let groupNeedsCache = false;
|
||||
@ -133,7 +131,8 @@ export const renderRGLayer = (
|
||||
|
||||
for (const obj of layerState.objects) {
|
||||
if (obj.type === 'brush_line') {
|
||||
const konvaBrushLine = stage.findOne<Konva.Line>(`#${obj.id}`) ?? createBrushLine(obj, konvaObjectGroup);
|
||||
const konvaBrushLine =
|
||||
stage.findOne<Konva.Line>(`#${obj.id}`) ?? createBrushLine(obj, konvaObjectGroup, RG_LAYER_BRUSH_LINE_NAME);
|
||||
|
||||
// Only update the points if they have changed. The point values are never mutated, they are only added to the
|
||||
// array, so checking the length is sufficient to determine if we need to re-cache.
|
||||
@ -147,7 +146,8 @@ export const renderRGLayer = (
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
} else if (obj.type === 'eraser_line') {
|
||||
const konvaEraserLine = stage.findOne<Konva.Line>(`#${obj.id}`) ?? createEraserLine(obj, konvaObjectGroup);
|
||||
const konvaEraserLine =
|
||||
stage.findOne<Konva.Line>(`#${obj.id}`) ?? createEraserLine(obj, konvaObjectGroup, RG_LAYER_ERASER_LINE_NAME);
|
||||
|
||||
// Only update the points if they have changed. The point values are never mutated, they are only added to the
|
||||
// array, so checking the length is sufficient to determine if we need to re-cache.
|
||||
@ -161,7 +161,8 @@ export const renderRGLayer = (
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
} else if (obj.type === 'rect_shape') {
|
||||
const konvaRectShape = stage.findOne<Konva.Rect>(`#${obj.id}`) ?? createRectShape(obj, konvaObjectGroup);
|
||||
const konvaRectShape =
|
||||
stage.findOne<Konva.Rect>(`#${obj.id}`) ?? createRectShape(obj, konvaObjectGroup, RG_LAYER_RECT_SHAPE_NAME);
|
||||
|
||||
// Only update the color if it has changed.
|
||||
if (konvaRectShape.fill() !== rgbColor) {
|
||||
|
@ -1,10 +1,14 @@
|
||||
import {
|
||||
CA_LAYER_NAME,
|
||||
INITIAL_IMAGE_LAYER_NAME,
|
||||
RASTER_LAYER_BRUSH_LINE_NAME,
|
||||
RASTER_LAYER_ERASER_LINE_NAME,
|
||||
RASTER_LAYER_NAME,
|
||||
RG_LAYER_LINE_NAME,
|
||||
RASTER_LAYER_RECT_SHAPE_NAME,
|
||||
RG_LAYER_BRUSH_LINE_NAME,
|
||||
RG_LAYER_ERASER_LINE_NAME,
|
||||
RG_LAYER_NAME,
|
||||
RG_LAYER_RECT_NAME,
|
||||
RG_LAYER_RECT_SHAPE_NAME,
|
||||
} from 'features/controlLayers/konva/naming';
|
||||
import type Konva from 'konva';
|
||||
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||
@ -89,17 +93,27 @@ export const mapId = (object: { id: string }): string => object.id;
|
||||
* Konva selection callback to select all renderable layers. This includes RG, CA II and Raster layers.
|
||||
* This can be provided to the `find` or `findOne` konva node methods.
|
||||
*/
|
||||
export const selectRenderableLayers = (n: Konva.Node): boolean =>
|
||||
n.name() === RG_LAYER_NAME ||
|
||||
n.name() === CA_LAYER_NAME ||
|
||||
n.name() === INITIAL_IMAGE_LAYER_NAME ||
|
||||
n.name() === RASTER_LAYER_NAME;
|
||||
export const selectRenderableLayers = (node: Konva.Node): boolean =>
|
||||
node.name() === RG_LAYER_NAME ||
|
||||
node.name() === CA_LAYER_NAME ||
|
||||
node.name() === INITIAL_IMAGE_LAYER_NAME ||
|
||||
node.name() === RASTER_LAYER_NAME;
|
||||
|
||||
/**
|
||||
* Konva selection callback to select RG mask objects. This includes lines and rects.
|
||||
* This can be provided to the `find` or `findOne` konva node methods.
|
||||
*/
|
||||
export const selectVectorMaskObjects = (node: Konva.Node): boolean => {
|
||||
return node.name() === RG_LAYER_LINE_NAME || node.name() === RG_LAYER_RECT_NAME;
|
||||
};
|
||||
export const selectVectorMaskObjects = (node: Konva.Node): boolean =>
|
||||
node.name() === RG_LAYER_BRUSH_LINE_NAME ||
|
||||
node.name() === RG_LAYER_ERASER_LINE_NAME ||
|
||||
node.name() === RG_LAYER_RECT_SHAPE_NAME;
|
||||
|
||||
/**
|
||||
* Konva selection callback to select raster layer objects. This includes lines and rects.
|
||||
* This can be provided to the `find` or `findOne` konva node methods.
|
||||
*/
|
||||
export const selectRasterObjects = (node: Konva.Node): boolean =>
|
||||
node.name() === RASTER_LAYER_BRUSH_LINE_NAME ||
|
||||
node.name() === RASTER_LAYER_ERASER_LINE_NAME ||
|
||||
node.name() === RASTER_LAYER_RECT_SHAPE_NAME;
|
||||
//#endregion
|
||||
|
@ -177,6 +177,12 @@ export const controlLayersSlice = createSlice({
|
||||
layer.bboxNeedsUpdate = false;
|
||||
layer.uploadedMaskImage = null;
|
||||
}
|
||||
if (isRasterLayer(layer)) {
|
||||
layer.isEnabled = true;
|
||||
layer.objects = [];
|
||||
layer.bbox = null;
|
||||
layer.bboxNeedsUpdate = false;
|
||||
}
|
||||
},
|
||||
layerDeleted: (state, action: PayloadAction<string>) => {
|
||||
state.layers = state.layers.filter((l) => l.id !== action.payload);
|
||||
|
Loading…
Reference in New Issue
Block a user