mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): even more simplified API - lean on the konva node manager to abstract imperative state API & rendering
This commit is contained in:
parent
d045f24014
commit
bd5a85bf70
@ -1,16 +1,6 @@
|
|||||||
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||||
import { getScaledFlooredCursorPosition } from 'features/controlLayers/konva/util';
|
import { getScaledFlooredCursorPosition } from 'features/controlLayers/konva/util';
|
||||||
import type {
|
import type { CanvasEntity } from 'features/controlLayers/store/types';
|
||||||
BrushLineAddedArg,
|
|
||||||
CanvasEntity,
|
|
||||||
CanvasV2State,
|
|
||||||
EraserLineAddedArg,
|
|
||||||
PointAddedToLineArg,
|
|
||||||
RectShapeAddedArg,
|
|
||||||
RgbaColor,
|
|
||||||
StageAttrs,
|
|
||||||
Tool,
|
|
||||||
} from 'features/controlLayers/store/types';
|
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
import type { Vector2d } from 'konva/lib/types';
|
import type { Vector2d } from 'konva/lib/types';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
@ -25,43 +15,16 @@ import {
|
|||||||
} from './constants';
|
} from './constants';
|
||||||
import { PREVIEW_TOOL_GROUP_ID } from './naming';
|
import { PREVIEW_TOOL_GROUP_ID } from './naming';
|
||||||
|
|
||||||
type Arg = {
|
|
||||||
manager: KonvaNodeManager;
|
|
||||||
getToolState: () => CanvasV2State['tool'];
|
|
||||||
getCurrentFill: () => RgbaColor;
|
|
||||||
setTool: (tool: Tool) => void;
|
|
||||||
setToolBuffer: (tool: Tool | null) => void;
|
|
||||||
getIsDrawing: () => boolean;
|
|
||||||
setIsDrawing: (isDrawing: boolean) => void;
|
|
||||||
getIsMouseDown: () => boolean;
|
|
||||||
setIsMouseDown: (isMouseDown: boolean) => void;
|
|
||||||
getLastMouseDownPos: () => Vector2d | null;
|
|
||||||
setLastMouseDownPos: (pos: Vector2d | null) => void;
|
|
||||||
getLastCursorPos: () => Vector2d | null;
|
|
||||||
setLastCursorPos: (pos: Vector2d | null) => void;
|
|
||||||
getLastAddedPoint: () => Vector2d | null;
|
|
||||||
setLastAddedPoint: (pos: Vector2d | null) => void;
|
|
||||||
setStageAttrs: (attrs: StageAttrs) => void;
|
|
||||||
getSelectedEntity: () => CanvasEntity | null;
|
|
||||||
getSpaceKey: () => boolean;
|
|
||||||
setSpaceKey: (val: boolean) => void;
|
|
||||||
getBbox: () => CanvasV2State['bbox'];
|
|
||||||
getSettings: () => CanvasV2State['settings'];
|
|
||||||
onBrushLineAdded: (arg: BrushLineAddedArg, entityType: CanvasEntity['type']) => void;
|
|
||||||
onEraserLineAdded: (arg: EraserLineAddedArg, entityType: CanvasEntity['type']) => void;
|
|
||||||
onPointAddedToLine: (arg: PointAddedToLineArg, entityType: CanvasEntity['type']) => void;
|
|
||||||
onRectShapeAdded: (arg: RectShapeAddedArg, entityType: CanvasEntity['type']) => void;
|
|
||||||
onBrushWidthChanged: (size: number) => void;
|
|
||||||
onEraserWidthChanged: (size: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the last cursor position atom with the current cursor position, returning the new position or `null` if the
|
* Updates the last cursor position atom with the current cursor position, returning the new position or `null` if the
|
||||||
* cursor is not over the stage.
|
* cursor is not over the stage.
|
||||||
* @param stage The konva stage
|
* @param stage The konva stage
|
||||||
* @param setLastCursorPos The callback to store the cursor pos
|
* @param setLastCursorPos The callback to store the cursor pos
|
||||||
*/
|
*/
|
||||||
const updateLastCursorPos = (stage: Konva.Stage, setLastCursorPos: Arg['setLastCursorPos']) => {
|
const updateLastCursorPos = (
|
||||||
|
stage: Konva.Stage,
|
||||||
|
setLastCursorPos: KonvaNodeManager['stateApi']['setLastCursorPos']
|
||||||
|
) => {
|
||||||
const pos = getScaledFlooredCursorPosition(stage);
|
const pos = getScaledFlooredCursorPosition(stage);
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
return null;
|
return null;
|
||||||
@ -93,10 +56,10 @@ const calculateNewBrushSize = (brushSize: number, delta: number) => {
|
|||||||
const maybeAddNextPoint = (
|
const maybeAddNextPoint = (
|
||||||
selectedEntity: CanvasEntity,
|
selectedEntity: CanvasEntity,
|
||||||
currentPos: Vector2d,
|
currentPos: Vector2d,
|
||||||
getToolState: Arg['getToolState'],
|
getToolState: KonvaNodeManager['stateApi']['getToolState'],
|
||||||
getLastAddedPoint: Arg['getLastAddedPoint'],
|
getLastAddedPoint: KonvaNodeManager['stateApi']['getLastAddedPoint'],
|
||||||
setLastAddedPoint: Arg['setLastAddedPoint'],
|
setLastAddedPoint: KonvaNodeManager['stateApi']['setLastAddedPoint'],
|
||||||
onPointAddedToLine: Arg['onPointAddedToLine']
|
onPointAddedToLine: KonvaNodeManager['stateApi']['onPointAddedToLine']
|
||||||
) => {
|
) => {
|
||||||
const isDrawableEntity =
|
const isDrawableEntity =
|
||||||
selectedEntity?.type === 'regional_guidance' ||
|
selectedEntity?.type === 'regional_guidance' ||
|
||||||
@ -132,8 +95,9 @@ const maybeAddNextPoint = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setStageEventHandlers = ({
|
export const setStageEventHandlers = (manager: KonvaNodeManager): (() => void) => {
|
||||||
manager,
|
const { stage, stateApi } = manager;
|
||||||
|
const {
|
||||||
getToolState,
|
getToolState,
|
||||||
getCurrentFill,
|
getCurrentFill,
|
||||||
setTool,
|
setTool,
|
||||||
@ -158,16 +122,15 @@ export const setStageEventHandlers = ({
|
|||||||
onEraserLineAdded,
|
onEraserLineAdded,
|
||||||
onPointAddedToLine,
|
onPointAddedToLine,
|
||||||
onRectShapeAdded,
|
onRectShapeAdded,
|
||||||
onBrushWidthChanged: onBrushSizeChanged,
|
onBrushWidthChanged,
|
||||||
onEraserWidthChanged: onEraserSizeChanged,
|
onEraserWidthChanged,
|
||||||
}: Arg): (() => void) => {
|
} = stateApi;
|
||||||
const stage = manager.stage;
|
|
||||||
|
|
||||||
//#region mouseenter
|
//#region mouseenter
|
||||||
stage.on('mouseenter', () => {
|
stage.on('mouseenter', () => {
|
||||||
const tool = getToolState().selected;
|
const tool = getToolState().selected;
|
||||||
stage.findOne<Konva.Layer>(`#${PREVIEW_TOOL_GROUP_ID}`)?.visible(tool === 'brush' || tool === 'eraser');
|
stage.findOne<Konva.Layer>(`#${PREVIEW_TOOL_GROUP_ID}`)?.visible(tool === 'brush' || tool === 'eraser');
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region mousedown
|
//#region mousedown
|
||||||
@ -288,7 +251,7 @@ export const setStageEventHandlers = ({
|
|||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region mouseup
|
//#region mouseup
|
||||||
@ -327,7 +290,7 @@ export const setStageEventHandlers = ({
|
|||||||
setLastMouseDownPos(null);
|
setLastMouseDownPos(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region mousemove
|
//#region mousemove
|
||||||
@ -433,7 +396,7 @@ export const setStageEventHandlers = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region mouseleave
|
//#region mouseleave
|
||||||
@ -462,7 +425,7 @@ export const setStageEventHandlers = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region wheel
|
//#region wheel
|
||||||
@ -477,9 +440,9 @@ export const setStageEventHandlers = ({
|
|||||||
}
|
}
|
||||||
// Holding ctrl or meta while scrolling changes the brush size
|
// Holding ctrl or meta while scrolling changes the brush size
|
||||||
if (toolState.selected === 'brush') {
|
if (toolState.selected === 'brush') {
|
||||||
onBrushSizeChanged(calculateNewBrushSize(toolState.brush.width, delta));
|
onBrushWidthChanged(calculateNewBrushSize(toolState.brush.width, delta));
|
||||||
} else if (toolState.selected === 'eraser') {
|
} else if (toolState.selected === 'eraser') {
|
||||||
onEraserSizeChanged(calculateNewBrushSize(toolState.eraser.width, delta));
|
onEraserWidthChanged(calculateNewBrushSize(toolState.eraser.width, delta));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We need the absolute cursor position - not the scaled position
|
// We need the absolute cursor position - not the scaled position
|
||||||
@ -503,11 +466,11 @@ export const setStageEventHandlers = ({
|
|||||||
stage.scaleY(newScale);
|
stage.scaleY(newScale);
|
||||||
stage.position(newPos);
|
stage.position(newPos);
|
||||||
setStageAttrs({ ...newPos, width: stage.width(), height: stage.height(), scale: newScale });
|
setStageAttrs({ ...newPos, width: stage.width(), height: stage.height(), scale: newScale });
|
||||||
manager.renderers.renderBackground();
|
manager.konvaApi.renderBackground();
|
||||||
manager.renderers.renderDocumentOverlay();
|
manager.konvaApi.renderDocumentOverlay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region dragmove
|
//#region dragmove
|
||||||
@ -519,9 +482,9 @@ export const setStageEventHandlers = ({
|
|||||||
height: stage.height(),
|
height: stage.height(),
|
||||||
scale: stage.scaleX(),
|
scale: stage.scaleX(),
|
||||||
});
|
});
|
||||||
manager.renderers.renderBackground();
|
manager.konvaApi.renderBackground();
|
||||||
manager.renderers.renderDocumentOverlay();
|
manager.konvaApi.renderDocumentOverlay();
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region dragend
|
//#region dragend
|
||||||
@ -534,7 +497,7 @@ export const setStageEventHandlers = ({
|
|||||||
height: stage.height(),
|
height: stage.height(),
|
||||||
scale: stage.scaleX(),
|
scale: stage.scaleX(),
|
||||||
});
|
});
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region key
|
//#region key
|
||||||
@ -555,12 +518,12 @@ export const setStageEventHandlers = ({
|
|||||||
setTool('view');
|
setTool('view');
|
||||||
setSpaceKey(true);
|
setSpaceKey(true);
|
||||||
} else if (e.key === 'r') {
|
} else if (e.key === 'r') {
|
||||||
manager.renderers.fitDocumentToStage();
|
manager.konvaApi.fitDocumentToStage();
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
manager.renderers.renderBackground();
|
manager.konvaApi.renderBackground();
|
||||||
manager.renderers.renderDocumentOverlay();
|
manager.konvaApi.renderDocumentOverlay();
|
||||||
}
|
}
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
};
|
};
|
||||||
window.addEventListener('keydown', onKeyDown);
|
window.addEventListener('keydown', onKeyDown);
|
||||||
|
|
||||||
@ -578,7 +541,7 @@ export const setStageEventHandlers = ({
|
|||||||
setToolBuffer(null);
|
setToolBuffer(null);
|
||||||
setSpaceKey(false);
|
setSpaceKey(false);
|
||||||
}
|
}
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
};
|
};
|
||||||
window.addEventListener('keyup', onKeyUp);
|
window.addEventListener('keyup', onKeyUp);
|
||||||
|
|
||||||
|
@ -1,5 +1,22 @@
|
|||||||
import type { BrushLine, CanvasEntity, EraserLine, ImageObject, RectShape } from 'features/controlLayers/store/types';
|
import type {
|
||||||
|
BrushLine,
|
||||||
|
BrushLineAddedArg,
|
||||||
|
CanvasEntity,
|
||||||
|
CanvasV2State,
|
||||||
|
EraserLine,
|
||||||
|
EraserLineAddedArg,
|
||||||
|
ImageObject,
|
||||||
|
PointAddedToLineArg,
|
||||||
|
PosChangedArg,
|
||||||
|
Rect,
|
||||||
|
RectShape,
|
||||||
|
RectShapeAddedArg,
|
||||||
|
RgbaColor,
|
||||||
|
StageAttrs,
|
||||||
|
Tool,
|
||||||
|
} from 'features/controlLayers/store/types';
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
|
import type { Vector2d } from 'konva/lib/types';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
export type BrushLineObjectRecord = {
|
export type BrushLineObjectRecord = {
|
||||||
@ -36,7 +53,7 @@ export type ImageObjectRecord = {
|
|||||||
|
|
||||||
type ObjectRecord = BrushLineObjectRecord | EraserLineObjectRecord | RectShapeObjectRecord | ImageObjectRecord;
|
type ObjectRecord = BrushLineObjectRecord | EraserLineObjectRecord | RectShapeObjectRecord | ImageObjectRecord;
|
||||||
|
|
||||||
type KonvaRenderers = {
|
type KonvaApi = {
|
||||||
renderRegions: () => void;
|
renderRegions: () => void;
|
||||||
renderLayers: () => void;
|
renderLayers: () => void;
|
||||||
renderControlAdapters: () => void;
|
renderControlAdapters: () => void;
|
||||||
@ -45,8 +62,9 @@ type KonvaRenderers = {
|
|||||||
renderDocumentOverlay: () => void;
|
renderDocumentOverlay: () => void;
|
||||||
renderBackground: () => void;
|
renderBackground: () => void;
|
||||||
renderToolPreview: () => void;
|
renderToolPreview: () => void;
|
||||||
fitDocumentToStage: () => void;
|
|
||||||
arrangeEntities: () => void;
|
arrangeEntities: () => void;
|
||||||
|
fitDocumentToStage: () => void;
|
||||||
|
fitStageToContainer: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type BackgroundLayer = {
|
type BackgroundLayer = {
|
||||||
@ -79,18 +97,63 @@ type PreviewLayer = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type StateApi = {
|
||||||
|
getToolState: () => CanvasV2State['tool'];
|
||||||
|
getCurrentFill: () => RgbaColor;
|
||||||
|
setTool: (tool: Tool) => void;
|
||||||
|
setToolBuffer: (tool: Tool | null) => void;
|
||||||
|
getIsDrawing: () => boolean;
|
||||||
|
setIsDrawing: (isDrawing: boolean) => void;
|
||||||
|
getIsMouseDown: () => boolean;
|
||||||
|
setIsMouseDown: (isMouseDown: boolean) => void;
|
||||||
|
getLastMouseDownPos: () => Vector2d | null;
|
||||||
|
setLastMouseDownPos: (pos: Vector2d | null) => void;
|
||||||
|
getLastCursorPos: () => Vector2d | null;
|
||||||
|
setLastCursorPos: (pos: Vector2d | null) => void;
|
||||||
|
getLastAddedPoint: () => Vector2d | null;
|
||||||
|
setLastAddedPoint: (pos: Vector2d | null) => void;
|
||||||
|
setStageAttrs: (attrs: StageAttrs) => void;
|
||||||
|
getSelectedEntity: () => CanvasEntity | null;
|
||||||
|
getSpaceKey: () => boolean;
|
||||||
|
setSpaceKey: (val: boolean) => void;
|
||||||
|
getBbox: () => CanvasV2State['bbox'];
|
||||||
|
getSettings: () => CanvasV2State['settings'];
|
||||||
|
onBrushLineAdded: (arg: BrushLineAddedArg, entityType: CanvasEntity['type']) => void;
|
||||||
|
onEraserLineAdded: (arg: EraserLineAddedArg, entityType: CanvasEntity['type']) => void;
|
||||||
|
onPointAddedToLine: (arg: PointAddedToLineArg, entityType: CanvasEntity['type']) => void;
|
||||||
|
onRectShapeAdded: (arg: RectShapeAddedArg, entityType: CanvasEntity['type']) => void;
|
||||||
|
onBrushWidthChanged: (size: number) => void;
|
||||||
|
onEraserWidthChanged: (size: number) => void;
|
||||||
|
getMaskOpacity: () => number;
|
||||||
|
onPosChanged: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void;
|
||||||
|
onBboxTransformed: (bbox: Rect) => void;
|
||||||
|
getShiftKey: () => boolean;
|
||||||
|
getCtrlKey: () => boolean;
|
||||||
|
getMetaKey: () => boolean;
|
||||||
|
getAltKey: () => boolean;
|
||||||
|
getDocument: () => CanvasV2State['document'];
|
||||||
|
getLayerEntityStates: () => CanvasV2State['layers']['entities'];
|
||||||
|
getControlAdapterEntityStates: () => CanvasV2State['controlAdapters']['entities'];
|
||||||
|
getRegionEntityStates: () => CanvasV2State['regions']['entities'];
|
||||||
|
getInpaintMaskEntityState: () => CanvasV2State['inpaintMask'];
|
||||||
|
};
|
||||||
|
|
||||||
export class KonvaNodeManager {
|
export class KonvaNodeManager {
|
||||||
stage: Konva.Stage;
|
stage: Konva.Stage;
|
||||||
|
container: HTMLDivElement;
|
||||||
adapters: Map<string, KonvaEntityAdapter>;
|
adapters: Map<string, KonvaEntityAdapter>;
|
||||||
_background: BackgroundLayer | null;
|
_background: BackgroundLayer | null;
|
||||||
_preview: PreviewLayer | null;
|
_preview: PreviewLayer | null;
|
||||||
_renderers: KonvaRenderers | null;
|
_konvaApi: KonvaApi | null;
|
||||||
|
_stateApi: StateApi | null;
|
||||||
|
|
||||||
constructor(stage: Konva.Stage) {
|
constructor(stage: Konva.Stage, container: HTMLDivElement) {
|
||||||
this.stage = stage;
|
this.stage = stage;
|
||||||
this._renderers = null;
|
this.container = container;
|
||||||
|
this._konvaApi = null;
|
||||||
this._preview = null;
|
this._preview = null;
|
||||||
this._background = null;
|
this._background = null;
|
||||||
|
this._stateApi = null;
|
||||||
this.adapters = new Map();
|
this.adapters = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,13 +184,13 @@ export class KonvaNodeManager {
|
|||||||
return this.adapters.delete(id);
|
return this.adapters.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
set renderers(renderers: KonvaRenderers) {
|
set konvaApi(konvaApi: KonvaApi) {
|
||||||
this._renderers = renderers;
|
this._konvaApi = konvaApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
get renderers(): KonvaRenderers {
|
get konvaApi(): KonvaApi {
|
||||||
assert(this._renderers !== null, 'Konva renderers have not been set');
|
assert(this._konvaApi !== null, 'Konva API has not been set');
|
||||||
return this._renderers;
|
return this._konvaApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
set preview(preview: PreviewLayer) {
|
set preview(preview: PreviewLayer) {
|
||||||
@ -147,6 +210,15 @@ export class KonvaNodeManager {
|
|||||||
assert(this._background !== null, 'Konva background layer has not been set');
|
assert(this._background !== null, 'Konva background layer has not been set');
|
||||||
return this._background;
|
return this._background;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set stateApi(stateApi: StateApi) {
|
||||||
|
this._stateApi = stateApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
get stateApi(): StateApi {
|
||||||
|
assert(this._stateApi !== null, 'State API has not been set');
|
||||||
|
return this._stateApi;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KonvaEntityAdapter {
|
export class KonvaEntityAdapter {
|
||||||
|
@ -1,23 +1,14 @@
|
|||||||
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||||
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a function to arrange the entities in the konva stage.
|
* Gets a function to arrange the entities in the konva stage.
|
||||||
* @param manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param getLayerEntityStates A function to get all layer entity states
|
|
||||||
* @param getControlAdapterEntityStates A function to get all control adapter entity states
|
|
||||||
* @param getRegionEntityStates A function to get all region entity states
|
|
||||||
* @returns An arrange entities function
|
* @returns An arrange entities function
|
||||||
*/
|
*/
|
||||||
export const getArrangeEntities =
|
export const getArrangeEntities = (manager: KonvaNodeManager) => {
|
||||||
(arg: {
|
const { getLayerEntityStates, getControlAdapterEntityStates, getRegionEntityStates } = manager.stateApi;
|
||||||
manager: KonvaNodeManager;
|
|
||||||
getLayerEntityStates: () => CanvasV2State['layers']['entities'];
|
function arrangeEntities(): void {
|
||||||
getControlAdapterEntityStates: () => CanvasV2State['controlAdapters']['entities'];
|
|
||||||
getRegionEntityStates: () => CanvasV2State['regions']['entities'];
|
|
||||||
}) =>
|
|
||||||
(): void => {
|
|
||||||
const { manager, getLayerEntityStates, getControlAdapterEntityStates, getRegionEntityStates } = arg;
|
|
||||||
const layers = getLayerEntityStates();
|
const layers = getLayerEntityStates();
|
||||||
const controlAdapters = getControlAdapterEntityStates();
|
const controlAdapters = getControlAdapterEntityStates();
|
||||||
const regions = getRegionEntityStates();
|
const regions = getRegionEntityStates();
|
||||||
@ -34,4 +25,7 @@ export const getArrangeEntities =
|
|||||||
}
|
}
|
||||||
manager.get('inpaint_mask')?.konvaLayer.zIndex(++zIndex);
|
manager.get('inpaint_mask')?.konvaLayer.zIndex(++zIndex);
|
||||||
manager.preview.layer.zIndex(++zIndex);
|
manager.preview.layer.zIndex(++zIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrangeEntities;
|
||||||
};
|
};
|
||||||
|
@ -38,11 +38,11 @@ export const createBackgroundLayer = (): Konva.Layer => new Konva.Layer({ id: BA
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a render function for the background layer.
|
* Gets a render function for the background layer.
|
||||||
* @param arg.manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @returns A function to render the background grid
|
* @returns A function to render the background grid
|
||||||
*/
|
*/
|
||||||
export const getRenderBackground = (arg: { manager: KonvaNodeManager }) => (): void => {
|
export const getRenderBackground = (manager: KonvaNodeManager) => {
|
||||||
const { manager } = arg;
|
function renderBackground(): void {
|
||||||
const background = manager.background.layer;
|
const background = manager.background.layer;
|
||||||
background.zIndex(0);
|
background.zIndex(0);
|
||||||
const scale = manager.stage.scaleX();
|
const scale = manager.stage.scaleX();
|
||||||
@ -116,4 +116,7 @@ export const getRenderBackground = (arg: { manager: KonvaNodeManager }) => (): v
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderBackground;
|
||||||
};
|
};
|
||||||
|
@ -6,16 +6,11 @@ import {
|
|||||||
createObjectGroup,
|
createObjectGroup,
|
||||||
updateImageSource,
|
updateImageSource,
|
||||||
} from 'features/controlLayers/konva/renderers/objects';
|
} from 'features/controlLayers/konva/renderers/objects';
|
||||||
import type { CanvasV2State, ControlAdapterEntity } from 'features/controlLayers/store/types';
|
import type { ControlAdapterEntity } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { assert } from 'tsafe';
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
/**
|
|
||||||
* Logic for creating and rendering control adapter (control net & t2i adapter) layers. These layers have image objects
|
|
||||||
* and require some special handling to update the source and attributes as control images are swapped or processed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a control adapter entity's konva nodes and entity adapter, creating them if they do not exist.
|
* Gets a control adapter entity's konva nodes and entity adapter, creating them if they do not exist.
|
||||||
* @param manager The konva node manager
|
* @param manager The konva node manager
|
||||||
@ -102,16 +97,12 @@ export const renderControlAdapter = async (manager: KonvaNodeManager, entity: Co
|
|||||||
/**
|
/**
|
||||||
* Gets a function to render all control adapters.
|
* Gets a function to render all control adapters.
|
||||||
* @param manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param getControlAdapterEntityStates A function to get all control adapter entities
|
|
||||||
* @returns A function to render all control adapters
|
* @returns A function to render all control adapters
|
||||||
*/
|
*/
|
||||||
export const getRenderControlAdapters =
|
export const getRenderControlAdapters = (manager: KonvaNodeManager) => {
|
||||||
(arg: {
|
const { getControlAdapterEntityStates } = manager.stateApi;
|
||||||
manager: KonvaNodeManager;
|
|
||||||
getControlAdapterEntityStates: () => CanvasV2State['controlAdapters']['entities'];
|
function renderControlAdapters(): void {
|
||||||
}) =>
|
|
||||||
(): void => {
|
|
||||||
const { manager, getControlAdapterEntityStates } = arg;
|
|
||||||
const entities = getControlAdapterEntityStates();
|
const entities = getControlAdapterEntityStates();
|
||||||
// Destroy nonexistent layers
|
// Destroy nonexistent layers
|
||||||
for (const adapters of manager.getAll('control_adapter')) {
|
for (const adapters of manager.getAll('control_adapter')) {
|
||||||
@ -122,4 +113,7 @@ export const getRenderControlAdapters =
|
|||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
renderControlAdapter(manager, entity);
|
renderControlAdapter(manager, entity);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderControlAdapters;
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
getRectShape,
|
getRectShape,
|
||||||
} from 'features/controlLayers/konva/renderers/objects';
|
} from 'features/controlLayers/konva/renderers/objects';
|
||||||
import { mapId } from 'features/controlLayers/konva/util';
|
import { mapId } from 'features/controlLayers/konva/util';
|
||||||
import type { CanvasEntity, CanvasV2State, InpaintMaskEntity, PosChangedArg } from 'features/controlLayers/store/types';
|
import type { CanvasEntity, InpaintMaskEntity, PosChangedArg } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,26 +66,14 @@ const getInpaintMask = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the inpaint mask render function.
|
* Gets a function to render the inpaint mask.
|
||||||
* @param manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param getEntityState A function to get the inpaint mask entity state
|
* @returns A function to render the inpaint mask
|
||||||
* @param getMaskOpacity A function to get the mask opacity
|
|
||||||
* @param getToolState A function to get the tool state
|
|
||||||
* @param getSelectedEntity A function to get the selected entity
|
|
||||||
* @param onPosChanged Callback for when the position changes (e.g. the entity is dragged)
|
|
||||||
* @returns The inpaint mask render function
|
|
||||||
*/
|
*/
|
||||||
export const getRenderInpaintMask =
|
export const getRenderInpaintMask = (manager: KonvaNodeManager) => {
|
||||||
(arg: {
|
const { getInpaintMaskEntityState, getMaskOpacity, getToolState, getSelectedEntity, onPosChanged } = manager.stateApi;
|
||||||
manager: KonvaNodeManager;
|
|
||||||
getInpaintMaskEntityState: () => CanvasV2State['inpaintMask'];
|
function renderInpaintMask(): void {
|
||||||
getMaskOpacity: () => number;
|
|
||||||
getToolState: () => CanvasV2State['tool'];
|
|
||||||
getSelectedEntity: () => CanvasEntity | null;
|
|
||||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void;
|
|
||||||
}) =>
|
|
||||||
(): void => {
|
|
||||||
const { manager, getInpaintMaskEntityState, getMaskOpacity, getToolState, getSelectedEntity, onPosChanged } = arg;
|
|
||||||
const entity = getInpaintMaskEntityState();
|
const entity = getInpaintMaskEntityState();
|
||||||
const globalMaskLayerOpacity = getMaskOpacity();
|
const globalMaskLayerOpacity = getMaskOpacity();
|
||||||
const toolState = getToolState();
|
const toolState = getToolState();
|
||||||
@ -228,4 +216,7 @@ export const getRenderInpaintMask =
|
|||||||
// } else {
|
// } else {
|
||||||
// bboxRect.visible(false);
|
// bboxRect.visible(false);
|
||||||
// }
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderInpaintMask;
|
||||||
};
|
};
|
||||||
|
@ -15,13 +15,9 @@ import {
|
|||||||
getRectShape,
|
getRectShape,
|
||||||
} from 'features/controlLayers/konva/renderers/objects';
|
} from 'features/controlLayers/konva/renderers/objects';
|
||||||
import { mapId } from 'features/controlLayers/konva/util';
|
import { mapId } from 'features/controlLayers/konva/util';
|
||||||
import type { CanvasEntity, CanvasV2State, LayerEntity, PosChangedArg, Tool } from 'features/controlLayers/store/types';
|
import type { CanvasEntity, LayerEntity, PosChangedArg, Tool } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
|
||||||
/**
|
|
||||||
* Logic for creating and rendering raster layers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets layer entity's konva nodes and entity adapter, creating them if they do not exist.
|
* Gets layer entity's konva nodes and entity adapter, creating them if they do not exist.
|
||||||
* @param manager The konva node manager
|
* @param manager The konva node manager
|
||||||
@ -137,20 +133,12 @@ export const renderLayer = async (
|
|||||||
/**
|
/**
|
||||||
* Gets a function to render all layers.
|
* Gets a function to render all layers.
|
||||||
* @param manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param getLayerEntityStates A function to get all layer entities
|
|
||||||
* @param getToolState A function to get the current tool state
|
|
||||||
* @param onPosChanged Callback for when the layer's position changes
|
|
||||||
* @returns A function to render all layers
|
* @returns A function to render all layers
|
||||||
*/
|
*/
|
||||||
export const getRenderLayers =
|
export const getRenderLayers = (manager: KonvaNodeManager) => {
|
||||||
(arg: {
|
const { getLayerEntityStates, getToolState, onPosChanged } = manager.stateApi;
|
||||||
manager: KonvaNodeManager;
|
|
||||||
getLayerEntityStates: () => CanvasV2State['layers']['entities'];
|
function renderLayers(): void {
|
||||||
getToolState: () => CanvasV2State['tool'];
|
|
||||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void;
|
|
||||||
}) =>
|
|
||||||
(): void => {
|
|
||||||
const { manager, getLayerEntityStates, getToolState, onPosChanged } = arg;
|
|
||||||
const entities = getLayerEntityStates();
|
const entities = getLayerEntityStates();
|
||||||
const tool = getToolState();
|
const tool = getToolState();
|
||||||
// Destroy nonexistent layers
|
// Destroy nonexistent layers
|
||||||
@ -162,4 +150,7 @@ export const getRenderLayers =
|
|||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
renderLayer(manager, entity, tool.selected, onPosChanged);
|
renderLayer(manager, entity, tool.selected, onPosChanged);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderLayers;
|
||||||
};
|
};
|
||||||
|
@ -19,9 +19,9 @@ import {
|
|||||||
PREVIEW_TOOL_GROUP_ID,
|
PREVIEW_TOOL_GROUP_ID,
|
||||||
} from 'features/controlLayers/konva/naming';
|
} from 'features/controlLayers/konva/naming';
|
||||||
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||||
import type { CanvasEntity, CanvasV2State, RgbaColor } from 'features/controlLayers/store/types';
|
import type { CanvasV2State } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { IRect, Vector2d } from 'konva/lib/types';
|
import type { IRect } from 'konva/lib/types';
|
||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,13 +245,12 @@ const NO_ANCHORS: string[] = [];
|
|||||||
/**
|
/**
|
||||||
* Gets the bbox render function.
|
* Gets the bbox render function.
|
||||||
* @param manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param getBbox A function to get the bbox
|
|
||||||
* @param getToolState A function to get the tool state
|
|
||||||
* @returns The bbox render function
|
* @returns The bbox render function
|
||||||
*/
|
*/
|
||||||
export const getRenderBbox =
|
export const getRenderBbox = (manager: KonvaNodeManager) => {
|
||||||
(manager: KonvaNodeManager, getBbox: () => CanvasV2State['bbox'], getToolState: () => CanvasV2State['tool']) =>
|
const { getBbox, getToolState } = manager.stateApi;
|
||||||
(): void => {
|
|
||||||
|
return (): void => {
|
||||||
const bbox = getBbox();
|
const bbox = getBbox();
|
||||||
const toolState = getToolState();
|
const toolState = getToolState();
|
||||||
manager.preview.bbox.group.listening(toolState.selected === 'bbox');
|
manager.preview.bbox.group.listening(toolState.selected === 'bbox');
|
||||||
@ -270,6 +269,7 @@ export const getRenderBbox =
|
|||||||
enabledAnchors: toolState.selected === 'bbox' ? ALL_ANCHORS : NO_ANCHORS,
|
enabledAnchors: toolState.selected === 'bbox' ? ALL_ANCHORS : NO_ANCHORS,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the tool preview konva nodes.
|
* Gets the tool preview konva nodes.
|
||||||
@ -328,30 +328,11 @@ export const createToolPreviewNodes = (): KonvaNodeManager['preview']['tool'] =>
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the tool preview (brush, eraser, rect) render function.
|
* Gets the tool preview (brush, eraser, rect) render function.
|
||||||
* @param arg.manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param arg.getToolState The selected tool
|
|
||||||
* @param arg.currentFill The selected layer's color
|
|
||||||
* @param arg.selectedEntity The selected layer's type
|
|
||||||
* @param arg.globalMaskLayerOpacity The global mask layer opacity
|
|
||||||
* @param arg.cursorPos The cursor position
|
|
||||||
* @param arg.lastMouseDownPos The position of the last mouse down event - used for the rect tool
|
|
||||||
* @param arg.brushSize The brush size
|
|
||||||
* @returns The tool preview render function
|
* @returns The tool preview render function
|
||||||
*/
|
*/
|
||||||
export const getRenderToolPreview =
|
export const getRenderToolPreview = (manager: KonvaNodeManager) => {
|
||||||
(arg: {
|
|
||||||
manager: KonvaNodeManager;
|
|
||||||
getToolState: () => CanvasV2State['tool'];
|
|
||||||
getCurrentFill: () => RgbaColor;
|
|
||||||
getSelectedEntity: () => CanvasEntity | null;
|
|
||||||
getLastCursorPos: () => Vector2d | null;
|
|
||||||
getLastMouseDownPos: () => Vector2d | null;
|
|
||||||
getIsDrawing: () => boolean;
|
|
||||||
getIsMouseDown: () => boolean;
|
|
||||||
}) =>
|
|
||||||
(): void => {
|
|
||||||
const {
|
const {
|
||||||
manager,
|
|
||||||
getToolState,
|
getToolState,
|
||||||
getCurrentFill,
|
getCurrentFill,
|
||||||
getSelectedEntity,
|
getSelectedEntity,
|
||||||
@ -359,8 +340,9 @@ export const getRenderToolPreview =
|
|||||||
getLastMouseDownPos,
|
getLastMouseDownPos,
|
||||||
getIsDrawing,
|
getIsDrawing,
|
||||||
getIsMouseDown,
|
getIsMouseDown,
|
||||||
} = arg;
|
} = manager.stateApi;
|
||||||
|
|
||||||
|
return (): void => {
|
||||||
const stage = manager.stage;
|
const stage = manager.stage;
|
||||||
const layerCount = manager.adapters.size;
|
const layerCount = manager.adapters.size;
|
||||||
const toolState = getToolState();
|
const toolState = getToolState();
|
||||||
@ -451,6 +433,7 @@ export const getRenderToolPreview =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scales the tool preview nodes. Depending on the scale of the stage, the border width and radius of the brush preview
|
* Scales the tool preview nodes. Depending on the scale of the stage, the border width and radius of the brush preview
|
||||||
@ -493,13 +476,13 @@ export const createDocumentOverlay = (): KonvaNodeManager['preview']['documentOv
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the document overlay render function.
|
* Gets the document overlay render function.
|
||||||
* @param arg.manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param arg.getDocument A function to get the document state
|
|
||||||
* @returns The document overlay render function
|
* @returns The document overlay render function
|
||||||
*/
|
*/
|
||||||
export const getRenderDocumentOverlay =
|
export const getRenderDocumentOverlay = (manager: KonvaNodeManager) => {
|
||||||
(arg: { manager: KonvaNodeManager; getDocument: () => CanvasV2State['document'] }) => (): void => {
|
const { getDocument } = manager.stateApi;
|
||||||
const { manager, getDocument } = arg;
|
|
||||||
|
function renderDocumentOverlay(): void {
|
||||||
const document = getDocument();
|
const document = getDocument();
|
||||||
const stage = manager.stage;
|
const stage = manager.stage;
|
||||||
|
|
||||||
@ -524,4 +507,7 @@ export const getRenderDocumentOverlay =
|
|||||||
width: document.width,
|
width: document.width,
|
||||||
height: document.height,
|
height: document.height,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderDocumentOverlay;
|
||||||
};
|
};
|
||||||
|
@ -19,7 +19,6 @@ import { mapId } from 'features/controlLayers/konva/util';
|
|||||||
import type {
|
import type {
|
||||||
CanvasEntity,
|
CanvasEntity,
|
||||||
CanvasEntityIdentifier,
|
CanvasEntityIdentifier,
|
||||||
CanvasV2State,
|
|
||||||
PosChangedArg,
|
PosChangedArg,
|
||||||
RegionEntity,
|
RegionEntity,
|
||||||
Tool,
|
Tool,
|
||||||
@ -230,25 +229,13 @@ export const renderRegion = (
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a function to render all regions.
|
* Gets a function to render all regions.
|
||||||
* @param arg.manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param arg.getRegionEntityStates A function to get all region entities
|
|
||||||
* @param arg.getMaskOpacity A function to get the mask opacity
|
|
||||||
* @param arg.getToolState A function to get the tool state
|
|
||||||
* @param arg.getSelectedEntity A function to get the selectedEntity
|
|
||||||
* @param arg.onPosChanged A callback for when the position of an entity changes
|
|
||||||
* @returns A function to render all regions
|
* @returns A function to render all regions
|
||||||
*/
|
*/
|
||||||
export const getRenderRegions =
|
export const getRenderRegions = (manager: KonvaNodeManager) => {
|
||||||
(arg: {
|
const { getRegionEntityStates, getMaskOpacity, getToolState, getSelectedEntity, onPosChanged } = manager.stateApi;
|
||||||
manager: KonvaNodeManager;
|
|
||||||
getRegionEntityStates: () => CanvasV2State['regions']['entities'];
|
function renderRegions(): void {
|
||||||
getMaskOpacity: () => number;
|
|
||||||
getToolState: () => CanvasV2State['tool'];
|
|
||||||
getSelectedEntity: () => CanvasEntity | null;
|
|
||||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void;
|
|
||||||
}) =>
|
|
||||||
() => {
|
|
||||||
const { manager, getRegionEntityStates, getMaskOpacity, getToolState, getSelectedEntity, onPosChanged } = arg;
|
|
||||||
const entities = getRegionEntityStates();
|
const entities = getRegionEntityStates();
|
||||||
const maskOpacity = getMaskOpacity();
|
const maskOpacity = getMaskOpacity();
|
||||||
const toolState = getToolState();
|
const toolState = getToolState();
|
||||||
@ -264,4 +251,7 @@ export const getRenderRegions =
|
|||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
renderRegion(manager, entity, maskOpacity, toolState.selected, selectedEntity, onPosChanged);
|
renderRegion(manager, entity, maskOpacity, toolState.selected, selectedEntity, onPosChanged);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderRegions;
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
getRenderToolPreview,
|
getRenderToolPreview,
|
||||||
} from 'features/controlLayers/konva/renderers/preview';
|
} from 'features/controlLayers/konva/renderers/preview';
|
||||||
import { getRenderRegions } from 'features/controlLayers/konva/renderers/regions';
|
import { getRenderRegions } from 'features/controlLayers/konva/renderers/regions';
|
||||||
import { getFitDocumentToStage } from 'features/controlLayers/konva/renderers/stage';
|
import { getFitDocumentToStage, getFitStageToContainer } from 'features/controlLayers/konva/renderers/stage';
|
||||||
import {
|
import {
|
||||||
$stageAttrs,
|
$stageAttrs,
|
||||||
bboxChanged,
|
bboxChanged,
|
||||||
@ -283,7 +283,7 @@ export const initializeRenderer = (
|
|||||||
spaceKey = val;
|
spaceKey = val;
|
||||||
};
|
};
|
||||||
|
|
||||||
const manager = new KonvaNodeManager(stage);
|
const manager = new KonvaNodeManager(stage, container);
|
||||||
$nodeManager.set(manager);
|
$nodeManager.set(manager);
|
||||||
|
|
||||||
manager.background = { layer: createBackgroundLayer() };
|
manager.background = { layer: createBackgroundLayer() };
|
||||||
@ -298,17 +298,31 @@ export const initializeRenderer = (
|
|||||||
manager.preview.layer.add(manager.preview.tool.group);
|
manager.preview.layer.add(manager.preview.tool.group);
|
||||||
manager.preview.layer.add(manager.preview.documentOverlay.group);
|
manager.preview.layer.add(manager.preview.documentOverlay.group);
|
||||||
manager.stage.add(manager.preview.layer);
|
manager.stage.add(manager.preview.layer);
|
||||||
|
manager.stateApi = {
|
||||||
const cleanupListeners = setStageEventHandlers({
|
// Read-only state
|
||||||
manager,
|
|
||||||
getToolState,
|
getToolState,
|
||||||
|
getSelectedEntity,
|
||||||
|
getBbox,
|
||||||
|
getSettings,
|
||||||
|
getCurrentFill,
|
||||||
|
getAltKey: $alt.get,
|
||||||
|
getCtrlKey: $ctrl.get,
|
||||||
|
getMetaKey: $meta.get,
|
||||||
|
getShiftKey: $shift.get,
|
||||||
|
getControlAdapterEntityStates,
|
||||||
|
getDocument,
|
||||||
|
getLayerEntityStates,
|
||||||
|
getRegionEntityStates,
|
||||||
|
getMaskOpacity,
|
||||||
|
getInpaintMaskEntityState,
|
||||||
|
|
||||||
|
// Read-write state
|
||||||
setTool,
|
setTool,
|
||||||
setToolBuffer,
|
setToolBuffer,
|
||||||
getIsDrawing,
|
getIsDrawing,
|
||||||
setIsDrawing,
|
setIsDrawing,
|
||||||
getIsMouseDown,
|
getIsMouseDown,
|
||||||
setIsMouseDown,
|
setIsMouseDown,
|
||||||
getSelectedEntity,
|
|
||||||
getLastAddedPoint,
|
getLastAddedPoint,
|
||||||
setLastAddedPoint,
|
setLastAddedPoint,
|
||||||
getLastCursorPos,
|
getLastCursorPos,
|
||||||
@ -318,61 +332,37 @@ export const initializeRenderer = (
|
|||||||
getSpaceKey,
|
getSpaceKey,
|
||||||
setSpaceKey,
|
setSpaceKey,
|
||||||
setStageAttrs: $stageAttrs.set,
|
setStageAttrs: $stageAttrs.set,
|
||||||
getBbox,
|
|
||||||
getSettings,
|
// Callbacks
|
||||||
onBrushLineAdded,
|
onBrushLineAdded,
|
||||||
onEraserLineAdded,
|
onEraserLineAdded,
|
||||||
onPointAddedToLine,
|
onPointAddedToLine,
|
||||||
onRectShapeAdded,
|
onRectShapeAdded,
|
||||||
onBrushWidthChanged,
|
onBrushWidthChanged,
|
||||||
onEraserWidthChanged,
|
onEraserWidthChanged,
|
||||||
getCurrentFill,
|
onPosChanged,
|
||||||
});
|
onBboxTransformed,
|
||||||
|
};
|
||||||
|
|
||||||
|
const cleanupListeners = setStageEventHandlers(manager);
|
||||||
|
|
||||||
// Calculating bounding boxes is expensive, must be debounced to not block the UI thread during a user interaction.
|
// Calculating bounding boxes is expensive, must be debounced to not block the UI thread during a user interaction.
|
||||||
// TODO(psyche): Figure out how to do this in a worker. Probably means running the renderer in a worker and sending
|
// TODO(psyche): Figure out how to do this in a worker. Probably means running the renderer in a worker and sending
|
||||||
// the entire state over when needed.
|
// the entire state over when needed.
|
||||||
const debouncedUpdateBboxes = debounce(updateBboxes, 300);
|
const debouncedUpdateBboxes = debounce(updateBboxes, 300);
|
||||||
|
|
||||||
manager.renderers = {
|
manager.konvaApi = {
|
||||||
renderRegions: getRenderRegions({
|
renderRegions: getRenderRegions(manager),
|
||||||
manager,
|
renderLayers: getRenderLayers(manager),
|
||||||
getRegionEntityStates,
|
renderControlAdapters: getRenderControlAdapters(manager),
|
||||||
getMaskOpacity,
|
renderInpaintMask: getRenderInpaintMask(manager),
|
||||||
getToolState,
|
renderBbox: getRenderBbox(manager),
|
||||||
getSelectedEntity,
|
renderToolPreview: getRenderToolPreview(manager),
|
||||||
onPosChanged,
|
renderDocumentOverlay: getRenderDocumentOverlay(manager),
|
||||||
}),
|
renderBackground: getRenderBackground(manager),
|
||||||
renderLayers: getRenderLayers({ manager, getLayerEntityStates, getToolState, onPosChanged }),
|
arrangeEntities: getArrangeEntities(manager),
|
||||||
renderControlAdapters: getRenderControlAdapters({ manager, getControlAdapterEntityStates }),
|
fitDocumentToStage: getFitDocumentToStage(manager),
|
||||||
renderInpaintMask: getRenderInpaintMask({
|
fitStageToContainer: getFitStageToContainer(manager),
|
||||||
manager,
|
|
||||||
getInpaintMaskEntityState,
|
|
||||||
getMaskOpacity,
|
|
||||||
getToolState,
|
|
||||||
getSelectedEntity,
|
|
||||||
onPosChanged,
|
|
||||||
}),
|
|
||||||
renderBbox: getRenderBbox(manager, getBbox, getToolState),
|
|
||||||
renderToolPreview: getRenderToolPreview({
|
|
||||||
manager,
|
|
||||||
getToolState,
|
|
||||||
getCurrentFill,
|
|
||||||
getSelectedEntity,
|
|
||||||
getLastCursorPos,
|
|
||||||
getLastMouseDownPos,
|
|
||||||
getIsDrawing,
|
|
||||||
getIsMouseDown,
|
|
||||||
}),
|
|
||||||
renderDocumentOverlay: getRenderDocumentOverlay({ manager, getDocument }),
|
|
||||||
renderBackground: getRenderBackground({ manager }),
|
|
||||||
fitDocumentToStage: getFitDocumentToStage({ manager, getDocument, setStageAttrs: $stageAttrs.set }),
|
|
||||||
arrangeEntities: getArrangeEntities({
|
|
||||||
manager,
|
|
||||||
getLayerEntityStates,
|
|
||||||
getControlAdapterEntityStates,
|
|
||||||
getRegionEntityStates,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderCanvas = () => {
|
const renderCanvas = () => {
|
||||||
@ -392,7 +382,7 @@ export const initializeRenderer = (
|
|||||||
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
||||||
) {
|
) {
|
||||||
logIfDebugging('Rendering layers');
|
logIfDebugging('Rendering layers');
|
||||||
manager.renderers.renderLayers();
|
manager.konvaApi.renderLayers();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -402,7 +392,7 @@ export const initializeRenderer = (
|
|||||||
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
||||||
) {
|
) {
|
||||||
logIfDebugging('Rendering regions');
|
logIfDebugging('Rendering regions');
|
||||||
manager.renderers.renderRegions();
|
manager.konvaApi.renderRegions();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -412,22 +402,22 @@ export const initializeRenderer = (
|
|||||||
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
||||||
) {
|
) {
|
||||||
logIfDebugging('Rendering inpaint mask');
|
logIfDebugging('Rendering inpaint mask');
|
||||||
manager.renderers.renderInpaintMask();
|
manager.konvaApi.renderInpaintMask();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFirstRender || canvasV2.controlAdapters.entities !== prevCanvasV2.controlAdapters.entities) {
|
if (isFirstRender || canvasV2.controlAdapters.entities !== prevCanvasV2.controlAdapters.entities) {
|
||||||
logIfDebugging('Rendering control adapters');
|
logIfDebugging('Rendering control adapters');
|
||||||
manager.renderers.renderControlAdapters();
|
manager.konvaApi.renderControlAdapters();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFirstRender || canvasV2.document !== prevCanvasV2.document) {
|
if (isFirstRender || canvasV2.document !== prevCanvasV2.document) {
|
||||||
logIfDebugging('Rendering document bounds overlay');
|
logIfDebugging('Rendering document bounds overlay');
|
||||||
manager.renderers.renderDocumentOverlay();
|
manager.konvaApi.renderDocumentOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFirstRender || canvasV2.bbox !== prevCanvasV2.bbox || canvasV2.tool.selected !== prevCanvasV2.tool.selected) {
|
if (isFirstRender || canvasV2.bbox !== prevCanvasV2.bbox || canvasV2.tool.selected !== prevCanvasV2.tool.selected) {
|
||||||
logIfDebugging('Rendering generation bbox');
|
logIfDebugging('Rendering generation bbox');
|
||||||
manager.renderers.renderBbox();
|
manager.konvaApi.renderBbox();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -447,7 +437,7 @@ export const initializeRenderer = (
|
|||||||
canvasV2.regions.entities !== prevCanvasV2.regions.entities
|
canvasV2.regions.entities !== prevCanvasV2.regions.entities
|
||||||
) {
|
) {
|
||||||
logIfDebugging('Arranging entities');
|
logIfDebugging('Arranging entities');
|
||||||
manager.renderers.arrangeEntities();
|
manager.konvaApi.arrangeEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
prevCanvasV2 = canvasV2;
|
prevCanvasV2 = canvasV2;
|
||||||
@ -461,30 +451,16 @@ export const initializeRenderer = (
|
|||||||
|
|
||||||
// We can use a resize observer to ensure the stage always fits the container. We also need to re-render the bg and
|
// We can use a resize observer to ensure the stage always fits the container. We also need to re-render the bg and
|
||||||
// document bounds overlay when the stage is resized.
|
// document bounds overlay when the stage is resized.
|
||||||
const fitStageToContainer = () => {
|
const resizeObserver = new ResizeObserver(manager.konvaApi.fitStageToContainer);
|
||||||
stage.width(container.offsetWidth);
|
|
||||||
stage.height(container.offsetHeight);
|
|
||||||
$stageAttrs.set({
|
|
||||||
x: stage.x(),
|
|
||||||
y: stage.y(),
|
|
||||||
width: stage.width(),
|
|
||||||
height: stage.height(),
|
|
||||||
scale: stage.scaleX(),
|
|
||||||
});
|
|
||||||
manager.renderers.renderBackground();
|
|
||||||
manager.renderers.renderDocumentOverlay();
|
|
||||||
};
|
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(fitStageToContainer);
|
|
||||||
resizeObserver.observe(container);
|
resizeObserver.observe(container);
|
||||||
fitStageToContainer();
|
manager.konvaApi.fitStageToContainer();
|
||||||
|
|
||||||
const unsubscribeRenderer = subscribe(renderCanvas);
|
const unsubscribeRenderer = subscribe(renderCanvas);
|
||||||
|
|
||||||
logIfDebugging('First render of konva stage');
|
logIfDebugging('First render of konva stage');
|
||||||
// On first render, the document should be fit to the stage.
|
// On first render, the document should be fit to the stage.
|
||||||
manager.renderers.fitDocumentToStage();
|
manager.konvaApi.fitDocumentToStage();
|
||||||
manager.renderers.renderToolPreview();
|
manager.konvaApi.renderToolPreview();
|
||||||
renderCanvas();
|
renderCanvas();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -1,23 +1,15 @@
|
|||||||
import { DOCUMENT_FIT_PADDING_PX } from 'features/controlLayers/konva/constants';
|
import { DOCUMENT_FIT_PADDING_PX } from 'features/controlLayers/konva/constants';
|
||||||
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||||
import type { CanvasV2State, StageAttrs } from 'features/controlLayers/store/types';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a function to fit the document to the stage, resetting the stage scale to 100%.
|
* Gets a function to fit the document to the stage, resetting the stage scale to 100%.
|
||||||
* If the document is smaller than the stage, the stage scale is increased to fit the document.
|
* If the document is smaller than the stage, the stage scale is increased to fit the document.
|
||||||
* @param arg.manager The konva node manager
|
* @param manager The konva node manager
|
||||||
* @param arg.getDocument A function to get the current document state
|
|
||||||
* @param arg.setStageAttrs A function to set the stage attributes
|
|
||||||
* @returns A function to fit the document to the stage
|
* @returns A function to fit the document to the stage
|
||||||
*/
|
*/
|
||||||
export const getFitDocumentToStage =
|
export const getFitDocumentToStage = (manager: KonvaNodeManager) => {
|
||||||
(arg: {
|
function fitDocumentToStage(): void {
|
||||||
manager: KonvaNodeManager;
|
const { getDocument, setStageAttrs } = manager.stateApi;
|
||||||
getDocument: () => CanvasV2State['document'];
|
|
||||||
setStageAttrs: (stageAttrs: StageAttrs) => void;
|
|
||||||
}) =>
|
|
||||||
(): void => {
|
|
||||||
const { manager, getDocument, setStageAttrs } = arg;
|
|
||||||
const document = getDocument();
|
const document = getDocument();
|
||||||
// Fit & center the document on the stage
|
// Fit & center the document on the stage
|
||||||
const width = manager.stage.width();
|
const width = manager.stage.width();
|
||||||
@ -29,4 +21,32 @@ export const getFitDocumentToStage =
|
|||||||
const y = (height - docHeightWithBuffer * scale) / 2 + DOCUMENT_FIT_PADDING_PX * scale;
|
const y = (height - docHeightWithBuffer * scale) / 2 + DOCUMENT_FIT_PADDING_PX * scale;
|
||||||
manager.stage.setAttrs({ x, y, width, height, scaleX: scale, scaleY: scale });
|
manager.stage.setAttrs({ x, y, width, height, scaleX: scale, scaleY: scale });
|
||||||
setStageAttrs({ x, y, width, height, scale });
|
setStageAttrs({ x, y, width, height, scale });
|
||||||
|
}
|
||||||
|
|
||||||
|
return fitDocumentToStage;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a function to fit the stage to its container element. Called during resize events.
|
||||||
|
* @param manager The konva node manager
|
||||||
|
* @returns A function to fit the stage to its container
|
||||||
|
*/
|
||||||
|
export const getFitStageToContainer = (manager: KonvaNodeManager) => {
|
||||||
|
const { stage, container } = manager;
|
||||||
|
const { setStageAttrs } = manager.stateApi;
|
||||||
|
function fitStageToContainer(): void {
|
||||||
|
stage.width(container.offsetWidth);
|
||||||
|
stage.height(container.offsetHeight);
|
||||||
|
setStageAttrs({
|
||||||
|
x: stage.x(),
|
||||||
|
y: stage.y(),
|
||||||
|
width: stage.width(),
|
||||||
|
height: stage.height(),
|
||||||
|
scale: stage.scaleX(),
|
||||||
|
});
|
||||||
|
manager.konvaApi.renderBackground();
|
||||||
|
manager.konvaApi.renderDocumentOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
return fitStageToContainer;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user