feat(ui): split canvas tool previews into modules

This commit is contained in:
psychedelicious
2024-08-31 10:29:39 +10:00
parent 8c65f60e7d
commit d001a36e14
13 changed files with 651 additions and 387 deletions

View File

@@ -15,9 +15,8 @@ import {
} from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { MAX_CANVAS_SCALE, MIN_CANVAS_SCALE } from 'features/controlLayers/konva/constants';
import { snapToNearest } from 'features/controlLayers/konva/util';
import { clamp, round } from 'lodash-es';
import { round } from 'lodash-es';
import { computed } from 'nanostores';
import type { KeyboardEvent } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';
@@ -102,7 +101,7 @@ export const CanvasScale = memo(() => {
setLocalScale(100);
return;
}
canvasManager.stage.setScale(clamp(localScale / 100, MIN_CANVAS_SCALE, MAX_CANVAS_SCALE));
canvasManager.stage.setScale(localScale / 100);
}, [canvasManager, localScale]);
const onChangeNumberInput = useCallback((valueAsString: string, valueAsNumber: number) => {
@@ -130,8 +129,8 @@ export const CanvasScale = memo(() => {
<NumberInput
display="flex"
alignItems="center"
min={MIN_CANVAS_SCALE * 100}
max={MAX_CANVAS_SCALE * 100}
min={canvasManager.stage.config.MIN_SCALE * 100}
max={canvasManager.stage.config.MAX_SCALE * 100}
value={localScale}
onChange={onChangeNumberInput}
onBlur={onBlur}

View File

@@ -7,7 +7,7 @@ import { useAppStore } from 'app/store/nanostores/store';
import { useAppSelector } from 'app/store/storeHooks';
import { HeadsUpDisplay } from 'features/controlLayers/components/HeadsUpDisplay';
import { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
import { TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL } from 'features/controlLayers/konva/patterns/transparency-checkerboard-pattern';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
import Konva from 'konva';
@@ -82,7 +82,7 @@ export const StageComponent = memo(() => {
<Flex
position="absolute"
borderRadius="base"
bgImage={TRANSPARENCY_CHECKER_PATTERN}
bgImage={TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL}
top={0}
right={0}
bottom={0}

View File

@@ -4,7 +4,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { rgbColorToString } from 'common/util/colorCodeTransformers';
import { useEntityAdapter } from 'features/controlLayers/contexts/EntityAdapterContext';
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
import { TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL } from 'features/controlLayers/konva/patterns/transparency-checkerboard-pattern';
import { selectCanvasSlice, selectEntity } from 'features/controlLayers/store/selectors';
import { memo, useEffect, useMemo, useRef } from 'react';
import { useSelector } from 'react-redux';
@@ -88,7 +88,7 @@ export const CanvasEntityPreviewImage = memo(() => {
right={0}
bottom={0}
left={0}
bgImage={TRANSPARENCY_CHECKER_PATTERN}
bgImage={TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL}
bgSize="5px"
opacity={0.1}
/>

View File

@@ -0,0 +1,141 @@
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleABC } from 'features/controlLayers/konva/CanvasModuleABC';
import type { CanvasToolModule } from 'features/controlLayers/konva/CanvasToolModule';
import { alignCoordForTool, getPrefixedId } from 'features/controlLayers/konva/util';
import Konva from 'konva';
import type { Logger } from 'roarr';
type BrushToolPreviewConfig = {
/**
* The inner border color for the brush tool preview.
*/
BORDER_INNER_COLOR: string;
/**
* The outer border color for the brush tool preview.
*/
BORDER_OUTER_COLOR: string;
};
const DEFAULT_CONFIG: BrushToolPreviewConfig = {
BORDER_INNER_COLOR: 'rgba(0,0,0,1)',
BORDER_OUTER_COLOR: 'rgba(255,255,255,0.8)',
};
export class CanvasBrushToolPreview extends CanvasModuleABC {
readonly type = 'brush_tool_preview';
id: string;
path: string[];
parent: CanvasToolModule;
manager: CanvasManager;
log: Logger;
subscriptions: Set<() => void> = new Set();
config: BrushToolPreviewConfig;
konva: {
group: Konva.Group;
fillCircle: Konva.Circle;
innerBorder: Konva.Ring;
outerBorder: Konva.Ring;
};
constructor(parent: CanvasToolModule, config?: Partial<BrushToolPreviewConfig>) {
super();
this.id = getPrefixedId(this.type);
this.parent = parent;
this.manager = this.parent.manager;
this.path = this.parent.path.concat(this.id);
this.log = this.manager.buildLogger(this.getLoggingContext);
this.config = { ...DEFAULT_CONFIG, ...config };
this.log.debug('Creating brush tool preview module');
this.konva = {
group: new Konva.Group({ name: `${this.type}:brush_group`, listening: false }),
fillCircle: new Konva.Circle({
name: `${this.type}:brush_fill_circle`,
listening: false,
strokeEnabled: false,
}),
innerBorder: new Konva.Ring({
name: `${this.type}:brush_inner_border_ring`,
listening: false,
innerRadius: 0,
outerRadius: 0,
fill: this.config.BORDER_INNER_COLOR,
strokeEnabled: false,
}),
outerBorder: new Konva.Ring({
name: `${this.type}:brush_outer_border_ring`,
listening: false,
innerRadius: 0,
outerRadius: 0,
fill: this.config.BORDER_OUTER_COLOR,
strokeEnabled: false,
}),
};
this.konva.group.add(this.konva.fillCircle, this.konva.innerBorder, this.konva.outerBorder);
}
render = () => {
const cursorPos = this.manager.stateApi.$lastCursorPos.get();
if (!cursorPos) {
return;
}
const toolState = this.manager.stateApi.getToolState();
const brushPreviewFill = this.manager.stateApi.getBrushPreviewFill();
const alignedCursorPos = alignCoordForTool(cursorPos, toolState.brush.width);
const radius = toolState.brush.width / 2;
// The circle is scaled
this.konva.fillCircle.setAttrs({
x: alignedCursorPos.x,
y: alignedCursorPos.y,
radius,
fill: rgbaColorToString(brushPreviewFill),
});
// But the borders are in screen-pixels
const onePixel = this.manager.stage.getScaledPixels(1);
const twoPixels = this.manager.stage.getScaledPixels(2);
this.konva.innerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: radius,
outerRadius: radius + onePixel,
});
this.konva.outerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: radius + onePixel,
outerRadius: radius + twoPixels,
});
};
setVisibility = (visible: boolean) => {
this.konva.group.visible(visible);
};
repr = () => {
return {
id: this.id,
type: this.type,
path: this.path,
config: this.config,
};
};
destroy = () => {
this.log.debug('Destroying brush tool preview module');
this.subscriptions.forEach((unsubscribe) => unsubscribe());
this.konva.group.destroy();
};
getLoggingContext = () => {
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
};
}

View File

@@ -0,0 +1,287 @@
import { rgbColorToString } from 'common/util/colorCodeTransformers';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleABC } from 'features/controlLayers/konva/CanvasModuleABC';
import type { CanvasToolModule } from 'features/controlLayers/konva/CanvasToolModule';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import Konva from 'konva';
import type { Logger } from 'roarr';
type ColorPickerToolConfig = {
/**
* The inner radius of the ring.
*/
RING_INNER_RADIUS: number;
/**
* The outer radius of the ring.
*/
RING_OUTER_RADIUS: number;
/**
* The inner border color of the outside edge of ring.
*/
RING_BORDER_INNER_COLOR: string;
/**
* The outer border color of the outside edge of ring.
*/
RING_BORDER_OUTER_COLOR: string;
/**
* The radius of the space between the center of the ring and start of the crosshair lines.
*/
CROSSHAIR_INNER_RADIUS: number;
/**
* The length of the crosshair lines.
*/
CROSSHAIR_LINE_LENGTH: number;
/**
* The thickness of the crosshair lines.
*/
CROSSHAIR_LINE_THICKNESS: number;
/**
* The color of the crosshair lines.
*/
CROSSHAIR_LINE_COLOR: string;
/**
* The thickness of the crosshair lines borders
*/
CROSSHAIR_LINE_BORDER_THICKNESS: number;
/**
* The color of the crosshair line borders.
*/
CROSSHAIR_BORDER_COLOR: string;
};
const DEFAULT_CONFIG: ColorPickerToolConfig = {
RING_INNER_RADIUS: 25,
RING_OUTER_RADIUS: 35,
RING_BORDER_INNER_COLOR: 'rgba(0,0,0,1)',
RING_BORDER_OUTER_COLOR: 'rgba(255,255,255,0.8)',
CROSSHAIR_INNER_RADIUS: 5,
CROSSHAIR_LINE_THICKNESS: 1.5,
CROSSHAIR_LINE_BORDER_THICKNESS: 0.75,
CROSSHAIR_LINE_LENGTH: 10,
CROSSHAIR_LINE_COLOR: 'rgba(0,0,0,1)',
CROSSHAIR_BORDER_COLOR: 'rgba(255,255,255,0.8)',
};
export class CanvasColorPickerToolPreview extends CanvasModuleABC {
readonly type = 'color_picker_tool_preview';
id: string;
path: string[];
parent: CanvasToolModule;
manager: CanvasManager;
log: Logger;
subscriptions: Set<() => void> = new Set();
config: ColorPickerToolConfig;
konva: {
group: Konva.Group;
ringNewColor: Konva.Ring;
ringOldColor: Konva.Arc;
ringInnerBorder: Konva.Ring;
ringOuterBorder: Konva.Ring;
crosshairNorthInner: Konva.Line;
crosshairNorthOuter: Konva.Line;
crosshairEastInner: Konva.Line;
crosshairEastOuter: Konva.Line;
crosshairSouthInner: Konva.Line;
crosshairSouthOuter: Konva.Line;
crosshairWestInner: Konva.Line;
crosshairWestOuter: Konva.Line;
};
constructor(parent: CanvasToolModule, config?: Partial<ColorPickerToolConfig>) {
super();
this.id = getPrefixedId(this.type);
this.parent = parent;
this.manager = this.parent.manager;
this.path = this.parent.path.concat(this.id);
this.log = this.manager.buildLogger(this.getLoggingContext);
this.config = { ...DEFAULT_CONFIG, ...config };
this.log.debug('Creating color picker tool preview module');
this.konva = {
group: new Konva.Group({ name: `${this.type}:color_picker_group`, listening: false }),
ringNewColor: new Konva.Ring({
name: `${this.type}:color_picker_new_color_ring`,
innerRadius: 0,
outerRadius: 0,
strokeEnabled: false,
}),
ringOldColor: new Konva.Arc({
name: `${this.type}:color_picker_old_color_arc`,
innerRadius: 0,
outerRadius: 0,
angle: 180,
strokeEnabled: false,
}),
ringInnerBorder: new Konva.Ring({
name: `${this.type}:color_picker_inner_border_ring`,
innerRadius: 0,
outerRadius: 0,
fill: this.config.RING_BORDER_INNER_COLOR,
strokeEnabled: false,
}),
ringOuterBorder: new Konva.Ring({
name: `${this.type}:color_picker_outer_border_ring`,
innerRadius: 0,
outerRadius: 0,
fill: this.config.RING_BORDER_OUTER_COLOR,
strokeEnabled: false,
}),
crosshairNorthInner: new Konva.Line({
name: `${this.type}:color_picker_crosshair_north1_line`,
stroke: this.config.CROSSHAIR_LINE_COLOR,
}),
crosshairNorthOuter: new Konva.Line({
name: `${this.type}:color_picker_crosshair_north2_line`,
stroke: this.config.CROSSHAIR_BORDER_COLOR,
}),
crosshairEastInner: new Konva.Line({
name: `${this.type}:color_picker_crosshair_east1_line`,
stroke: this.config.CROSSHAIR_LINE_COLOR,
}),
crosshairEastOuter: new Konva.Line({
name: `${this.type}:color_picker_crosshair_east2_line`,
stroke: this.config.CROSSHAIR_BORDER_COLOR,
}),
crosshairSouthInner: new Konva.Line({
name: `${this.type}:color_picker_crosshair_south1_line`,
stroke: this.config.CROSSHAIR_LINE_COLOR,
}),
crosshairSouthOuter: new Konva.Line({
name: `${this.type}:color_picker_crosshair_south2_line`,
stroke: this.config.CROSSHAIR_BORDER_COLOR,
}),
crosshairWestInner: new Konva.Line({
name: `${this.type}:color_picker_crosshair_west1_line`,
stroke: this.config.CROSSHAIR_LINE_COLOR,
}),
crosshairWestOuter: new Konva.Line({
name: `${this.type}:color_picker_crosshair_west2_line`,
stroke: this.config.CROSSHAIR_BORDER_COLOR,
}),
};
this.konva.group.add(
this.konva.ringNewColor,
this.konva.ringOldColor,
this.konva.ringInnerBorder,
this.konva.ringOuterBorder,
this.konva.crosshairNorthOuter,
this.konva.crosshairNorthInner,
this.konva.crosshairEastOuter,
this.konva.crosshairEastInner,
this.konva.crosshairSouthOuter,
this.konva.crosshairSouthInner,
this.konva.crosshairWestOuter,
this.konva.crosshairWestInner
);
}
render = () => {
const cursorPos = this.manager.stateApi.$lastCursorPos.get();
if (!cursorPos) {
return;
}
const toolState = this.manager.stateApi.getToolState();
const colorUnderCursor = this.manager.stateApi.$colorUnderCursor.get();
const colorPickerInnerRadius = this.manager.stage.getScaledPixels(this.config.RING_INNER_RADIUS);
const colorPickerOuterRadius = this.manager.stage.getScaledPixels(this.config.RING_OUTER_RADIUS);
const onePixel = this.manager.stage.getScaledPixels(1);
const twoPixels = this.manager.stage.getScaledPixels(2);
this.konva.ringNewColor.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
fill: rgbColorToString(colorUnderCursor),
innerRadius: colorPickerInnerRadius,
outerRadius: colorPickerOuterRadius,
});
this.konva.ringOldColor.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
fill: rgbColorToString(toolState.fill),
innerRadius: colorPickerInnerRadius,
outerRadius: colorPickerOuterRadius,
});
this.konva.ringInnerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: colorPickerOuterRadius,
outerRadius: colorPickerOuterRadius + onePixel,
});
this.konva.ringOuterBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: colorPickerOuterRadius + onePixel,
outerRadius: colorPickerOuterRadius + twoPixels,
});
const size = this.manager.stage.getScaledPixels(this.config.CROSSHAIR_LINE_LENGTH);
const space = this.manager.stage.getScaledPixels(this.config.CROSSHAIR_INNER_RADIUS);
const innerThickness = this.manager.stage.getScaledPixels(this.config.CROSSHAIR_LINE_THICKNESS);
const outerThickness = this.manager.stage.getScaledPixels(
this.config.CROSSHAIR_LINE_THICKNESS + this.config.CROSSHAIR_LINE_BORDER_THICKNESS * 2
);
this.konva.crosshairNorthOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x, cursorPos.y - size, cursorPos.x, cursorPos.y - space],
});
this.konva.crosshairNorthInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x, cursorPos.y - size, cursorPos.x, cursorPos.y - space],
});
this.konva.crosshairEastOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x + space, cursorPos.y, cursorPos.x + size, cursorPos.y],
});
this.konva.crosshairEastInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x + space, cursorPos.y, cursorPos.x + size, cursorPos.y],
});
this.konva.crosshairSouthOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x, cursorPos.y + space, cursorPos.x, cursorPos.y + size],
});
this.konva.crosshairSouthInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x, cursorPos.y + space, cursorPos.x, cursorPos.y + size],
});
this.konva.crosshairWestOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x - space, cursorPos.y, cursorPos.x - size, cursorPos.y],
});
this.konva.crosshairWestInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x - space, cursorPos.y, cursorPos.x - size, cursorPos.y],
});
};
setVisibility = (visible: boolean) => {
this.konva.group.visible(visible);
};
repr = () => {
return {
id: this.id,
type: this.type,
path: this.path,
config: this.config,
};
};
destroy = () => {
this.log.debug('Destroying color picker tool preview module');
this.subscriptions.forEach((unsubscribe) => unsubscribe());
this.konva.group.destroy();
};
getLoggingContext = () => {
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
};
}

View File

@@ -0,0 +1,141 @@
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleABC } from 'features/controlLayers/konva/CanvasModuleABC';
import type { CanvasToolModule } from 'features/controlLayers/konva/CanvasToolModule';
import { alignCoordForTool, getPrefixedId } from 'features/controlLayers/konva/util';
import Konva from 'konva';
import type { Logger } from 'roarr';
type EraserToolPreviewConfig = {
/**
* The inner border color for the eraser tool preview.
*/
BORDER_INNER_COLOR: string;
/**
* The outer border color for the eraser tool preview.
*/
BORDER_OUTER_COLOR: string;
};
const DEFAULT_CONFIG: EraserToolPreviewConfig = {
BORDER_INNER_COLOR: 'rgba(0,0,0,1)',
BORDER_OUTER_COLOR: 'rgba(255,255,255,0.8)',
};
export class CanvasEraserToolPreview extends CanvasModuleABC {
readonly type = 'eraser_tool_preview';
id: string;
path: string[];
parent: CanvasToolModule;
manager: CanvasManager;
log: Logger;
subscriptions: Set<() => void> = new Set();
config: EraserToolPreviewConfig;
konva: {
group: Konva.Group;
cutoutCircle: Konva.Circle;
innerBorder: Konva.Ring;
outerBorder: Konva.Ring;
};
constructor(parent: CanvasToolModule, config?: Partial<EraserToolPreviewConfig>) {
super();
this.id = getPrefixedId(this.type);
this.parent = parent;
this.manager = this.parent.manager;
this.path = this.parent.path.concat(this.id);
this.log = this.manager.buildLogger(this.getLoggingContext);
this.config = { ...DEFAULT_CONFIG, ...config };
this.log.debug('Creating eraser tool preview module');
this.konva = {
group: new Konva.Group({ name: `${this.type}:eraser_group`, listening: false }),
cutoutCircle: new Konva.Circle({
name: `${this.type}:eraser_cutout_circle`,
listening: false,
strokeEnabled: false,
// The fill is used only to erase what is underneath it, so its color doesn't matter - just needs to be opaque
fill: 'white',
globalCompositeOperation: 'destination-out',
}),
innerBorder: new Konva.Ring({
name: `${this.type}:eraser_inner_border_ring`,
listening: false,
innerRadius: 0,
outerRadius: 0,
fill: this.config.BORDER_INNER_COLOR,
strokeEnabled: false,
}),
outerBorder: new Konva.Ring({
name: `${this.type}:eraser_outer_border_ring`,
innerRadius: 0,
outerRadius: 0,
fill: this.config.BORDER_OUTER_COLOR,
strokeEnabled: false,
}),
};
this.konva.group.add(this.konva.cutoutCircle, this.konva.innerBorder, this.konva.outerBorder);
}
render = () => {
const cursorPos = this.manager.stateApi.$lastCursorPos.get();
if (!cursorPos) {
return;
}
const toolState = this.manager.stateApi.getToolState();
const alignedCursorPos = alignCoordForTool(cursorPos, toolState.eraser.width);
const radius = toolState.eraser.width / 2;
// The circle is scaled
this.konva.cutoutCircle.setAttrs({
x: alignedCursorPos.x,
y: alignedCursorPos.y,
radius,
});
// But the borders are in screen-pixels
const onePixel = this.manager.stage.getScaledPixels(1);
const twoPixels = this.manager.stage.getScaledPixels(2);
this.konva.innerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: radius,
outerRadius: radius + onePixel,
});
this.konva.outerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: radius + onePixel,
outerRadius: radius + twoPixels,
});
};
setVisibility = (visible: boolean) => {
this.konva.group.visible(visible);
};
repr = () => {
return {
id: this.id,
type: this.type,
path: this.path,
config: this.config,
};
};
destroy = () => {
this.log.debug('Destroying eraser tool preview module');
this.subscriptions.forEach((unsubscribe) => unsubscribe());
this.konva.group.destroy();
};
getLoggingContext = () => {
return { ...this.parent.getLoggingContext(), path: this.path.join('.') };
};
}

View File

@@ -1,6 +1,5 @@
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleABC } from 'features/controlLayers/konva/CanvasModuleABC';
import { CANVAS_SCALE_BY } from 'features/controlLayers/konva/constants';
import { getPrefixedId, getRectUnion } from 'features/controlLayers/konva/util';
import type { CanvasEntityIdentifier, Coordinate, Dimensions, Rect } from 'features/controlLayers/store/types';
import type Konva from 'konva';
@@ -8,27 +7,52 @@ import type { KonvaEventObject } from 'konva/lib/Node';
import { clamp } from 'lodash-es';
import type { Logger } from 'roarr';
type CanvasStageModuleConfig = {
/**
* The minimum (furthest-zoomed-in) scale of the canvas
*/
MIN_SCALE: number;
/**
* The maximum (furthest-zoomed-out) scale of the canvas
*/
MAX_SCALE: number;
/**
* The factor by which the canvas should be scaled when zooming in/out
*/
SCALE_FACTOR: number;
};
const DEFAULT_CONFIG: CanvasStageModuleConfig = {
MIN_SCALE: 0.1,
MAX_SCALE: 20,
SCALE_FACTOR: 0.999,
};
export class CanvasStageModule extends CanvasModuleABC {
readonly type = 'stage';
static MIN_CANVAS_SCALE = 0.1;
static MAX_CANVAS_SCALE = 20;
id: string;
path: string[];
konva: { stage: Konva.Stage };
manager: CanvasManager;
container: HTMLDivElement;
log: Logger;
config: CanvasStageModuleConfig;
subscriptions = new Set<() => void>();
constructor(stage: Konva.Stage, container: HTMLDivElement, manager: CanvasManager) {
constructor(
stage: Konva.Stage,
container: HTMLDivElement,
manager: CanvasManager,
config?: Partial<CanvasStageModuleConfig>
) {
super();
this.id = getPrefixedId('stage');
this.manager = manager;
this.path = this.manager.path.concat(this.id);
this.log = this.manager.buildLogger(this.getLoggingContext);
this.config = { ...DEFAULT_CONFIG, ...config };
this.log.debug('Creating stage module');
@@ -163,11 +187,7 @@ export class CanvasStageModule extends CanvasModuleABC {
*/
setScale = (scale: number, center: Coordinate = this.getCenter(true)) => {
this.log.trace('Setting scale');
const newScale = clamp(
Math.round(scale * 100) / 100,
CanvasStageModule.MIN_CANVAS_SCALE,
CanvasStageModule.MAX_CANVAS_SCALE
);
const newScale = clamp(Math.round(scale * 100) / 100, this.config.MIN_SCALE, this.config.MAX_SCALE);
const { x, y } = this.getPosition();
const oldScale = this.getScale();
@@ -207,7 +227,7 @@ export class CanvasStageModule extends CanvasModuleABC {
if (cursorPos) {
// When wheeling on trackpad, e.evt.ctrlKey is true - in that case, let's reverse the direction
const delta = e.evt.ctrlKey ? -e.evt.deltaY : e.evt.deltaY;
const scale = this.manager.stage.getScale() * CANVAS_SCALE_BY ** delta;
const scale = this.manager.stage.getScale() * this.config.SCALE_FACTOR ** delta;
this.manager.stage.setScale(scale, cursorPos);
}
};

View File

@@ -1,12 +1,9 @@
import { rgbaColorToString, rgbColorToString } from 'common/util/colorCodeTransformers';
import { CanvasBrushToolPreview } from 'features/controlLayers/konva/CanvasBrushToolPreview';
import { CanvasColorPickerToolPreview } from 'features/controlLayers/konva/CanvasColorPickerToolPreview';
import { CanvasEraserToolPreview } from 'features/controlLayers/konva/CanvasEraserToolPreview';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleABC } from 'features/controlLayers/konva/CanvasModuleABC';
import type { CanvasPreviewModule } from 'features/controlLayers/konva/CanvasPreviewModule';
import {
BRUSH_BORDER_INNER_COLOR,
BRUSH_BORDER_OUTER_COLOR,
BRUSH_SPACING_TARGET_SCALE,
} from 'features/controlLayers/konva/constants';
import {
alignCoordForTool,
calculateNewBrushSizeFromWheelDelta,
@@ -31,209 +28,58 @@ import Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import type { Logger } from 'roarr';
type CanvasToolModuleConfig = {
BRUSH_SPACING_TARGET_SCALE: number;
};
const DEFAULT_CONFIG: CanvasToolModuleConfig = {
BRUSH_SPACING_TARGET_SCALE: 0.1,
};
export class CanvasToolModule extends CanvasModuleABC {
readonly type = 'tool';
static readonly COLOR_PICKER_RADIUS = 25;
static readonly COLOR_PICKER_THICKNESS = 15;
static readonly COLOR_PICKER_CROSSHAIR_SPACE = 5;
static readonly COLOR_PICKER_CROSSHAIR_INNER_THICKNESS = 1.5;
static readonly COLOR_PICKER_CROSSHAIR_OUTER_THICKNESS = 3;
static readonly COLOR_PICKER_CROSSHAIR_SIZE = 10;
id: string;
path: string[];
parent: CanvasPreviewModule;
manager: CanvasManager;
log: Logger;
subscriptions: Set<() => void> = new Set();
config: CanvasToolModuleConfig;
brushToolPreview: CanvasBrushToolPreview;
eraserToolPreview: CanvasEraserToolPreview;
colorPickerToolPreview: CanvasColorPickerToolPreview;
konva: {
stage: Konva.Stage;
group: Konva.Group;
brush: {
group: Konva.Group;
fillCircle: Konva.Circle;
innerBorder: Konva.Ring;
outerBorder: Konva.Ring;
};
eraser: {
group: Konva.Group;
fillCircle: Konva.Circle;
innerBorder: Konva.Ring;
outerBorder: Konva.Ring;
};
colorPicker: {
group: Konva.Group;
newColor: Konva.Ring;
oldColor: Konva.Arc;
innerBorder: Konva.Ring;
outerBorder: Konva.Ring;
crosshairNorthInner: Konva.Line;
crosshairNorthOuter: Konva.Line;
crosshairEastInner: Konva.Line;
crosshairEastOuter: Konva.Line;
crosshairSouthInner: Konva.Line;
crosshairSouthOuter: Konva.Line;
crosshairWestInner: Konva.Line;
crosshairWestOuter: Konva.Line;
};
};
/**
* A set of subscriptions that should be cleaned up when the transformer is destroyed.
*/
subscriptions: Set<() => void> = new Set();
constructor(parent: CanvasPreviewModule) {
constructor(parent: CanvasPreviewModule, config?: Partial<CanvasToolModuleConfig>) {
super();
this.id = getPrefixedId(this.type);
this.parent = parent;
this.manager = this.parent.manager;
this.path = this.parent.path.concat(this.id);
this.log = this.manager.buildLogger(this.getLoggingContext);
this.config = { ...DEFAULT_CONFIG, ...config };
this.log.debug('Creating tool module');
this.brushToolPreview = new CanvasBrushToolPreview(this);
this.eraserToolPreview = new CanvasEraserToolPreview(this);
this.colorPickerToolPreview = new CanvasColorPickerToolPreview(this);
this.konva = {
stage: this.manager.stage.konva.stage,
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
brush: {
group: new Konva.Group({ name: `${this.type}:brush_group`, listening: false }),
fillCircle: new Konva.Circle({
name: `${this.type}:brush_fill_circle`,
listening: false,
strokeEnabled: false,
}),
innerBorder: new Konva.Ring({
name: `${this.type}:brush_inner_border_ring`,
listening: false,
innerRadius: 0,
outerRadius: 0,
fill: BRUSH_BORDER_INNER_COLOR,
strokeEnabled: false,
}),
outerBorder: new Konva.Ring({
name: `${this.type}:brush_outer_border_ring`,
listening: false,
innerRadius: 0,
outerRadius: 0,
fill: BRUSH_BORDER_OUTER_COLOR,
strokeEnabled: false,
}),
},
eraser: {
group: new Konva.Group({ name: `${this.type}:eraser_group`, listening: false }),
fillCircle: new Konva.Circle({
name: `${this.type}:eraser_fill_circle`,
listening: false,
strokeEnabled: false,
fill: 'white',
globalCompositeOperation: 'destination-out',
}),
innerBorder: new Konva.Ring({
name: `${this.type}:eraser_inner_border_ring`,
listening: false,
innerRadius: 0,
outerRadius: 0,
fill: BRUSH_BORDER_INNER_COLOR,
strokeEnabled: false,
}),
outerBorder: new Konva.Ring({
name: `${this.type}:eraser_outer_border_ring`,
innerRadius: 0,
outerRadius: 0,
fill: BRUSH_BORDER_OUTER_COLOR,
strokeEnabled: false,
}),
},
colorPicker: {
group: new Konva.Group({ name: `${this.type}:color_picker_group`, listening: false }),
newColor: new Konva.Ring({
name: `${this.type}:color_picker_new_color_ring`,
innerRadius: 0,
outerRadius: 0,
strokeEnabled: false,
}),
oldColor: new Konva.Arc({
name: `${this.type}:color_picker_old_color_arc`,
innerRadius: 0,
outerRadius: 0,
angle: 180,
strokeEnabled: false,
}),
innerBorder: new Konva.Ring({
name: `${this.type}:color_picker_inner_border_ring`,
listening: false,
innerRadius: 0,
outerRadius: 0,
fill: BRUSH_BORDER_INNER_COLOR,
strokeEnabled: false,
}),
outerBorder: new Konva.Ring({
name: `${this.type}:color_picker_outer_border_ring`,
innerRadius: 0,
outerRadius: 0,
fill: BRUSH_BORDER_OUTER_COLOR,
strokeEnabled: false,
}),
crosshairNorthInner: new Konva.Line({
name: `${this.type}:color_picker_crosshair_north1_line`,
stroke: BRUSH_BORDER_INNER_COLOR,
}),
crosshairNorthOuter: new Konva.Line({
name: `${this.type}:color_picker_crosshair_north2_line`,
stroke: BRUSH_BORDER_OUTER_COLOR,
}),
crosshairEastInner: new Konva.Line({
name: `${this.type}:color_picker_crosshair_east1_line`,
stroke: BRUSH_BORDER_INNER_COLOR,
}),
crosshairEastOuter: new Konva.Line({
name: `${this.type}:color_picker_crosshair_east2_line`,
stroke: BRUSH_BORDER_OUTER_COLOR,
}),
crosshairSouthInner: new Konva.Line({
name: `${this.type}:color_picker_crosshair_south1_line`,
stroke: BRUSH_BORDER_INNER_COLOR,
}),
crosshairSouthOuter: new Konva.Line({
name: `${this.type}:color_picker_crosshair_south2_line`,
stroke: BRUSH_BORDER_OUTER_COLOR,
}),
crosshairWestInner: new Konva.Line({
name: `${this.type}:color_picker_crosshair_west1_line`,
stroke: BRUSH_BORDER_INNER_COLOR,
}),
crosshairWestOuter: new Konva.Line({
name: `${this.type}:color_picker_crosshair_west2_line`,
stroke: BRUSH_BORDER_OUTER_COLOR,
}),
},
};
this.konva.brush.group.add(this.konva.brush.fillCircle, this.konva.brush.innerBorder, this.konva.brush.outerBorder);
this.konva.group.add(this.konva.brush.group);
this.konva.eraser.group.add(
this.konva.eraser.fillCircle,
this.konva.eraser.innerBorder,
this.konva.eraser.outerBorder
);
this.konva.group.add(this.konva.eraser.group);
this.konva.colorPicker.group.add(
this.konva.colorPicker.newColor,
this.konva.colorPicker.oldColor,
this.konva.colorPicker.innerBorder,
this.konva.colorPicker.outerBorder,
this.konva.colorPicker.crosshairNorthOuter,
this.konva.colorPicker.crosshairNorthInner,
this.konva.colorPicker.crosshairEastOuter,
this.konva.colorPicker.crosshairEastInner,
this.konva.colorPicker.crosshairSouthOuter,
this.konva.colorPicker.crosshairSouthInner,
this.konva.colorPicker.crosshairWestOuter,
this.konva.colorPicker.crosshairWestInner
);
this.konva.group.add(this.konva.colorPicker.group);
this.konva.group.add(this.brushToolPreview.konva.group);
this.konva.group.add(this.eraserToolPreview.konva.group);
this.konva.group.add(this.colorPickerToolPreview.konva.group);
this.subscriptions.add(this.manager.stateApi.$stageAttrs.listen(this.render));
this.subscriptions.add(this.manager.stateApi.$toolState.listen(this.render));
@@ -245,9 +91,9 @@ export class CanvasToolModule extends CanvasModuleABC {
}
setToolVisibility = (tool: Tool, isDrawable: boolean) => {
this.konva.brush.group.visible(isDrawable && tool === 'brush');
this.konva.eraser.group.visible(isDrawable && tool === 'eraser');
this.konva.colorPicker.group.visible(tool === 'colorPicker');
this.brushToolPreview.setVisibility(isDrawable && tool === 'brush');
this.eraserToolPreview.setVisibility(isDrawable && tool === 'eraser');
this.colorPickerToolPreview.setVisibility(tool === 'colorPicker');
};
syncCursorStyle = () => {
@@ -294,142 +140,6 @@ export class CanvasToolModule extends CanvasModuleABC {
}
};
renderBrushTool = (cursorPos: Coordinate) => {
const toolState = this.manager.stateApi.getToolState();
const brushPreviewFill = this.manager.stateApi.getBrushPreviewFill();
const alignedCursorPos = alignCoordForTool(cursorPos, toolState.brush.width);
const onePixel = this.manager.stage.getScaledPixels(1);
const twoPixels = this.manager.stage.getScaledPixels(2);
const radius = toolState.brush.width / 2;
// The circle is scaled
this.konva.brush.fillCircle.setAttrs({
x: alignedCursorPos.x,
y: alignedCursorPos.y,
radius,
fill: rgbaColorToString(brushPreviewFill),
});
// But the borders are in screen-pixels
this.konva.brush.innerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: radius,
outerRadius: radius + onePixel,
});
this.konva.brush.outerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: radius + onePixel,
outerRadius: radius + twoPixels,
});
};
renderEraserTool = (cursorPos: Coordinate) => {
const toolState = this.manager.stateApi.getToolState();
const alignedCursorPos = alignCoordForTool(cursorPos, toolState.eraser.width);
const onePixel = this.manager.stage.getScaledPixels(1);
const twoPixels = this.manager.stage.getScaledPixels(2);
const radius = toolState.eraser.width / 2;
// The circle is scaled
this.konva.eraser.fillCircle.setAttrs({
x: alignedCursorPos.x,
y: alignedCursorPos.y,
radius,
fill: 'white',
});
// But the borders are in screen-pixels
this.konva.eraser.innerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: radius,
outerRadius: radius + onePixel,
});
this.konva.eraser.outerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: radius + onePixel,
outerRadius: radius + twoPixels,
});
};
renderColorPicker = (cursorPos: Coordinate) => {
const toolState = this.manager.stateApi.getToolState();
const colorUnderCursor = this.manager.stateApi.$colorUnderCursor.get();
const colorPickerInnerRadius = this.manager.stage.getScaledPixels(CanvasToolModule.COLOR_PICKER_RADIUS);
const colorPickerOuterRadius = this.manager.stage.getScaledPixels(
CanvasToolModule.COLOR_PICKER_RADIUS + CanvasToolModule.COLOR_PICKER_THICKNESS
);
const onePixel = this.manager.stage.getScaledPixels(1);
const twoPixels = this.manager.stage.getScaledPixels(2);
this.konva.colorPicker.newColor.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
fill: rgbColorToString(colorUnderCursor),
innerRadius: colorPickerInnerRadius,
outerRadius: colorPickerOuterRadius,
});
this.konva.colorPicker.oldColor.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
fill: rgbColorToString(toolState.fill),
innerRadius: colorPickerInnerRadius,
outerRadius: colorPickerOuterRadius,
});
this.konva.colorPicker.innerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: colorPickerOuterRadius,
outerRadius: colorPickerOuterRadius + onePixel,
});
this.konva.colorPicker.outerBorder.setAttrs({
x: cursorPos.x,
y: cursorPos.y,
innerRadius: colorPickerOuterRadius + onePixel,
outerRadius: colorPickerOuterRadius + twoPixels,
});
const size = this.manager.stage.getScaledPixels(CanvasToolModule.COLOR_PICKER_CROSSHAIR_SIZE);
const space = this.manager.stage.getScaledPixels(CanvasToolModule.COLOR_PICKER_CROSSHAIR_SPACE);
const innerThickness = this.manager.stage.getScaledPixels(CanvasToolModule.COLOR_PICKER_CROSSHAIR_INNER_THICKNESS);
const outerThickness = this.manager.stage.getScaledPixels(CanvasToolModule.COLOR_PICKER_CROSSHAIR_OUTER_THICKNESS);
this.konva.colorPicker.crosshairNorthOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x, cursorPos.y - size, cursorPos.x, cursorPos.y - space],
});
this.konva.colorPicker.crosshairNorthInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x, cursorPos.y - size, cursorPos.x, cursorPos.y - space],
});
this.konva.colorPicker.crosshairEastOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x + space, cursorPos.y, cursorPos.x + size, cursorPos.y],
});
this.konva.colorPicker.crosshairEastInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x + space, cursorPos.y, cursorPos.x + size, cursorPos.y],
});
this.konva.colorPicker.crosshairSouthOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x, cursorPos.y + space, cursorPos.x, cursorPos.y + size],
});
this.konva.colorPicker.crosshairSouthInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x, cursorPos.y + space, cursorPos.x, cursorPos.y + size],
});
this.konva.colorPicker.crosshairWestOuter.setAttrs({
strokeWidth: outerThickness,
points: [cursorPos.x - space, cursorPos.y, cursorPos.x - size, cursorPos.y],
});
this.konva.colorPicker.crosshairWestInner.setAttrs({
strokeWidth: innerThickness,
points: [cursorPos.x - space, cursorPos.y, cursorPos.x - size, cursorPos.y],
});
};
render = () => {
const stage = this.manager.stage;
const renderedEntityCount = this.manager.stateApi.getRenderedEntityCount();
@@ -455,11 +165,11 @@ export class CanvasToolModule extends CanvasModuleABC {
// No need to render the brush preview if the cursor position or color is missing
if (cursorPos && tool === 'brush') {
this.renderBrushTool(cursorPos);
this.brushToolPreview.render();
} else if (cursorPos && tool === 'eraser') {
this.renderEraserTool(cursorPos);
this.eraserToolPreview.render();
} else if (cursorPos && tool === 'colorPicker') {
this.renderColorPicker(cursorPos);
this.colorPickerToolPreview.render();
}
this.setToolVisibility(tool, isDrawable);
@@ -719,7 +429,7 @@ export class CanvasToolModule extends CanvasModuleABC {
if (drawingBuffer) {
if (drawingBuffer.type === 'brush_line') {
const lastPoint = getLastPointOfLine(drawingBuffer.points);
const minDistance = toolState.brush.width * BRUSH_SPACING_TARGET_SCALE;
const minDistance = toolState.brush.width * this.config.BRUSH_SPACING_TARGET_SCALE;
if (lastPoint && validateCandidatePoint(pos, lastPoint, minDistance)) {
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
@@ -756,7 +466,7 @@ export class CanvasToolModule extends CanvasModuleABC {
if (drawingBuffer) {
if (drawingBuffer.type === 'eraser_line') {
const lastPoint = getLastPointOfLine(drawingBuffer.points);
const minDistance = toolState.eraser.width * BRUSH_SPACING_TARGET_SCALE;
const minDistance = toolState.eraser.width * this.config.BRUSH_SPACING_TARGET_SCALE;
if (lastPoint && validateCandidatePoint(pos, lastPoint, minDistance)) {
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);

View File

@@ -1,41 +1,6 @@
/**
* A transparency checker pattern image.
* This is invokeai/frontend/web/public/assets/images/transparent_bg.png as a dataURL
* This is ./transparent_bg.png as a dataURL
*/
export const TRANSPARENCY_CHECKER_PATTERN =
export const TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAEsmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjIwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMjAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMCIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjMwMC8xIgogICB0aWZmOllSZXNvbHV0aW9uPSIzMDAvMSIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB4bXA6TW9kaWZ5RGF0ZT0iMjAyNC0wNC0yM1QwODoyMDo0NysxMDowMCIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNC0yM1QwODoyMDo0NysxMDowMCI+CiAgIDx4bXBNTTpIaXN0b3J5PgogICAgPHJkZjpTZXE+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjgiCiAgICAgIHN0RXZ0OndoZW49IjIwMjQtMDQtMjNUMDg6MjA6NDcrMTA6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/Pn9pdVgAAAGBaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWR3yuDURjHP5uJmKghFy6WxpVpqMWNMgm1tGbKr5vt3S+1d3t73y3JrXKrKHHj1wV/AbfKtVJESq53TdywXs9rakv2nJ7zfM73nOfpnOeAPZJRVMPhAzWb18NTAffC4pK7oYiDTjpw4YgqhjYeCgWpaR8P2Kx457Vq1T73rzXHE4YCtkbhMUXT88LTwsG1vGbxrnC7ko7Ghc+F+3W5oPC9pcfKXLQ4VeYvi/VIeALsbcLuVBXHqlhJ66qwvByPmikov/exXuJMZOfnJPaId2MQZooAbmaYZAI/g4zK7MfLEAOyoka+7yd/lpzkKjJrrKOzSoo0efpFLUj1hMSk6AkZGdat/v/tq5EcHipXdwag/sU033qhYQdK26b5eWyapROoe4arbCU/dwQj76JvVzTPIbRuwsV1RYvtweUWdD1pUT36I9WJ25NJeD2DlkVw3ULTcrlnv/ucPkJkQ77qBvYPoE/Ot658AxagZ8FoS/a7AAAACXBIWXMAAC4jAAAuIwF4pT92AAAAL0lEQVQ4jWM8ffo0A25gYmKCR5YJjxxBMKp5ZGhm/P//Px7pM2fO0MrmUc0jQzMAB2EIhZC3pUYAAAAASUVORK5CYII=';
/**
* The inner border color for the brush preview.
*/
export const BRUSH_BORDER_INNER_COLOR = 'rgba(0,0,0,1)';
/**
* The outer border color for the brush preview.
*/
export const BRUSH_BORDER_OUTER_COLOR = 'rgba(255,255,255,0.8)';
/**
* The target spacing of individual points of brush strokes, as a percentage of the brush size.
*/
export const BRUSH_SPACING_TARGET_SCALE = 0.1;
/**
* Konva wheel zoom exponential scale factor
*/
export const CANVAS_SCALE_BY = 0.999;
/**
* Minimum (furthest-zoomed-out) scale
*/
export const MIN_CANVAS_SCALE = 0.1;
/**
* Maximum (furthest-zoomed-in) scale
*/
export const MAX_CANVAS_SCALE = 20;
/**
* The fine grid size of the canvas
*/
export const CANVAS_GRID_SIZE_FINE = 8;

View File

@@ -1,7 +1,8 @@
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import { CANVAS_GRID_SIZE_FINE } from 'features/controlLayers/konva/constants';
import type { Dimensions } from 'features/controlLayers/store/types';
const CANVAS_GRID_SIZE_FINE = 8;
/**
* Scales the bounding box dimensions to the optimal dimension. The optimal dimensions should be the trained dimension
* for the model. For example, 1024 for SDXL or 512 for SD1.5.

View File

@@ -2,7 +2,7 @@ import { Box, Flex, Image } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { useBoolean } from 'common/hooks/useBoolean';
import { preventDefault } from 'common/util/stopPropagation';
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
import { TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL } from 'features/controlLayers/konva/patterns/transparency-checkerboard-pattern';
import type { Dimensions } from 'features/controlLayers/store/types';
import { ImageComparisonLabel } from 'features/gallery/components/ImageViewer/ImageComparisonLabel';
import { selectComparisonFit } from 'features/gallery/store/gallerySelectors';
@@ -79,7 +79,7 @@ export const ImageComparisonHover = memo(({ firstImage, secondImage, containerDi
left={0}
right={0}
bottom={0}
backgroundImage={TRANSPARENCY_CHECKER_PATTERN}
backgroundImage={TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL}
backgroundRepeat="repeat"
opacity={0.2}
/>

View File

@@ -1,7 +1,7 @@
import { Box, Flex, Icon, Image } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { preventDefault } from 'common/util/stopPropagation';
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
import { TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL } from 'features/controlLayers/konva/patterns/transparency-checkerboard-pattern';
import type { Dimensions } from 'features/controlLayers/store/types';
import { ImageComparisonLabel } from 'features/gallery/components/ImageViewer/ImageComparisonLabel';
import { selectComparisonFit } from 'features/gallery/store/gallerySelectors';
@@ -121,7 +121,7 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage, containerD
left={0}
right={0}
bottom={0}
backgroundImage={TRANSPARENCY_CHECKER_PATTERN}
backgroundImage={TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL}
backgroundRepeat="repeat"
opacity={0.2}
/>