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 { getScaledFlooredCursorPosition } from 'features/controlLayers/konva/util';
|
||||
import type {
|
||||
BrushLineAddedArg,
|
||||
CanvasEntity,
|
||||
CanvasV2State,
|
||||
EraserLineAddedArg,
|
||||
PointAddedToLineArg,
|
||||
RectShapeAddedArg,
|
||||
RgbaColor,
|
||||
StageAttrs,
|
||||
Tool,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import type { CanvasEntity } from 'features/controlLayers/store/types';
|
||||
import type Konva from 'konva';
|
||||
import type { Vector2d } from 'konva/lib/types';
|
||||
import { clamp } from 'lodash-es';
|
||||
@ -25,43 +15,16 @@ import {
|
||||
} from './constants';
|
||||
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
|
||||
* cursor is not over the stage.
|
||||
* @param stage The konva stage
|
||||
* @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);
|
||||
if (!pos) {
|
||||
return null;
|
||||
@ -93,10 +56,10 @@ const calculateNewBrushSize = (brushSize: number, delta: number) => {
|
||||
const maybeAddNextPoint = (
|
||||
selectedEntity: CanvasEntity,
|
||||
currentPos: Vector2d,
|
||||
getToolState: Arg['getToolState'],
|
||||
getLastAddedPoint: Arg['getLastAddedPoint'],
|
||||
setLastAddedPoint: Arg['setLastAddedPoint'],
|
||||
onPointAddedToLine: Arg['onPointAddedToLine']
|
||||
getToolState: KonvaNodeManager['stateApi']['getToolState'],
|
||||
getLastAddedPoint: KonvaNodeManager['stateApi']['getLastAddedPoint'],
|
||||
setLastAddedPoint: KonvaNodeManager['stateApi']['setLastAddedPoint'],
|
||||
onPointAddedToLine: KonvaNodeManager['stateApi']['onPointAddedToLine']
|
||||
) => {
|
||||
const isDrawableEntity =
|
||||
selectedEntity?.type === 'regional_guidance' ||
|
||||
@ -132,42 +95,42 @@ const maybeAddNextPoint = (
|
||||
);
|
||||
};
|
||||
|
||||
export const setStageEventHandlers = ({
|
||||
manager,
|
||||
getToolState,
|
||||
getCurrentFill,
|
||||
setTool,
|
||||
setToolBuffer,
|
||||
getIsDrawing,
|
||||
setIsDrawing,
|
||||
getIsMouseDown,
|
||||
setIsMouseDown,
|
||||
getLastMouseDownPos,
|
||||
setLastMouseDownPos,
|
||||
getLastCursorPos,
|
||||
setLastCursorPos,
|
||||
getLastAddedPoint,
|
||||
setLastAddedPoint,
|
||||
setStageAttrs,
|
||||
getSelectedEntity,
|
||||
getSpaceKey,
|
||||
setSpaceKey,
|
||||
getBbox,
|
||||
getSettings,
|
||||
onBrushLineAdded,
|
||||
onEraserLineAdded,
|
||||
onPointAddedToLine,
|
||||
onRectShapeAdded,
|
||||
onBrushWidthChanged: onBrushSizeChanged,
|
||||
onEraserWidthChanged: onEraserSizeChanged,
|
||||
}: Arg): (() => void) => {
|
||||
const stage = manager.stage;
|
||||
export const setStageEventHandlers = (manager: KonvaNodeManager): (() => void) => {
|
||||
const { stage, stateApi } = manager;
|
||||
const {
|
||||
getToolState,
|
||||
getCurrentFill,
|
||||
setTool,
|
||||
setToolBuffer,
|
||||
getIsDrawing,
|
||||
setIsDrawing,
|
||||
getIsMouseDown,
|
||||
setIsMouseDown,
|
||||
getLastMouseDownPos,
|
||||
setLastMouseDownPos,
|
||||
getLastCursorPos,
|
||||
setLastCursorPos,
|
||||
getLastAddedPoint,
|
||||
setLastAddedPoint,
|
||||
setStageAttrs,
|
||||
getSelectedEntity,
|
||||
getSpaceKey,
|
||||
setSpaceKey,
|
||||
getBbox,
|
||||
getSettings,
|
||||
onBrushLineAdded,
|
||||
onEraserLineAdded,
|
||||
onPointAddedToLine,
|
||||
onRectShapeAdded,
|
||||
onBrushWidthChanged,
|
||||
onEraserWidthChanged,
|
||||
} = stateApi;
|
||||
|
||||
//#region mouseenter
|
||||
stage.on('mouseenter', () => {
|
||||
const tool = getToolState().selected;
|
||||
stage.findOne<Konva.Layer>(`#${PREVIEW_TOOL_GROUP_ID}`)?.visible(tool === 'brush' || tool === 'eraser');
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
});
|
||||
|
||||
//#region mousedown
|
||||
@ -288,7 +251,7 @@ export const setStageEventHandlers = ({
|
||||
setLastAddedPoint(pos);
|
||||
}
|
||||
}
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
});
|
||||
|
||||
//#region mouseup
|
||||
@ -327,7 +290,7 @@ export const setStageEventHandlers = ({
|
||||
setLastMouseDownPos(null);
|
||||
}
|
||||
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
});
|
||||
|
||||
//#region mousemove
|
||||
@ -433,7 +396,7 @@ export const setStageEventHandlers = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
});
|
||||
|
||||
//#region mouseleave
|
||||
@ -462,7 +425,7 @@ export const setStageEventHandlers = ({
|
||||
}
|
||||
}
|
||||
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
});
|
||||
|
||||
//#region wheel
|
||||
@ -477,9 +440,9 @@ export const setStageEventHandlers = ({
|
||||
}
|
||||
// Holding ctrl or meta while scrolling changes the brush size
|
||||
if (toolState.selected === 'brush') {
|
||||
onBrushSizeChanged(calculateNewBrushSize(toolState.brush.width, delta));
|
||||
onBrushWidthChanged(calculateNewBrushSize(toolState.brush.width, delta));
|
||||
} else if (toolState.selected === 'eraser') {
|
||||
onEraserSizeChanged(calculateNewBrushSize(toolState.eraser.width, delta));
|
||||
onEraserWidthChanged(calculateNewBrushSize(toolState.eraser.width, delta));
|
||||
}
|
||||
} else {
|
||||
// We need the absolute cursor position - not the scaled position
|
||||
@ -503,11 +466,11 @@ export const setStageEventHandlers = ({
|
||||
stage.scaleY(newScale);
|
||||
stage.position(newPos);
|
||||
setStageAttrs({ ...newPos, width: stage.width(), height: stage.height(), scale: newScale });
|
||||
manager.renderers.renderBackground();
|
||||
manager.renderers.renderDocumentOverlay();
|
||||
manager.konvaApi.renderBackground();
|
||||
manager.konvaApi.renderDocumentOverlay();
|
||||
}
|
||||
}
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
});
|
||||
|
||||
//#region dragmove
|
||||
@ -519,9 +482,9 @@ export const setStageEventHandlers = ({
|
||||
height: stage.height(),
|
||||
scale: stage.scaleX(),
|
||||
});
|
||||
manager.renderers.renderBackground();
|
||||
manager.renderers.renderDocumentOverlay();
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderBackground();
|
||||
manager.konvaApi.renderDocumentOverlay();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
});
|
||||
|
||||
//#region dragend
|
||||
@ -534,7 +497,7 @@ export const setStageEventHandlers = ({
|
||||
height: stage.height(),
|
||||
scale: stage.scaleX(),
|
||||
});
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
});
|
||||
|
||||
//#region key
|
||||
@ -555,12 +518,12 @@ export const setStageEventHandlers = ({
|
||||
setTool('view');
|
||||
setSpaceKey(true);
|
||||
} else if (e.key === 'r') {
|
||||
manager.renderers.fitDocumentToStage();
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.renderers.renderBackground();
|
||||
manager.renderers.renderDocumentOverlay();
|
||||
manager.konvaApi.fitDocumentToStage();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
manager.konvaApi.renderBackground();
|
||||
manager.konvaApi.renderDocumentOverlay();
|
||||
}
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
};
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
|
||||
@ -578,7 +541,7 @@ export const setStageEventHandlers = ({
|
||||
setToolBuffer(null);
|
||||
setSpaceKey(false);
|
||||
}
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
};
|
||||
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 { Vector2d } from 'konva/lib/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export type BrushLineObjectRecord = {
|
||||
@ -36,7 +53,7 @@ export type ImageObjectRecord = {
|
||||
|
||||
type ObjectRecord = BrushLineObjectRecord | EraserLineObjectRecord | RectShapeObjectRecord | ImageObjectRecord;
|
||||
|
||||
type KonvaRenderers = {
|
||||
type KonvaApi = {
|
||||
renderRegions: () => void;
|
||||
renderLayers: () => void;
|
||||
renderControlAdapters: () => void;
|
||||
@ -45,8 +62,9 @@ type KonvaRenderers = {
|
||||
renderDocumentOverlay: () => void;
|
||||
renderBackground: () => void;
|
||||
renderToolPreview: () => void;
|
||||
fitDocumentToStage: () => void;
|
||||
arrangeEntities: () => void;
|
||||
fitDocumentToStage: () => void;
|
||||
fitStageToContainer: () => void;
|
||||
};
|
||||
|
||||
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 {
|
||||
stage: Konva.Stage;
|
||||
container: HTMLDivElement;
|
||||
adapters: Map<string, KonvaEntityAdapter>;
|
||||
_background: BackgroundLayer | 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._renderers = null;
|
||||
this.container = container;
|
||||
this._konvaApi = null;
|
||||
this._preview = null;
|
||||
this._background = null;
|
||||
this._stateApi = null;
|
||||
this.adapters = new Map();
|
||||
}
|
||||
|
||||
@ -121,13 +184,13 @@ export class KonvaNodeManager {
|
||||
return this.adapters.delete(id);
|
||||
}
|
||||
|
||||
set renderers(renderers: KonvaRenderers) {
|
||||
this._renderers = renderers;
|
||||
set konvaApi(konvaApi: KonvaApi) {
|
||||
this._konvaApi = konvaApi;
|
||||
}
|
||||
|
||||
get renderers(): KonvaRenderers {
|
||||
assert(this._renderers !== null, 'Konva renderers have not been set');
|
||||
return this._renderers;
|
||||
get konvaApi(): KonvaApi {
|
||||
assert(this._konvaApi !== null, 'Konva API has not been set');
|
||||
return this._konvaApi;
|
||||
}
|
||||
|
||||
set preview(preview: PreviewLayer) {
|
||||
@ -147,6 +210,15 @@ export class KonvaNodeManager {
|
||||
assert(this._background !== null, 'Konva background layer has not been set');
|
||||
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 {
|
||||
|
@ -1,23 +1,14 @@
|
||||
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.
|
||||
* @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
|
||||
*/
|
||||
export const getArrangeEntities =
|
||||
(arg: {
|
||||
manager: KonvaNodeManager;
|
||||
getLayerEntityStates: () => CanvasV2State['layers']['entities'];
|
||||
getControlAdapterEntityStates: () => CanvasV2State['controlAdapters']['entities'];
|
||||
getRegionEntityStates: () => CanvasV2State['regions']['entities'];
|
||||
}) =>
|
||||
(): void => {
|
||||
const { manager, getLayerEntityStates, getControlAdapterEntityStates, getRegionEntityStates } = arg;
|
||||
export const getArrangeEntities = (manager: KonvaNodeManager) => {
|
||||
const { getLayerEntityStates, getControlAdapterEntityStates, getRegionEntityStates } = manager.stateApi;
|
||||
|
||||
function arrangeEntities(): void {
|
||||
const layers = getLayerEntityStates();
|
||||
const controlAdapters = getControlAdapterEntityStates();
|
||||
const regions = getRegionEntityStates();
|
||||
@ -34,4 +25,7 @@ export const getArrangeEntities =
|
||||
}
|
||||
manager.get('inpaint_mask')?.konvaLayer.zIndex(++zIndex);
|
||||
manager.preview.layer.zIndex(++zIndex);
|
||||
};
|
||||
}
|
||||
|
||||
return arrangeEntities;
|
||||
};
|
||||
|
@ -38,82 +38,85 @@ export const createBackgroundLayer = (): Konva.Layer => new Konva.Layer({ id: BA
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export const getRenderBackground = (arg: { manager: KonvaNodeManager }) => (): void => {
|
||||
const { manager } = arg;
|
||||
const background = manager.background.layer;
|
||||
background.zIndex(0);
|
||||
const scale = manager.stage.scaleX();
|
||||
const gridSpacing = getGridSpacing(scale);
|
||||
const x = manager.stage.x();
|
||||
const y = manager.stage.y();
|
||||
const width = manager.stage.width();
|
||||
const height = manager.stage.height();
|
||||
const stageRect = {
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: width,
|
||||
y2: height,
|
||||
};
|
||||
export const getRenderBackground = (manager: KonvaNodeManager) => {
|
||||
function renderBackground(): void {
|
||||
const background = manager.background.layer;
|
||||
background.zIndex(0);
|
||||
const scale = manager.stage.scaleX();
|
||||
const gridSpacing = getGridSpacing(scale);
|
||||
const x = manager.stage.x();
|
||||
const y = manager.stage.y();
|
||||
const width = manager.stage.width();
|
||||
const height = manager.stage.height();
|
||||
const stageRect = {
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: width,
|
||||
y2: height,
|
||||
};
|
||||
|
||||
const gridOffset = {
|
||||
x: Math.ceil(x / scale / gridSpacing) * gridSpacing,
|
||||
y: Math.ceil(y / scale / gridSpacing) * gridSpacing,
|
||||
};
|
||||
const gridOffset = {
|
||||
x: Math.ceil(x / scale / gridSpacing) * gridSpacing,
|
||||
y: Math.ceil(y / scale / gridSpacing) * gridSpacing,
|
||||
};
|
||||
|
||||
const gridRect = {
|
||||
x1: -gridOffset.x,
|
||||
y1: -gridOffset.y,
|
||||
x2: width / scale - gridOffset.x + gridSpacing,
|
||||
y2: height / scale - gridOffset.y + gridSpacing,
|
||||
};
|
||||
const gridRect = {
|
||||
x1: -gridOffset.x,
|
||||
y1: -gridOffset.y,
|
||||
x2: width / scale - gridOffset.x + gridSpacing,
|
||||
y2: height / scale - gridOffset.y + gridSpacing,
|
||||
};
|
||||
|
||||
const gridFullRect = {
|
||||
x1: Math.min(stageRect.x1, gridRect.x1),
|
||||
y1: Math.min(stageRect.y1, gridRect.y1),
|
||||
x2: Math.max(stageRect.x2, gridRect.x2),
|
||||
y2: Math.max(stageRect.y2, gridRect.y2),
|
||||
};
|
||||
const gridFullRect = {
|
||||
x1: Math.min(stageRect.x1, gridRect.x1),
|
||||
y1: Math.min(stageRect.y1, gridRect.y1),
|
||||
x2: Math.max(stageRect.x2, gridRect.x2),
|
||||
y2: Math.max(stageRect.y2, gridRect.y2),
|
||||
};
|
||||
|
||||
// find the x & y size of the grid
|
||||
const xSize = gridFullRect.x2 - gridFullRect.x1;
|
||||
const ySize = gridFullRect.y2 - gridFullRect.y1;
|
||||
// compute the number of steps required on each axis.
|
||||
const xSteps = Math.round(xSize / gridSpacing) + 1;
|
||||
const ySteps = Math.round(ySize / gridSpacing) + 1;
|
||||
// find the x & y size of the grid
|
||||
const xSize = gridFullRect.x2 - gridFullRect.x1;
|
||||
const ySize = gridFullRect.y2 - gridFullRect.y1;
|
||||
// compute the number of steps required on each axis.
|
||||
const xSteps = Math.round(xSize / gridSpacing) + 1;
|
||||
const ySteps = Math.round(ySize / gridSpacing) + 1;
|
||||
|
||||
const strokeWidth = 1 / scale;
|
||||
let _x = 0;
|
||||
let _y = 0;
|
||||
const strokeWidth = 1 / scale;
|
||||
let _x = 0;
|
||||
let _y = 0;
|
||||
|
||||
background.destroyChildren();
|
||||
background.destroyChildren();
|
||||
|
||||
for (let i = 0; i < xSteps; i++) {
|
||||
_x = gridFullRect.x1 + i * gridSpacing;
|
||||
background.add(
|
||||
new Konva.Line({
|
||||
x: _x,
|
||||
y: gridFullRect.y1,
|
||||
points: [0, 0, 0, ySize],
|
||||
stroke: _x % 64 ? fineGridLineColor : baseGridLineColor,
|
||||
strokeWidth,
|
||||
listening: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
for (let i = 0; i < ySteps; i++) {
|
||||
_y = gridFullRect.y1 + i * gridSpacing;
|
||||
background.add(
|
||||
new Konva.Line({
|
||||
x: gridFullRect.x1,
|
||||
y: _y,
|
||||
points: [0, 0, xSize, 0],
|
||||
stroke: _y % 64 ? fineGridLineColor : baseGridLineColor,
|
||||
strokeWidth,
|
||||
listening: false,
|
||||
})
|
||||
);
|
||||
for (let i = 0; i < xSteps; i++) {
|
||||
_x = gridFullRect.x1 + i * gridSpacing;
|
||||
background.add(
|
||||
new Konva.Line({
|
||||
x: _x,
|
||||
y: gridFullRect.y1,
|
||||
points: [0, 0, 0, ySize],
|
||||
stroke: _x % 64 ? fineGridLineColor : baseGridLineColor,
|
||||
strokeWidth,
|
||||
listening: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
for (let i = 0; i < ySteps; i++) {
|
||||
_y = gridFullRect.y1 + i * gridSpacing;
|
||||
background.add(
|
||||
new Konva.Line({
|
||||
x: gridFullRect.x1,
|
||||
y: _y,
|
||||
points: [0, 0, xSize, 0],
|
||||
stroke: _y % 64 ? fineGridLineColor : baseGridLineColor,
|
||||
strokeWidth,
|
||||
listening: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return renderBackground;
|
||||
};
|
||||
|
@ -6,16 +6,11 @@ import {
|
||||
createObjectGroup,
|
||||
updateImageSource,
|
||||
} 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 { isEqual } from 'lodash-es';
|
||||
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.
|
||||
* @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.
|
||||
* @param manager The konva node manager
|
||||
* @param getControlAdapterEntityStates A function to get all control adapter entities
|
||||
* @returns A function to render all control adapters
|
||||
*/
|
||||
export const getRenderControlAdapters =
|
||||
(arg: {
|
||||
manager: KonvaNodeManager;
|
||||
getControlAdapterEntityStates: () => CanvasV2State['controlAdapters']['entities'];
|
||||
}) =>
|
||||
(): void => {
|
||||
const { manager, getControlAdapterEntityStates } = arg;
|
||||
export const getRenderControlAdapters = (manager: KonvaNodeManager) => {
|
||||
const { getControlAdapterEntityStates } = manager.stateApi;
|
||||
|
||||
function renderControlAdapters(): void {
|
||||
const entities = getControlAdapterEntityStates();
|
||||
// Destroy nonexistent layers
|
||||
for (const adapters of manager.getAll('control_adapter')) {
|
||||
@ -122,4 +113,7 @@ export const getRenderControlAdapters =
|
||||
for (const entity of entities) {
|
||||
renderControlAdapter(manager, entity);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return renderControlAdapters;
|
||||
};
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
getRectShape,
|
||||
} from 'features/controlLayers/konva/renderers/objects';
|
||||
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';
|
||||
|
||||
/**
|
||||
@ -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 getEntityState A function to get the inpaint mask entity state
|
||||
* @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
|
||||
* @returns A function to render the inpaint mask
|
||||
*/
|
||||
export const getRenderInpaintMask =
|
||||
(arg: {
|
||||
manager: KonvaNodeManager;
|
||||
getInpaintMaskEntityState: () => CanvasV2State['inpaintMask'];
|
||||
getMaskOpacity: () => number;
|
||||
getToolState: () => CanvasV2State['tool'];
|
||||
getSelectedEntity: () => CanvasEntity | null;
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void;
|
||||
}) =>
|
||||
(): void => {
|
||||
const { manager, getInpaintMaskEntityState, getMaskOpacity, getToolState, getSelectedEntity, onPosChanged } = arg;
|
||||
export const getRenderInpaintMask = (manager: KonvaNodeManager) => {
|
||||
const { getInpaintMaskEntityState, getMaskOpacity, getToolState, getSelectedEntity, onPosChanged } = manager.stateApi;
|
||||
|
||||
function renderInpaintMask(): void {
|
||||
const entity = getInpaintMaskEntityState();
|
||||
const globalMaskLayerOpacity = getMaskOpacity();
|
||||
const toolState = getToolState();
|
||||
@ -228,4 +216,7 @@ export const getRenderInpaintMask =
|
||||
// } else {
|
||||
// bboxRect.visible(false);
|
||||
// }
|
||||
};
|
||||
}
|
||||
|
||||
return renderInpaintMask;
|
||||
};
|
||||
|
@ -15,13 +15,9 @@ import {
|
||||
getRectShape,
|
||||
} from 'features/controlLayers/konva/renderers/objects';
|
||||
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';
|
||||
|
||||
/**
|
||||
* Logic for creating and rendering raster layers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets layer entity's konva nodes and entity adapter, creating them if they do not exist.
|
||||
* @param manager The konva node manager
|
||||
@ -137,20 +133,12 @@ export const renderLayer = async (
|
||||
/**
|
||||
* Gets a function to render all layers.
|
||||
* @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
|
||||
*/
|
||||
export const getRenderLayers =
|
||||
(arg: {
|
||||
manager: KonvaNodeManager;
|
||||
getLayerEntityStates: () => CanvasV2State['layers']['entities'];
|
||||
getToolState: () => CanvasV2State['tool'];
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void;
|
||||
}) =>
|
||||
(): void => {
|
||||
const { manager, getLayerEntityStates, getToolState, onPosChanged } = arg;
|
||||
export const getRenderLayers = (manager: KonvaNodeManager) => {
|
||||
const { getLayerEntityStates, getToolState, onPosChanged } = manager.stateApi;
|
||||
|
||||
function renderLayers(): void {
|
||||
const entities = getLayerEntityStates();
|
||||
const tool = getToolState();
|
||||
// Destroy nonexistent layers
|
||||
@ -162,4 +150,7 @@ export const getRenderLayers =
|
||||
for (const entity of entities) {
|
||||
renderLayer(manager, entity, tool.selected, onPosChanged);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return renderLayers;
|
||||
};
|
||||
|
@ -19,9 +19,9 @@ import {
|
||||
PREVIEW_TOOL_GROUP_ID,
|
||||
} from 'features/controlLayers/konva/naming';
|
||||
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 type { IRect, Vector2d } from 'konva/lib/types';
|
||||
import type { IRect } from 'konva/lib/types';
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
/**
|
||||
@ -245,13 +245,12 @@ const NO_ANCHORS: string[] = [];
|
||||
/**
|
||||
* Gets the bbox render function.
|
||||
* @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
|
||||
*/
|
||||
export const getRenderBbox =
|
||||
(manager: KonvaNodeManager, getBbox: () => CanvasV2State['bbox'], getToolState: () => CanvasV2State['tool']) =>
|
||||
(): void => {
|
||||
export const getRenderBbox = (manager: KonvaNodeManager) => {
|
||||
const { getBbox, getToolState } = manager.stateApi;
|
||||
|
||||
return (): void => {
|
||||
const bbox = getBbox();
|
||||
const toolState = getToolState();
|
||||
manager.preview.bbox.group.listening(toolState.selected === 'bbox');
|
||||
@ -270,6 +269,7 @@ export const getRenderBbox =
|
||||
enabledAnchors: toolState.selected === 'bbox' ? ALL_ANCHORS : NO_ANCHORS,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the tool preview konva nodes.
|
||||
@ -328,39 +328,21 @@ export const createToolPreviewNodes = (): KonvaNodeManager['preview']['tool'] =>
|
||||
|
||||
/**
|
||||
* Gets the tool preview (brush, eraser, rect) render function.
|
||||
* @param arg.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
|
||||
* @param manager The konva node manager
|
||||
* @returns The tool preview render function
|
||||
*/
|
||||
export const getRenderToolPreview =
|
||||
(arg: {
|
||||
manager: KonvaNodeManager;
|
||||
getToolState: () => CanvasV2State['tool'];
|
||||
getCurrentFill: () => RgbaColor;
|
||||
getSelectedEntity: () => CanvasEntity | null;
|
||||
getLastCursorPos: () => Vector2d | null;
|
||||
getLastMouseDownPos: () => Vector2d | null;
|
||||
getIsDrawing: () => boolean;
|
||||
getIsMouseDown: () => boolean;
|
||||
}) =>
|
||||
(): void => {
|
||||
const {
|
||||
manager,
|
||||
getToolState,
|
||||
getCurrentFill,
|
||||
getSelectedEntity,
|
||||
getLastCursorPos,
|
||||
getLastMouseDownPos,
|
||||
getIsDrawing,
|
||||
getIsMouseDown,
|
||||
} = arg;
|
||||
export const getRenderToolPreview = (manager: KonvaNodeManager) => {
|
||||
const {
|
||||
getToolState,
|
||||
getCurrentFill,
|
||||
getSelectedEntity,
|
||||
getLastCursorPos,
|
||||
getLastMouseDownPos,
|
||||
getIsDrawing,
|
||||
getIsMouseDown,
|
||||
} = manager.stateApi;
|
||||
|
||||
return (): void => {
|
||||
const stage = manager.stage;
|
||||
const layerCount = manager.adapters.size;
|
||||
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
|
||||
@ -493,13 +476,13 @@ export const createDocumentOverlay = (): KonvaNodeManager['preview']['documentOv
|
||||
|
||||
/**
|
||||
* Gets the document overlay render function.
|
||||
* @param arg.manager The konva node manager
|
||||
* @param arg.getDocument A function to get the document state
|
||||
* @param manager The konva node manager
|
||||
* @returns The document overlay render function
|
||||
*/
|
||||
export const getRenderDocumentOverlay =
|
||||
(arg: { manager: KonvaNodeManager; getDocument: () => CanvasV2State['document'] }) => (): void => {
|
||||
const { manager, getDocument } = arg;
|
||||
export const getRenderDocumentOverlay = (manager: KonvaNodeManager) => {
|
||||
const { getDocument } = manager.stateApi;
|
||||
|
||||
function renderDocumentOverlay(): void {
|
||||
const document = getDocument();
|
||||
const stage = manager.stage;
|
||||
|
||||
@ -524,4 +507,7 @@ export const getRenderDocumentOverlay =
|
||||
width: document.width,
|
||||
height: document.height,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return renderDocumentOverlay;
|
||||
};
|
||||
|
@ -19,7 +19,6 @@ import { mapId } from 'features/controlLayers/konva/util';
|
||||
import type {
|
||||
CanvasEntity,
|
||||
CanvasEntityIdentifier,
|
||||
CanvasV2State,
|
||||
PosChangedArg,
|
||||
RegionEntity,
|
||||
Tool,
|
||||
@ -230,25 +229,13 @@ export const renderRegion = (
|
||||
|
||||
/**
|
||||
* Gets a function to render all regions.
|
||||
* @param arg.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
|
||||
* @param manager The konva node manager
|
||||
* @returns A function to render all regions
|
||||
*/
|
||||
export const getRenderRegions =
|
||||
(arg: {
|
||||
manager: KonvaNodeManager;
|
||||
getRegionEntityStates: () => CanvasV2State['regions']['entities'];
|
||||
getMaskOpacity: () => number;
|
||||
getToolState: () => CanvasV2State['tool'];
|
||||
getSelectedEntity: () => CanvasEntity | null;
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void;
|
||||
}) =>
|
||||
() => {
|
||||
const { manager, getRegionEntityStates, getMaskOpacity, getToolState, getSelectedEntity, onPosChanged } = arg;
|
||||
export const getRenderRegions = (manager: KonvaNodeManager) => {
|
||||
const { getRegionEntityStates, getMaskOpacity, getToolState, getSelectedEntity, onPosChanged } = manager.stateApi;
|
||||
|
||||
function renderRegions(): void {
|
||||
const entities = getRegionEntityStates();
|
||||
const maskOpacity = getMaskOpacity();
|
||||
const toolState = getToolState();
|
||||
@ -264,4 +251,7 @@ export const getRenderRegions =
|
||||
for (const entity of entities) {
|
||||
renderRegion(manager, entity, maskOpacity, toolState.selected, selectedEntity, onPosChanged);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return renderRegions;
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ import {
|
||||
getRenderToolPreview,
|
||||
} from 'features/controlLayers/konva/renderers/preview';
|
||||
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 {
|
||||
$stageAttrs,
|
||||
bboxChanged,
|
||||
@ -283,7 +283,7 @@ export const initializeRenderer = (
|
||||
spaceKey = val;
|
||||
};
|
||||
|
||||
const manager = new KonvaNodeManager(stage);
|
||||
const manager = new KonvaNodeManager(stage, container);
|
||||
$nodeManager.set(manager);
|
||||
|
||||
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.documentOverlay.group);
|
||||
manager.stage.add(manager.preview.layer);
|
||||
|
||||
const cleanupListeners = setStageEventHandlers({
|
||||
manager,
|
||||
manager.stateApi = {
|
||||
// Read-only state
|
||||
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,
|
||||
setToolBuffer,
|
||||
getIsDrawing,
|
||||
setIsDrawing,
|
||||
getIsMouseDown,
|
||||
setIsMouseDown,
|
||||
getSelectedEntity,
|
||||
getLastAddedPoint,
|
||||
setLastAddedPoint,
|
||||
getLastCursorPos,
|
||||
@ -318,61 +332,37 @@ export const initializeRenderer = (
|
||||
getSpaceKey,
|
||||
setSpaceKey,
|
||||
setStageAttrs: $stageAttrs.set,
|
||||
getBbox,
|
||||
getSettings,
|
||||
|
||||
// Callbacks
|
||||
onBrushLineAdded,
|
||||
onEraserLineAdded,
|
||||
onPointAddedToLine,
|
||||
onRectShapeAdded,
|
||||
onBrushWidthChanged,
|
||||
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.
|
||||
// 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.
|
||||
const debouncedUpdateBboxes = debounce(updateBboxes, 300);
|
||||
|
||||
manager.renderers = {
|
||||
renderRegions: getRenderRegions({
|
||||
manager,
|
||||
getRegionEntityStates,
|
||||
getMaskOpacity,
|
||||
getToolState,
|
||||
getSelectedEntity,
|
||||
onPosChanged,
|
||||
}),
|
||||
renderLayers: getRenderLayers({ manager, getLayerEntityStates, getToolState, onPosChanged }),
|
||||
renderControlAdapters: getRenderControlAdapters({ manager, getControlAdapterEntityStates }),
|
||||
renderInpaintMask: getRenderInpaintMask({
|
||||
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,
|
||||
}),
|
||||
manager.konvaApi = {
|
||||
renderRegions: getRenderRegions(manager),
|
||||
renderLayers: getRenderLayers(manager),
|
||||
renderControlAdapters: getRenderControlAdapters(manager),
|
||||
renderInpaintMask: getRenderInpaintMask(manager),
|
||||
renderBbox: getRenderBbox(manager),
|
||||
renderToolPreview: getRenderToolPreview(manager),
|
||||
renderDocumentOverlay: getRenderDocumentOverlay(manager),
|
||||
renderBackground: getRenderBackground(manager),
|
||||
arrangeEntities: getArrangeEntities(manager),
|
||||
fitDocumentToStage: getFitDocumentToStage(manager),
|
||||
fitStageToContainer: getFitStageToContainer(manager),
|
||||
};
|
||||
|
||||
const renderCanvas = () => {
|
||||
@ -392,7 +382,7 @@ export const initializeRenderer = (
|
||||
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
||||
) {
|
||||
logIfDebugging('Rendering layers');
|
||||
manager.renderers.renderLayers();
|
||||
manager.konvaApi.renderLayers();
|
||||
}
|
||||
|
||||
if (
|
||||
@ -402,7 +392,7 @@ export const initializeRenderer = (
|
||||
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
||||
) {
|
||||
logIfDebugging('Rendering regions');
|
||||
manager.renderers.renderRegions();
|
||||
manager.konvaApi.renderRegions();
|
||||
}
|
||||
|
||||
if (
|
||||
@ -412,22 +402,22 @@ export const initializeRenderer = (
|
||||
canvasV2.tool.selected !== prevCanvasV2.tool.selected
|
||||
) {
|
||||
logIfDebugging('Rendering inpaint mask');
|
||||
manager.renderers.renderInpaintMask();
|
||||
manager.konvaApi.renderInpaintMask();
|
||||
}
|
||||
|
||||
if (isFirstRender || canvasV2.controlAdapters.entities !== prevCanvasV2.controlAdapters.entities) {
|
||||
logIfDebugging('Rendering control adapters');
|
||||
manager.renderers.renderControlAdapters();
|
||||
manager.konvaApi.renderControlAdapters();
|
||||
}
|
||||
|
||||
if (isFirstRender || canvasV2.document !== prevCanvasV2.document) {
|
||||
logIfDebugging('Rendering document bounds overlay');
|
||||
manager.renderers.renderDocumentOverlay();
|
||||
manager.konvaApi.renderDocumentOverlay();
|
||||
}
|
||||
|
||||
if (isFirstRender || canvasV2.bbox !== prevCanvasV2.bbox || canvasV2.tool.selected !== prevCanvasV2.tool.selected) {
|
||||
logIfDebugging('Rendering generation bbox');
|
||||
manager.renderers.renderBbox();
|
||||
manager.konvaApi.renderBbox();
|
||||
}
|
||||
|
||||
if (
|
||||
@ -447,7 +437,7 @@ export const initializeRenderer = (
|
||||
canvasV2.regions.entities !== prevCanvasV2.regions.entities
|
||||
) {
|
||||
logIfDebugging('Arranging entities');
|
||||
manager.renderers.arrangeEntities();
|
||||
manager.konvaApi.arrangeEntities();
|
||||
}
|
||||
|
||||
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
|
||||
// document bounds overlay when the stage is resized.
|
||||
const 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);
|
||||
const resizeObserver = new ResizeObserver(manager.konvaApi.fitStageToContainer);
|
||||
resizeObserver.observe(container);
|
||||
fitStageToContainer();
|
||||
manager.konvaApi.fitStageToContainer();
|
||||
|
||||
const unsubscribeRenderer = subscribe(renderCanvas);
|
||||
|
||||
logIfDebugging('First render of konva stage');
|
||||
// On first render, the document should be fit to the stage.
|
||||
manager.renderers.fitDocumentToStage();
|
||||
manager.renderers.renderToolPreview();
|
||||
manager.konvaApi.fitDocumentToStage();
|
||||
manager.konvaApi.renderToolPreview();
|
||||
renderCanvas();
|
||||
|
||||
return () => {
|
||||
|
@ -1,23 +1,15 @@
|
||||
import { DOCUMENT_FIT_PADDING_PX } from 'features/controlLayers/konva/constants';
|
||||
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%.
|
||||
* 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 arg.getDocument A function to get the current document state
|
||||
* @param arg.setStageAttrs A function to set the stage attributes
|
||||
* @param manager The konva node manager
|
||||
* @returns A function to fit the document to the stage
|
||||
*/
|
||||
export const getFitDocumentToStage =
|
||||
(arg: {
|
||||
manager: KonvaNodeManager;
|
||||
getDocument: () => CanvasV2State['document'];
|
||||
setStageAttrs: (stageAttrs: StageAttrs) => void;
|
||||
}) =>
|
||||
(): void => {
|
||||
const { manager, getDocument, setStageAttrs } = arg;
|
||||
export const getFitDocumentToStage = (manager: KonvaNodeManager) => {
|
||||
function fitDocumentToStage(): void {
|
||||
const { getDocument, setStageAttrs } = manager.stateApi;
|
||||
const document = getDocument();
|
||||
// Fit & center the document on the stage
|
||||
const width = manager.stage.width();
|
||||
@ -29,4 +21,32 @@ export const getFitDocumentToStage =
|
||||
const y = (height - docHeightWithBuffer * scale) / 2 + DOCUMENT_FIT_PADDING_PX * scale;
|
||||
manager.stage.setAttrs({ x, y, width, height, scaleX: scale, scaleY: 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