mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): layers manage their own bbox
This commit is contained in:
parent
915357a6c1
commit
1985944659
@ -281,15 +281,6 @@ const useStageRenderer = (
|
|||||||
state.size.height,
|
state.size.height,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
log.trace('Rendering bbox');
|
|
||||||
if (asPreview) {
|
|
||||||
// Preview should not display bboxes
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
renderers.renderBboxes(stage, state.layers, tool);
|
|
||||||
}, [stage, asPreview, state.layers, tool, onBboxChanged, renderers]);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (asPreview) {
|
if (asPreview) {
|
||||||
// Preview should not check for transparency
|
// Preview should not check for transparency
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
import openBase64ImageInTab from 'common/util/openBase64ImageInTab';
|
||||||
import { imageDataToDataURL } from 'features/canvas/util/blobToDataURL';
|
import { imageDataToDataURL } from 'features/canvas/util/blobToDataURL';
|
||||||
import { BBOX_SELECTED_STROKE } from 'features/controlLayers/konva/constants';
|
|
||||||
import {
|
import {
|
||||||
getLayerBboxId,
|
|
||||||
LAYER_BBOX_NAME,
|
LAYER_BBOX_NAME,
|
||||||
RASTER_LAYER_OBJECT_GROUP_NAME,
|
RASTER_LAYER_OBJECT_GROUP_NAME,
|
||||||
RG_LAYER_OBJECT_GROUP_NAME,
|
RG_LAYER_OBJECT_GROUP_NAME,
|
||||||
} from 'features/controlLayers/konva/naming';
|
} from 'features/controlLayers/konva/naming';
|
||||||
import type { Layer, Tool } from 'features/controlLayers/store/types';
|
import { createBboxRect } from 'features/controlLayers/konva/renderers/objects';
|
||||||
|
import type { Layer } from 'features/controlLayers/store/types';
|
||||||
import { isRegionalGuidanceLayer, isRGOrRasterlayer } from 'features/controlLayers/store/types';
|
import { isRegionalGuidanceLayer, isRGOrRasterlayer } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect } from 'konva/lib/types';
|
import type { IRect } from 'konva/lib/types';
|
||||||
@ -175,22 +174,6 @@ export const getLayerBboxFast = (layer: Konva.Layer): IRect => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a bounding box rect for a layer.
|
|
||||||
* @param layerState The layer state for the layer to create the bounding box for
|
|
||||||
* @param konvaLayer The konva layer to attach the bounding box to
|
|
||||||
*/
|
|
||||||
const createBboxRect = (layerState: Layer, konvaLayer: Konva.Layer): Konva.Rect => {
|
|
||||||
const rect = new Konva.Rect({
|
|
||||||
id: getLayerBboxId(layerState.id),
|
|
||||||
name: LAYER_BBOX_NAME,
|
|
||||||
strokeWidth: 1,
|
|
||||||
visible: false,
|
|
||||||
});
|
|
||||||
konvaLayer.add(rect);
|
|
||||||
return rect;
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterRGChildren = (node: Konva.Node): boolean => node.name() === RG_LAYER_OBJECT_GROUP_NAME;
|
const filterRGChildren = (node: Konva.Node): boolean => node.name() === RG_LAYER_OBJECT_GROUP_NAME;
|
||||||
const filterRasterChildren = (node: Konva.Node): boolean => node.name() === RASTER_LAYER_OBJECT_GROUP_NAME;
|
const filterRasterChildren = (node: Konva.Node): boolean => node.name() === RASTER_LAYER_OBJECT_GROUP_NAME;
|
||||||
|
|
||||||
@ -230,42 +213,3 @@ export const updateBboxes = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the bounding boxes for the layers.
|
|
||||||
* @param stage The konva stage
|
|
||||||
* @param layerStates An array of layers to draw bboxes for
|
|
||||||
* @param tool The current tool
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const renderBboxes = (stage: Konva.Stage, layerStates: Layer[], tool: Tool): void => {
|
|
||||||
// Hide all bboxes so they don't interfere with getClientRect
|
|
||||||
for (const bboxRect of stage.find<Konva.Rect>(`.${LAYER_BBOX_NAME}`)) {
|
|
||||||
bboxRect.visible(false);
|
|
||||||
bboxRect.listening(false);
|
|
||||||
}
|
|
||||||
// No selected layer or not using the move tool - nothing more to do here
|
|
||||||
if (tool !== 'move') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const layer of layerStates.filter(isRGOrRasterlayer)) {
|
|
||||||
if (!layer.bbox) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const konvaLayer = stage.findOne<Konva.Layer>(`#${layer.id}`);
|
|
||||||
assert(konvaLayer, `Layer ${layer.id} not found in stage`);
|
|
||||||
|
|
||||||
const bboxRect = konvaLayer.findOne<Konva.Rect>(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(layer, konvaLayer);
|
|
||||||
|
|
||||||
bboxRect.setAttrs({
|
|
||||||
visible: !layer.bboxNeedsUpdate,
|
|
||||||
listening: layer.isSelected,
|
|
||||||
x: layer.bbox.x,
|
|
||||||
y: layer.bbox.y,
|
|
||||||
width: layer.bbox.width,
|
|
||||||
height: layer.bbox.height,
|
|
||||||
stroke: layer.isSelected ? BBOX_SELECTED_STROKE : '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { DEBOUNCE_MS } from 'features/controlLayers/konva/constants';
|
import { DEBOUNCE_MS } from 'features/controlLayers/konva/constants';
|
||||||
import { BACKGROUND_LAYER_ID, TOOL_PREVIEW_LAYER_ID } from 'features/controlLayers/konva/naming';
|
import { BACKGROUND_LAYER_ID, TOOL_PREVIEW_LAYER_ID } from 'features/controlLayers/konva/naming';
|
||||||
import { renderBackground } from 'features/controlLayers/konva/renderers/background';
|
import { renderBackground } from 'features/controlLayers/konva/renderers/background';
|
||||||
import { renderBboxes, 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 { renderNoLayersMessage } from 'features/controlLayers/konva/renderers/noLayersMessage';
|
import { renderNoLayersMessage } from 'features/controlLayers/konva/renderers/noLayersMessage';
|
||||||
@ -90,7 +90,6 @@ const renderLayers = (
|
|||||||
export const renderers = {
|
export const renderers = {
|
||||||
renderToolPreview,
|
renderToolPreview,
|
||||||
renderLayers,
|
renderLayers,
|
||||||
renderBboxes,
|
|
||||||
renderBackground,
|
renderBackground,
|
||||||
renderNoLayersMessage,
|
renderNoLayersMessage,
|
||||||
arrangeLayers,
|
arrangeLayers,
|
||||||
@ -105,7 +104,6 @@ export const renderers = {
|
|||||||
const getDebouncedRenderers = (ms = DEBOUNCE_MS): typeof renderers => ({
|
const getDebouncedRenderers = (ms = DEBOUNCE_MS): typeof renderers => ({
|
||||||
renderToolPreview: debounce(renderToolPreview, ms),
|
renderToolPreview: debounce(renderToolPreview, ms),
|
||||||
renderLayers: debounce(renderLayers, ms),
|
renderLayers: debounce(renderLayers, ms),
|
||||||
renderBboxes: debounce(renderBboxes, ms),
|
|
||||||
renderBackground: debounce(renderBackground, ms),
|
renderBackground: debounce(renderBackground, ms),
|
||||||
renderNoLayersMessage: debounce(renderNoLayersMessage, ms),
|
renderNoLayersMessage: debounce(renderNoLayersMessage, ms),
|
||||||
arrangeLayers: debounce(arrangeLayers, ms),
|
arrangeLayers: debounce(arrangeLayers, ms),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
import { getObjectGroupId } from 'features/controlLayers/konva/naming';
|
import { getLayerBboxId, getObjectGroupId, LAYER_BBOX_NAME } from 'features/controlLayers/konva/naming';
|
||||||
import type { BrushLine, EraserLine, ImageObject, RectShape } from 'features/controlLayers/store/types';
|
import type { BrushLine, EraserLine, ImageObject, Layer, RectShape } from 'features/controlLayers/store/types';
|
||||||
import { DEFAULT_RGBA_COLOR } from 'features/controlLayers/store/types';
|
import { DEFAULT_RGBA_COLOR } from 'features/controlLayers/store/types';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
@ -193,6 +193,23 @@ export const createImageObjectGroup = async (
|
|||||||
});
|
});
|
||||||
return konvaImageGroup;
|
return konvaImageGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a bounding box rect for a layer.
|
||||||
|
* @param layerState The layer state for the layer to create the bounding box for
|
||||||
|
* @param konvaLayer The konva layer to attach the bounding box to
|
||||||
|
*/
|
||||||
|
export const createBboxRect = (layerState: Layer, konvaLayer: Konva.Layer): Konva.Rect => {
|
||||||
|
const rect = new Konva.Rect({
|
||||||
|
id: getLayerBboxId(layerState.id),
|
||||||
|
name: LAYER_BBOX_NAME,
|
||||||
|
strokeWidth: 1,
|
||||||
|
visible: false,
|
||||||
|
});
|
||||||
|
konvaLayer.add(rect);
|
||||||
|
return rect;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a konva group for a layer's objects.
|
* Creates a konva group for a layer's objects.
|
||||||
* @param konvaLayer The konva layer to add the object group to
|
* @param konvaLayer The konva layer to add the object group to
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { BBOX_SELECTED_STROKE } from 'features/controlLayers/konva/constants';
|
||||||
import {
|
import {
|
||||||
|
LAYER_BBOX_NAME,
|
||||||
RASTER_LAYER_BRUSH_LINE_NAME,
|
RASTER_LAYER_BRUSH_LINE_NAME,
|
||||||
RASTER_LAYER_ERASER_LINE_NAME,
|
RASTER_LAYER_ERASER_LINE_NAME,
|
||||||
RASTER_LAYER_IMAGE_NAME,
|
RASTER_LAYER_IMAGE_NAME,
|
||||||
@ -7,6 +9,7 @@ import {
|
|||||||
RASTER_LAYER_RECT_SHAPE_NAME,
|
RASTER_LAYER_RECT_SHAPE_NAME,
|
||||||
} from 'features/controlLayers/konva/naming';
|
} from 'features/controlLayers/konva/naming';
|
||||||
import {
|
import {
|
||||||
|
createBboxRect,
|
||||||
createBrushLine,
|
createBrushLine,
|
||||||
createEraserLine,
|
createEraserLine,
|
||||||
createImageObjectGroup,
|
createImageObjectGroup,
|
||||||
@ -141,5 +144,22 @@ export const renderRasterLayer = async (
|
|||||||
konvaLayer.visible(layerState.isEnabled);
|
konvaLayer.visible(layerState.isEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bboxRect = konvaLayer.findOne<Konva.Rect>(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(layerState, konvaLayer);
|
||||||
|
|
||||||
|
if (layerState.bbox) {
|
||||||
|
const active = !layerState.bboxNeedsUpdate && layerState.isSelected && tool === 'move';
|
||||||
|
bboxRect.setAttrs({
|
||||||
|
visible: active,
|
||||||
|
listening: active,
|
||||||
|
x: layerState.bbox.x,
|
||||||
|
y: layerState.bbox.y,
|
||||||
|
width: layerState.bbox.width,
|
||||||
|
height: layerState.bbox.height,
|
||||||
|
stroke: layerState.isSelected ? BBOX_SELECTED_STROKE : '',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
bboxRect.visible(false);
|
||||||
|
}
|
||||||
|
|
||||||
konvaObjectGroup.opacity(layerState.opacity);
|
konvaObjectGroup.opacity(layerState.opacity);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
||||||
|
import { BBOX_SELECTED_STROKE } from 'features/controlLayers/konva/constants';
|
||||||
import {
|
import {
|
||||||
COMPOSITING_RECT_NAME,
|
COMPOSITING_RECT_NAME,
|
||||||
|
LAYER_BBOX_NAME,
|
||||||
RG_LAYER_BRUSH_LINE_NAME,
|
RG_LAYER_BRUSH_LINE_NAME,
|
||||||
RG_LAYER_ERASER_LINE_NAME,
|
RG_LAYER_ERASER_LINE_NAME,
|
||||||
RG_LAYER_NAME,
|
RG_LAYER_NAME,
|
||||||
@ -9,6 +11,7 @@ import {
|
|||||||
} from 'features/controlLayers/konva/naming';
|
} from 'features/controlLayers/konva/naming';
|
||||||
import { getLayerBboxFast } from 'features/controlLayers/konva/renderers/bbox';
|
import { getLayerBboxFast } from 'features/controlLayers/konva/renderers/bbox';
|
||||||
import {
|
import {
|
||||||
|
createBboxRect,
|
||||||
createBrushLine,
|
createBrushLine,
|
||||||
createEraserLine,
|
createEraserLine,
|
||||||
createObjectGroup,
|
createObjectGroup,
|
||||||
@ -227,4 +230,21 @@ export const renderRGLayer = (
|
|||||||
// Updating group opacity does not require re-caching
|
// Updating group opacity does not require re-caching
|
||||||
konvaObjectGroup.opacity(globalMaskLayerOpacity);
|
konvaObjectGroup.opacity(globalMaskLayerOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bboxRect = konvaLayer.findOne<Konva.Rect>(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(layerState, konvaLayer);
|
||||||
|
|
||||||
|
if (layerState.bbox) {
|
||||||
|
const active = !layerState.bboxNeedsUpdate && layerState.isSelected && tool === 'move';
|
||||||
|
bboxRect.setAttrs({
|
||||||
|
visible: active,
|
||||||
|
listening: active,
|
||||||
|
x: layerState.bbox.x,
|
||||||
|
y: layerState.bbox.y,
|
||||||
|
width: layerState.bbox.width,
|
||||||
|
height: layerState.bbox.height,
|
||||||
|
stroke: layerState.isSelected ? BBOX_SELECTED_STROKE : '',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
bboxRect.visible(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user