diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasScale.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasScale.tsx index 05157b577b..8001d0b403 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasScale.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasScale.tsx @@ -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(() => { { { right={0} bottom={0} left={0} - bgImage={TRANSPARENCY_CHECKER_PATTERN} + bgImage={TRANSPARENCY_CHECKERBOARD_PATTERN_DATAURL} bgSize="5px" opacity={0.1} /> diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasBrushToolPreview.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasBrushToolPreview.ts new file mode 100644 index 0000000000..9dbf69d00e --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasBrushToolPreview.ts @@ -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) { + 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('.') }; + }; +} diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasColorPickerToolPreview.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasColorPickerToolPreview.ts new file mode 100644 index 0000000000..2be0bbc4e8 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasColorPickerToolPreview.ts @@ -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) { + 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('.') }; + }; +} diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserToolPreview.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserToolPreview.ts new file mode 100644 index 0000000000..21bb826bbc --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserToolPreview.ts @@ -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) { + 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('.') }; + }; +} diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStageModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStageModule.ts index 8d4124d1dd..b0bcae7834 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStageModule.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasStageModule.ts @@ -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 + ) { 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); } }; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasToolModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasToolModule.ts index 30297495ae..08af91dc66 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasToolModule.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasToolModule.ts @@ -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) { 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); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/constants.ts b/invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparency-checkerboard-pattern.ts similarity index 73% rename from invokeai/frontend/web/src/features/controlLayers/konva/constants.ts rename to invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparency-checkerboard-pattern.ts index 398ff4b12e..7073a3b916 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/constants.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparency-checkerboard-pattern.ts @@ -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; diff --git a/invokeai/frontend/web/public/assets/images/transparent_bg.png b/invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparent_bg.png similarity index 100% rename from invokeai/frontend/web/public/assets/images/transparent_bg.png rename to invokeai/frontend/web/src/features/controlLayers/konva/patterns/transparent_bg.png diff --git a/invokeai/frontend/web/src/features/controlLayers/util/getScaledBoundingBoxDimensions.ts b/invokeai/frontend/web/src/features/controlLayers/util/getScaledBoundingBoxDimensions.ts index d98d03f33e..3ae8c600fa 100644 --- a/invokeai/frontend/web/src/features/controlLayers/util/getScaledBoundingBoxDimensions.ts +++ b/invokeai/frontend/web/src/features/controlLayers/util/getScaledBoundingBoxDimensions.ts @@ -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. diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx index c9f2d8e034..99e87c3782 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonHover.tsx @@ -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} /> diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx index 49a3095c73..23d6d3e05a 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageComparisonSlider.tsx @@ -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} />