mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): buffered drawing edge cases
This commit is contained in:
parent
41c195d936
commit
d029680ac1
@ -1,12 +1,24 @@
|
|||||||
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { $stageAttrs } from 'features/controlLayers/store/canvasV2Slice';
|
import {
|
||||||
|
$isDrawing,
|
||||||
|
$isMouseDown,
|
||||||
|
$lastAddedPoint,
|
||||||
|
$lastCursorPos,
|
||||||
|
$lastMouseDownPos,
|
||||||
|
$stageAttrs,
|
||||||
|
} from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import { round } from 'lodash-es';
|
import { round } from 'lodash-es';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
export const HeadsUpDisplay = memo(() => {
|
export const HeadsUpDisplay = memo(() => {
|
||||||
const stageAttrs = useStore($stageAttrs);
|
const stageAttrs = useStore($stageAttrs);
|
||||||
|
const cursorPos = useStore($lastCursorPos);
|
||||||
|
const isDrawing = useStore($isDrawing);
|
||||||
|
const isMouseDown = useStore($isMouseDown);
|
||||||
|
const lastMouseDownPos = useStore($lastMouseDownPos);
|
||||||
|
const lastAddedPoint = useStore($lastAddedPoint);
|
||||||
const bbox = useAppSelector((s) => s.canvasV2.bbox);
|
const bbox = useAppSelector((s) => s.canvasV2.bbox);
|
||||||
const document = useAppSelector((s) => s.canvasV2.document);
|
const document = useAppSelector((s) => s.canvasV2.document);
|
||||||
|
|
||||||
@ -25,6 +37,20 @@ export const HeadsUpDisplay = memo(() => {
|
|||||||
<HUDItem label="BBox Height % 8" value={round(bbox.height % 8, 2)} />
|
<HUDItem label="BBox Height % 8" value={round(bbox.height % 8, 2)} />
|
||||||
<HUDItem label="BBox X % 8" value={round(bbox.x % 8, 2)} />
|
<HUDItem label="BBox X % 8" value={round(bbox.x % 8, 2)} />
|
||||||
<HUDItem label="BBox Y % 8" value={round(bbox.y % 8, 2)} />
|
<HUDItem label="BBox Y % 8" value={round(bbox.y % 8, 2)} />
|
||||||
|
<HUDItem
|
||||||
|
label="Cursor Position"
|
||||||
|
value={cursorPos ? `${round(cursorPos.x, 2)}, ${round(cursorPos.y, 2)}` : '?, ?'}
|
||||||
|
/>
|
||||||
|
<HUDItem label="Is Drawing" value={isDrawing ? 'True' : 'False'} />
|
||||||
|
<HUDItem label="Is Mouse Down" value={isMouseDown ? 'True' : 'False'} />
|
||||||
|
<HUDItem
|
||||||
|
label="Last Mouse Down Pos"
|
||||||
|
value={lastMouseDownPos ? `${round(lastMouseDownPos.x, 2)}, ${round(lastMouseDownPos.y, 2)}` : '?, ?'}
|
||||||
|
/>
|
||||||
|
<HUDItem
|
||||||
|
label="Last Added Point"
|
||||||
|
value={lastAddedPoint ? `${round(lastAddedPoint.x, 2)}, ${round(lastAddedPoint.y, 2)}` : '?, ?'}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,25 +1,8 @@
|
|||||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import { getLayerBboxId, LAYER_BBOX_NAME } from 'features/controlLayers/konva/naming';
|
import type { EraserLine } from 'features/controlLayers/store/types';
|
||||||
import type { CanvasEntity, EraserLine } from 'features/controlLayers/store/types';
|
|
||||||
import { RGBA_RED } from 'features/controlLayers/store/types';
|
import { RGBA_RED } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a bounding box rect for a layer.
|
|
||||||
* @param entity The layer state for the layer to create the bounding box for
|
|
||||||
* @param konvaLayer The konva layer to attach the bounding box to
|
|
||||||
*/
|
|
||||||
export const createBboxRect = (entity: CanvasEntity, konvaLayer: Konva.Layer): Konva.Rect => {
|
|
||||||
const rect = new Konva.Rect({
|
|
||||||
id: getLayerBboxId(entity.id),
|
|
||||||
name: LAYER_BBOX_NAME,
|
|
||||||
strokeWidth: 1,
|
|
||||||
visible: false,
|
|
||||||
});
|
|
||||||
konvaLayer.add(rect);
|
|
||||||
return rect;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class CanvasEraserLine {
|
export class CanvasEraserLine {
|
||||||
id: string;
|
id: string;
|
||||||
konvaLineGroup: Konva.Group;
|
konvaLineGroup: Konva.Group;
|
||||||
|
@ -22,7 +22,7 @@ export class CanvasInpaintMask {
|
|||||||
transformer: Konva.Transformer;
|
transformer: Konva.Transformer;
|
||||||
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect>;
|
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect>;
|
||||||
private drawingBuffer: BrushLine | EraserLine | null;
|
private drawingBuffer: BrushLine | EraserLine | null;
|
||||||
private prevInpaintMaskState: InpaintMaskEntity;
|
private inpaintMaskState: InpaintMaskEntity;
|
||||||
|
|
||||||
constructor(entity: InpaintMaskEntity, manager: CanvasManager) {
|
constructor(entity: InpaintMaskEntity, manager: CanvasManager) {
|
||||||
this.id = INPAINT_MASK_LAYER_ID;
|
this.id = INPAINT_MASK_LAYER_ID;
|
||||||
@ -60,7 +60,7 @@ export class CanvasInpaintMask {
|
|||||||
this.group.add(this.compositingRect);
|
this.group.add(this.compositingRect);
|
||||||
this.objects = new Map();
|
this.objects = new Map();
|
||||||
this.drawingBuffer = null;
|
this.drawingBuffer = null;
|
||||||
this.prevInpaintMaskState = entity;
|
this.inpaintMaskState = entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
@ -79,7 +79,7 @@ export class CanvasInpaintMask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.renderObject(this.drawingBuffer, true);
|
await this.renderObject(this.drawingBuffer, true);
|
||||||
this.updateGroup(true, this.prevInpaintMaskState);
|
this.updateGroup(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +96,8 @@ export class CanvasInpaintMask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async render(inpaintMaskState: InpaintMaskEntity) {
|
async render(inpaintMaskState: InpaintMaskEntity) {
|
||||||
|
this.inpaintMaskState = inpaintMaskState;
|
||||||
|
|
||||||
// Update the layer's position and listening state
|
// Update the layer's position and listening state
|
||||||
this.group.setAttrs({
|
this.group.setAttrs({
|
||||||
x: inpaintMaskState.x,
|
x: inpaintMaskState.x,
|
||||||
@ -117,11 +119,18 @@ export class CanvasInpaintMask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const obj of inpaintMaskState.objects) {
|
for (const obj of inpaintMaskState.objects) {
|
||||||
didDraw = await this.renderObject(obj);
|
if (await this.renderObject(obj)) {
|
||||||
|
didDraw = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateGroup(didDraw, inpaintMaskState);
|
if (this.drawingBuffer) {
|
||||||
this.prevInpaintMaskState = inpaintMaskState;
|
if (await this.renderObject(this.drawingBuffer)) {
|
||||||
|
didDraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateGroup(didDraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async renderObject(obj: InpaintMaskEntity['objects'][number], force = false): Promise<boolean> {
|
private async renderObject(obj: InpaintMaskEntity['objects'][number], force = false): Promise<boolean> {
|
||||||
@ -172,18 +181,15 @@ export class CanvasInpaintMask {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGroup(didDraw: boolean, inpaintMaskState: InpaintMaskEntity) {
|
updateGroup(didDraw: boolean) {
|
||||||
// Only update layer visibility if it has changed.
|
this.layer.visible(this.inpaintMaskState.isEnabled);
|
||||||
if (this.layer.visible() !== inpaintMaskState.isEnabled) {
|
|
||||||
this.layer.visible(inpaintMaskState.isEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The user is allowed to reduce mask opacity to 0, but we need the opacity for the compositing rect to work
|
// The user is allowed to reduce mask opacity to 0, but we need the opacity for the compositing rect to work
|
||||||
this.group.opacity(1);
|
this.group.opacity(1);
|
||||||
|
|
||||||
if (didDraw) {
|
if (didDraw) {
|
||||||
// Convert the color to a string, stripping the alpha - the object group will handle opacity.
|
// Convert the color to a string, stripping the alpha - the object group will handle opacity.
|
||||||
const rgbColor = rgbColorToString(inpaintMaskState.fill);
|
const rgbColor = rgbColorToString(this.inpaintMaskState.fill);
|
||||||
const maskOpacity = this.manager.stateApi.getMaskOpacity();
|
const maskOpacity = this.manager.stateApi.getMaskOpacity();
|
||||||
|
|
||||||
this.compositingRect.setAttrs({
|
this.compositingRect.setAttrs({
|
||||||
|
@ -20,7 +20,7 @@ export class CanvasLayer {
|
|||||||
transformer: Konva.Transformer;
|
transformer: Konva.Transformer;
|
||||||
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect | CanvasImage>;
|
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect | CanvasImage>;
|
||||||
private drawingBuffer: BrushLine | EraserLine | null;
|
private drawingBuffer: BrushLine | EraserLine | null;
|
||||||
private prevLayerState: LayerEntity;
|
private layerState: LayerEntity;
|
||||||
|
|
||||||
constructor(entity: LayerEntity, manager: CanvasManager) {
|
constructor(entity: LayerEntity, manager: CanvasManager) {
|
||||||
this.id = entity.id;
|
this.id = entity.id;
|
||||||
@ -59,7 +59,7 @@ export class CanvasLayer {
|
|||||||
|
|
||||||
this.objects = new Map();
|
this.objects = new Map();
|
||||||
this.drawingBuffer = null;
|
this.drawingBuffer = null;
|
||||||
this.prevLayerState = entity;
|
this.layerState = entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
@ -74,7 +74,7 @@ export class CanvasLayer {
|
|||||||
if (obj) {
|
if (obj) {
|
||||||
this.drawingBuffer = obj;
|
this.drawingBuffer = obj;
|
||||||
await this.renderObject(this.drawingBuffer, true);
|
await this.renderObject(this.drawingBuffer, true);
|
||||||
this.updateGroup(true, this.prevLayerState);
|
this.updateGroup(true);
|
||||||
} else {
|
} else {
|
||||||
this.drawingBuffer = null;
|
this.drawingBuffer = null;
|
||||||
}
|
}
|
||||||
@ -93,6 +93,8 @@ export class CanvasLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async render(layerState: LayerEntity) {
|
async render(layerState: LayerEntity) {
|
||||||
|
this.layerState = layerState;
|
||||||
|
|
||||||
// Update the layer's position and listening state
|
// Update the layer's position and listening state
|
||||||
this.group.setAttrs({
|
this.group.setAttrs({
|
||||||
x: layerState.x,
|
x: layerState.x,
|
||||||
@ -114,24 +116,18 @@ export class CanvasLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const obj of layerState.objects) {
|
for (const obj of layerState.objects) {
|
||||||
didDraw = await this.renderObject(obj);
|
if (await this.renderObject(obj)) {
|
||||||
|
didDraw = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.drawingBuffer) {
|
if (this.drawingBuffer) {
|
||||||
didDraw = await this.renderObject(this.drawingBuffer);
|
if (await this.renderObject(this.drawingBuffer)) {
|
||||||
|
didDraw = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only update layer visibility if it has changed.
|
this.updateGroup(didDraw);
|
||||||
if (this.layer.visible() !== layerState.isEnabled) {
|
|
||||||
this.layer.visible(layerState.isEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.group.opacity(layerState.opacity);
|
|
||||||
|
|
||||||
// The layer only listens when using the move tool - otherwise the stage is handling mouse events
|
|
||||||
this.updateGroup(didDraw, this.prevLayerState);
|
|
||||||
|
|
||||||
this.prevLayerState = layerState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async renderObject(obj: LayerEntity['objects'][number], force = false): Promise<boolean> {
|
private async renderObject(obj: LayerEntity['objects'][number], force = false): Promise<boolean> {
|
||||||
@ -184,7 +180,7 @@ export class CanvasLayer {
|
|||||||
if (!image) {
|
if (!image) {
|
||||||
image = await new CanvasImage(obj, {
|
image = await new CanvasImage(obj, {
|
||||||
onLoad: () => {
|
onLoad: () => {
|
||||||
this.updateGroup(true, this.prevLayerState);
|
this.updateGroup(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.objects.set(image.id, image);
|
this.objects.set(image.id, image);
|
||||||
@ -200,7 +196,10 @@ export class CanvasLayer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGroup(didDraw: boolean, _: LayerEntity) {
|
updateGroup(didDraw: boolean) {
|
||||||
|
this.layer.visible(this.layerState.isEnabled);
|
||||||
|
|
||||||
|
this.group.opacity(this.layerState.opacity);
|
||||||
const isSelected = this.manager.stateApi.getIsSelected(this.id);
|
const isSelected = this.manager.stateApi.getIsSelected(this.id);
|
||||||
const selectedTool = this.manager.stateApi.getToolState().selected;
|
const selectedTool = this.manager.stateApi.getToolState().selected;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ export class CanvasRegion {
|
|||||||
transformer: Konva.Transformer;
|
transformer: Konva.Transformer;
|
||||||
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect>;
|
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect>;
|
||||||
private drawingBuffer: BrushLine | EraserLine | null;
|
private drawingBuffer: BrushLine | EraserLine | null;
|
||||||
private prevRegionState: RegionEntity;
|
private regionState: RegionEntity;
|
||||||
|
|
||||||
constructor(entity: RegionEntity, manager: CanvasManager) {
|
constructor(entity: RegionEntity, manager: CanvasManager) {
|
||||||
this.id = entity.id;
|
this.id = entity.id;
|
||||||
@ -60,7 +60,7 @@ export class CanvasRegion {
|
|||||||
this.group.add(this.compositingRect);
|
this.group.add(this.compositingRect);
|
||||||
this.objects = new Map();
|
this.objects = new Map();
|
||||||
this.drawingBuffer = null;
|
this.drawingBuffer = null;
|
||||||
this.prevRegionState = entity;
|
this.regionState = entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
@ -78,7 +78,7 @@ export class CanvasRegion {
|
|||||||
this.drawingBuffer.color = RGBA_RED;
|
this.drawingBuffer.color = RGBA_RED;
|
||||||
}
|
}
|
||||||
await this.renderObject(this.drawingBuffer, true);
|
await this.renderObject(this.drawingBuffer, true);
|
||||||
this.updateGroup(true, this.prevRegionState);
|
this.updateGroup(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,6 +95,8 @@ export class CanvasRegion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async render(regionState: RegionEntity) {
|
async render(regionState: RegionEntity) {
|
||||||
|
this.regionState = regionState;
|
||||||
|
|
||||||
// Update the layer's position and listening state
|
// Update the layer's position and listening state
|
||||||
this.group.setAttrs({
|
this.group.setAttrs({
|
||||||
x: regionState.x,
|
x: regionState.x,
|
||||||
@ -116,11 +118,18 @@ export class CanvasRegion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const obj of regionState.objects) {
|
for (const obj of regionState.objects) {
|
||||||
didDraw = await this.renderObject(obj);
|
if (await this.renderObject(obj)) {
|
||||||
|
didDraw = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateGroup(didDraw, regionState);
|
if (this.drawingBuffer) {
|
||||||
this.prevRegionState = regionState;
|
if (await this.renderObject(this.drawingBuffer)) {
|
||||||
|
didDraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateGroup(didDraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async renderObject(obj: RegionEntity['objects'][number], force = false): Promise<boolean> {
|
private async renderObject(obj: RegionEntity['objects'][number], force = false): Promise<boolean> {
|
||||||
@ -171,18 +180,15 @@ export class CanvasRegion {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGroup(didDraw: boolean, regionState: RegionEntity) {
|
updateGroup(didDraw: boolean) {
|
||||||
// Only update layer visibility if it has changed.
|
this.layer.visible(this.regionState.isEnabled);
|
||||||
if (this.layer.visible() !== regionState.isEnabled) {
|
|
||||||
this.layer.visible(regionState.isEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The user is allowed to reduce mask opacity to 0, but we need the opacity for the compositing rect to work
|
// The user is allowed to reduce mask opacity to 0, but we need the opacity for the compositing rect to work
|
||||||
this.group.opacity(1);
|
this.group.opacity(1);
|
||||||
|
|
||||||
if (didDraw) {
|
if (didDraw) {
|
||||||
// Convert the color to a string, stripping the alpha - the object group will handle opacity.
|
// Convert the color to a string, stripping the alpha - the object group will handle opacity.
|
||||||
const rgbColor = rgbColorToString(regionState.fill);
|
const rgbColor = rgbColorToString(this.regionState.fill);
|
||||||
const maskOpacity = this.manager.stateApi.getMaskOpacity();
|
const maskOpacity = this.manager.stateApi.getMaskOpacity();
|
||||||
this.compositingRect.setAttrs({
|
this.compositingRect.setAttrs({
|
||||||
// The rect should be the size of the layer - use the fast method if we don't have a pixel-perfect bbox already
|
// The rect should be the size of the layer - use the fast method if we don't have a pixel-perfect bbox already
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import { getScaledCursorPosition } from 'features/controlLayers/konva/util';
|
import { getScaledCursorPosition } from 'features/controlLayers/konva/util';
|
||||||
import type { CanvasEntity, CanvasV2State, Position } from 'features/controlLayers/store/types';
|
import type {
|
||||||
|
CanvasEntity,
|
||||||
|
CanvasV2State,
|
||||||
|
InpaintMaskEntity,
|
||||||
|
LayerEntity,
|
||||||
|
Position,
|
||||||
|
RegionEntity,
|
||||||
|
} from 'features/controlLayers/store/types';
|
||||||
import { isDrawableEntity, isDrawableEntityAdapter } from 'features/controlLayers/store/types';
|
import { isDrawableEntity, isDrawableEntityAdapter } from 'features/controlLayers/store/types';
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
|
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import type { Vector2d } from 'konva/lib/types';
|
import type { Vector2d } from 'konva/lib/types';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { BRUSH_SPACING_TARGET_SCALE, CANVAS_SCALE_BY, MAX_CANVAS_SCALE, MIN_CANVAS_SCALE } from './constants';
|
import { BRUSH_SPACING_TARGET_SCALE, CANVAS_SCALE_BY, MAX_CANVAS_SCALE, MIN_CANVAS_SCALE } from './constants';
|
||||||
import { getBrushLineId } from './naming';
|
import { getBrushLineId, getEraserLineId } from './naming';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the last cursor position atom with the current cursor position, returning the new position or `null` if the
|
* Updates the last cursor position atom with the current cursor position, returning the new position or `null` if the
|
||||||
@ -109,9 +117,6 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
getCurrentFill,
|
getCurrentFill,
|
||||||
setTool,
|
setTool,
|
||||||
setToolBuffer,
|
setToolBuffer,
|
||||||
getIsDrawing,
|
|
||||||
setIsDrawing,
|
|
||||||
getIsMouseDown,
|
|
||||||
setIsMouseDown,
|
setIsMouseDown,
|
||||||
getLastMouseDownPos,
|
getLastMouseDownPos,
|
||||||
setLastMouseDownPos,
|
setLastMouseDownPos,
|
||||||
@ -125,14 +130,36 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
setSpaceKey,
|
setSpaceKey,
|
||||||
getBbox,
|
getBbox,
|
||||||
getSettings,
|
getSettings,
|
||||||
onBrushLineAdded,
|
|
||||||
onEraserLineAdded,
|
|
||||||
onPointAddedToLine,
|
|
||||||
onRectShapeAdded,
|
onRectShapeAdded,
|
||||||
onBrushWidthChanged,
|
onBrushWidthChanged,
|
||||||
onEraserWidthChanged,
|
onEraserWidthChanged,
|
||||||
} = stateApi;
|
} = stateApi;
|
||||||
|
|
||||||
|
function getIsPrimaryMouseDown(e: KonvaEventObject<MouseEvent>) {
|
||||||
|
return e.evt.buttons === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClip(entity: RegionEntity | LayerEntity | InpaintMaskEntity) {
|
||||||
|
const settings = getSettings();
|
||||||
|
const bbox = getBbox();
|
||||||
|
|
||||||
|
if (settings.clipToBbox) {
|
||||||
|
return {
|
||||||
|
x: bbox.x - entity.x,
|
||||||
|
y: bbox.y - entity.y,
|
||||||
|
width: bbox.width,
|
||||||
|
height: bbox.height,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
x: -stage.x() / stage.scaleX() - entity.x,
|
||||||
|
y: -stage.y() / stage.scaleY() - entity.y,
|
||||||
|
width: stage.width() / stage.scaleX(),
|
||||||
|
height: stage.height() / stage.scaleY(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//#region mouseenter
|
//#region mouseenter
|
||||||
stage.on('mouseenter', () => {
|
stage.on('mouseenter', () => {
|
||||||
manager.preview.tool.render();
|
manager.preview.tool.render();
|
||||||
@ -152,43 +179,32 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
isDrawableEntity(selectedEntity) &&
|
isDrawableEntity(selectedEntity) &&
|
||||||
selectedEntityAdapter &&
|
selectedEntityAdapter &&
|
||||||
isDrawableEntityAdapter(selectedEntityAdapter) &&
|
isDrawableEntityAdapter(selectedEntityAdapter) &&
|
||||||
!getSpaceKey()
|
!getSpaceKey() &&
|
||||||
|
getIsPrimaryMouseDown(e)
|
||||||
) {
|
) {
|
||||||
setIsDrawing(true);
|
|
||||||
setLastMouseDownPos(pos);
|
setLastMouseDownPos(pos);
|
||||||
|
|
||||||
if (toolState.selected === 'brush') {
|
if (toolState.selected === 'brush') {
|
||||||
const bbox = getBbox();
|
|
||||||
const settings = getSettings();
|
|
||||||
|
|
||||||
const clip = settings.clipToBbox
|
|
||||||
? {
|
|
||||||
x: bbox.x,
|
|
||||||
y: bbox.y,
|
|
||||||
width: bbox.width,
|
|
||||||
height: bbox.height,
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (e.evt.shiftKey) {
|
if (e.evt.shiftKey) {
|
||||||
const lastAddedPoint = getLastAddedPoint();
|
const lastAddedPoint = getLastAddedPoint();
|
||||||
// Create a straight line if holding shift
|
// Create a straight line if holding shift
|
||||||
if (lastAddedPoint) {
|
if (lastAddedPoint) {
|
||||||
onBrushLineAdded(
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
{
|
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
id: selectedEntity.id,
|
}
|
||||||
points: [
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
lastAddedPoint.x - selectedEntity.x,
|
id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
|
||||||
lastAddedPoint.y - selectedEntity.y,
|
type: 'brush_line',
|
||||||
pos.x - selectedEntity.x,
|
points: [
|
||||||
pos.y - selectedEntity.y,
|
lastAddedPoint.x - selectedEntity.x,
|
||||||
],
|
lastAddedPoint.y - selectedEntity.y,
|
||||||
color: getCurrentFill(),
|
pos.x - selectedEntity.x,
|
||||||
width: toolState.brush.width,
|
pos.y - selectedEntity.y,
|
||||||
clip,
|
],
|
||||||
},
|
strokeWidth: toolState.brush.width,
|
||||||
selectedEntity.type
|
color: getCurrentFill(),
|
||||||
);
|
clip: getClip(selectedEntity),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (selectedEntityAdapter.getDrawingBuffer()) {
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
@ -205,64 +221,39 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
],
|
],
|
||||||
strokeWidth: toolState.brush.width,
|
strokeWidth: toolState.brush.width,
|
||||||
color: getCurrentFill(),
|
color: getCurrentFill(),
|
||||||
clip,
|
clip: getClip(selectedEntity),
|
||||||
});
|
});
|
||||||
// onBrushLineAdded(
|
|
||||||
// {
|
|
||||||
// id: selectedEntity.id,
|
|
||||||
// points: [
|
|
||||||
// pos.x - selectedEntity.x,
|
|
||||||
// pos.y - selectedEntity.y,
|
|
||||||
// pos.x - selectedEntity.x,
|
|
||||||
// pos.y - selectedEntity.y,
|
|
||||||
// ],
|
|
||||||
// color: getCurrentFill(),
|
|
||||||
// width: toolState.brush.width,
|
|
||||||
// clip,
|
|
||||||
// },
|
|
||||||
// selectedEntity.type
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'eraser') {
|
if (toolState.selected === 'eraser') {
|
||||||
const bbox = getBbox();
|
|
||||||
const settings = getSettings();
|
|
||||||
|
|
||||||
const clip = settings.clipToBbox
|
|
||||||
? {
|
|
||||||
x: bbox.x,
|
|
||||||
y: bbox.y,
|
|
||||||
width: bbox.width,
|
|
||||||
height: bbox.height,
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
if (e.evt.shiftKey) {
|
if (e.evt.shiftKey) {
|
||||||
// Create a straight line if holding shift
|
// Create a straight line if holding shift
|
||||||
const lastAddedPoint = getLastAddedPoint();
|
const lastAddedPoint = getLastAddedPoint();
|
||||||
if (lastAddedPoint) {
|
if (lastAddedPoint) {
|
||||||
onEraserLineAdded(
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
{
|
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
id: selectedEntity.id,
|
}
|
||||||
points: [
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
lastAddedPoint.x - selectedEntity.x,
|
id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
|
||||||
lastAddedPoint.y - selectedEntity.y,
|
type: 'eraser_line',
|
||||||
pos.x - selectedEntity.x,
|
points: [
|
||||||
pos.y - selectedEntity.y,
|
lastAddedPoint.x - selectedEntity.x,
|
||||||
],
|
lastAddedPoint.y - selectedEntity.y,
|
||||||
width: toolState.eraser.width,
|
pos.x - selectedEntity.x,
|
||||||
clip,
|
pos.y - selectedEntity.y,
|
||||||
},
|
],
|
||||||
selectedEntity.type
|
strokeWidth: toolState.eraser.width,
|
||||||
);
|
clip: getClip(selectedEntity),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (selectedEntityAdapter.getDrawingBuffer()) {
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
selectedEntityAdapter.finalizeDrawingBuffer();
|
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
}
|
}
|
||||||
await selectedEntityAdapter.setDrawingBuffer({
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
|
id: getEraserLineId(selectedEntityAdapter.id, uuidv4()),
|
||||||
type: 'eraser_line',
|
type: 'eraser_line',
|
||||||
points: [
|
points: [
|
||||||
pos.x - selectedEntity.x,
|
pos.x - selectedEntity.x,
|
||||||
@ -271,23 +262,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
pos.y - selectedEntity.y,
|
pos.y - selectedEntity.y,
|
||||||
],
|
],
|
||||||
strokeWidth: toolState.eraser.width,
|
strokeWidth: toolState.eraser.width,
|
||||||
clip,
|
clip: getClip(selectedEntity),
|
||||||
});
|
});
|
||||||
|
|
||||||
// onEraserLineAdded(
|
|
||||||
// {
|
|
||||||
// id: selectedEntity.id,
|
|
||||||
// points: [
|
|
||||||
// pos.x - selectedEntity.x,
|
|
||||||
// pos.y - selectedEntity.y,
|
|
||||||
// pos.x - selectedEntity.x,
|
|
||||||
// pos.y - selectedEntity.y,
|
|
||||||
// ],
|
|
||||||
// width: toolState.eraser.width,
|
|
||||||
// clip,
|
|
||||||
// },
|
|
||||||
// selectedEntity.type
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(pos);
|
||||||
}
|
}
|
||||||
@ -296,7 +272,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//#region mouseup
|
//#region mouseup
|
||||||
stage.on('mouseup', async () => {
|
stage.on('mouseup', async (e) => {
|
||||||
setIsMouseDown(false);
|
setIsMouseDown(false);
|
||||||
const pos = getLastCursorPos();
|
const pos = getLastCursorPos();
|
||||||
const selectedEntity = getSelectedEntity();
|
const selectedEntity = getSelectedEntity();
|
||||||
@ -349,7 +325,6 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsDrawing(false);
|
|
||||||
setLastMouseDownPos(null);
|
setLastMouseDownPos(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,7 +332,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//#region mousemove
|
//#region mousemove
|
||||||
stage.on('mousemove', async () => {
|
stage.on('mousemove', async (e) => {
|
||||||
const toolState = getToolState();
|
const toolState = getToolState();
|
||||||
const pos = updateLastCursorPos(stage, setLastCursorPos);
|
const pos = updateLastCursorPos(stage, setLastCursorPos);
|
||||||
const selectedEntity = getSelectedEntity();
|
const selectedEntity = getSelectedEntity();
|
||||||
@ -370,11 +345,11 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
selectedEntityAdapter &&
|
selectedEntityAdapter &&
|
||||||
isDrawableEntityAdapter(selectedEntityAdapter) &&
|
isDrawableEntityAdapter(selectedEntityAdapter) &&
|
||||||
!getSpaceKey() &&
|
!getSpaceKey() &&
|
||||||
getIsMouseDown()
|
getIsPrimaryMouseDown(e)
|
||||||
) {
|
) {
|
||||||
if (toolState.selected === 'brush') {
|
if (toolState.selected === 'brush') {
|
||||||
if (getIsDrawing()) {
|
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
||||||
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
if (drawingBuffer) {
|
||||||
if (drawingBuffer?.type === 'brush_line') {
|
if (drawingBuffer?.type === 'brush_line') {
|
||||||
const lastAddedPoint = getLastAddedPoint();
|
const lastAddedPoint = getLastAddedPoint();
|
||||||
const nextPoint = getNextPoint(pos, toolState, lastAddedPoint);
|
const nextPoint = getNextPoint(pos, toolState, lastAddedPoint);
|
||||||
@ -386,52 +361,31 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
} else {
|
} else {
|
||||||
await selectedEntityAdapter.setDrawingBuffer(null);
|
await selectedEntityAdapter.setDrawingBuffer(null);
|
||||||
}
|
}
|
||||||
// Continue the last line
|
|
||||||
// maybeAddNextPoint(
|
|
||||||
// selectedEntity,
|
|
||||||
// pos,
|
|
||||||
// getToolState,
|
|
||||||
// getLastAddedPoint,
|
|
||||||
// setLastAddedPoint,
|
|
||||||
// onPointAddedToLine
|
|
||||||
// );
|
|
||||||
} else {
|
} else {
|
||||||
const bbox = getBbox();
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
const settings = getSettings();
|
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
|
}
|
||||||
const clip = settings.clipToBbox
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
? {
|
id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
|
||||||
x: bbox.x,
|
type: 'brush_line',
|
||||||
y: bbox.y,
|
points: [
|
||||||
width: bbox.width,
|
pos.x - selectedEntity.x,
|
||||||
height: bbox.height,
|
pos.y - selectedEntity.y,
|
||||||
}
|
pos.x - selectedEntity.x,
|
||||||
: null;
|
pos.y - selectedEntity.y,
|
||||||
// Start a new line
|
],
|
||||||
onBrushLineAdded(
|
strokeWidth: toolState.brush.width,
|
||||||
{
|
color: getCurrentFill(),
|
||||||
id: selectedEntity.id,
|
clip: getClip(selectedEntity),
|
||||||
points: [
|
});
|
||||||
pos.x - selectedEntity.x,
|
|
||||||
pos.y - selectedEntity.y,
|
|
||||||
pos.x - selectedEntity.x,
|
|
||||||
pos.y - selectedEntity.y,
|
|
||||||
],
|
|
||||||
width: toolState.brush.width,
|
|
||||||
color: getCurrentFill(),
|
|
||||||
clip,
|
|
||||||
},
|
|
||||||
selectedEntity.type
|
|
||||||
);
|
|
||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(pos);
|
||||||
setIsDrawing(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'eraser') {
|
if (toolState.selected === 'eraser') {
|
||||||
if (getIsDrawing()) {
|
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
||||||
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
if (drawingBuffer) {
|
||||||
if (drawingBuffer?.type === 'eraser_line') {
|
if (drawingBuffer.type === 'eraser_line') {
|
||||||
const lastAddedPoint = getLastAddedPoint();
|
const lastAddedPoint = getLastAddedPoint();
|
||||||
const nextPoint = getNextPoint(pos, toolState, lastAddedPoint);
|
const nextPoint = getNextPoint(pos, toolState, lastAddedPoint);
|
||||||
if (nextPoint) {
|
if (nextPoint) {
|
||||||
@ -442,45 +396,23 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
} else {
|
} else {
|
||||||
await selectedEntityAdapter.setDrawingBuffer(null);
|
await selectedEntityAdapter.setDrawingBuffer(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue the last line
|
|
||||||
// maybeAddNextPoint(
|
|
||||||
// selectedEntity,
|
|
||||||
// pos,
|
|
||||||
// getToolState,
|
|
||||||
// getLastAddedPoint,
|
|
||||||
// setLastAddedPoint,
|
|
||||||
// onPointAddedToLine
|
|
||||||
// );
|
|
||||||
} else {
|
} else {
|
||||||
const bbox = getBbox();
|
if (selectedEntityAdapter.getDrawingBuffer()) {
|
||||||
const settings = getSettings();
|
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
|
}
|
||||||
const clip = settings.clipToBbox
|
await selectedEntityAdapter.setDrawingBuffer({
|
||||||
? {
|
id: getEraserLineId(selectedEntityAdapter.id, uuidv4()),
|
||||||
x: bbox.x,
|
type: 'eraser_line',
|
||||||
y: bbox.y,
|
points: [
|
||||||
width: bbox.width,
|
pos.x - selectedEntity.x,
|
||||||
height: bbox.height,
|
pos.y - selectedEntity.y,
|
||||||
}
|
pos.x - selectedEntity.x,
|
||||||
: null;
|
pos.y - selectedEntity.y,
|
||||||
// Start a new line
|
],
|
||||||
onEraserLineAdded(
|
strokeWidth: toolState.eraser.width,
|
||||||
{
|
clip: getClip(selectedEntity),
|
||||||
id: selectedEntity.id,
|
});
|
||||||
points: [
|
|
||||||
pos.x - selectedEntity.x,
|
|
||||||
pos.y - selectedEntity.y,
|
|
||||||
pos.x - selectedEntity.x,
|
|
||||||
pos.y - selectedEntity.y,
|
|
||||||
],
|
|
||||||
width: toolState.eraser.width,
|
|
||||||
clip,
|
|
||||||
},
|
|
||||||
selectedEntity.type
|
|
||||||
);
|
|
||||||
setLastAddedPoint(pos);
|
setLastAddedPoint(pos);
|
||||||
setIsDrawing(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -488,22 +420,32 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//#region mouseleave
|
//#region mouseleave
|
||||||
stage.on('mouseleave', () => {
|
stage.on('mouseleave', async (e) => {
|
||||||
const pos = updateLastCursorPos(stage, setLastCursorPos);
|
const pos = updateLastCursorPos(stage, setLastCursorPos);
|
||||||
setIsDrawing(false);
|
|
||||||
setLastCursorPos(null);
|
setLastCursorPos(null);
|
||||||
setLastMouseDownPos(null);
|
setLastMouseDownPos(null);
|
||||||
const selectedEntity = getSelectedEntity();
|
const selectedEntity = getSelectedEntity();
|
||||||
|
const selectedEntityAdapter = getSelectedEntityAdapter();
|
||||||
const toolState = getToolState();
|
const toolState = getToolState();
|
||||||
|
|
||||||
if (pos && selectedEntity && isDrawableEntity(selectedEntity) && !getSpaceKey() && getIsMouseDown()) {
|
if (
|
||||||
if (getIsMouseDown()) {
|
pos &&
|
||||||
if (toolState.selected === 'brush') {
|
selectedEntity &&
|
||||||
onPointAddedToLine({ id: selectedEntity.id, point: [pos.x, pos.y] }, selectedEntity.type);
|
isDrawableEntity(selectedEntity) &&
|
||||||
}
|
selectedEntityAdapter &&
|
||||||
if (toolState.selected === 'eraser') {
|
isDrawableEntityAdapter(selectedEntityAdapter) &&
|
||||||
onPointAddedToLine({ id: selectedEntity.id, point: [pos.x, pos.y] }, selectedEntity.type);
|
!getSpaceKey() &&
|
||||||
}
|
getIsPrimaryMouseDown(e)
|
||||||
|
) {
|
||||||
|
const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
|
||||||
|
if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') {
|
||||||
|
drawingBuffer.points.push(pos.x - selectedEntity.x, pos.y - selectedEntity.y);
|
||||||
|
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||||
|
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
|
} else if (toolState.selected === 'eraser' && drawingBuffer?.type === 'eraser_line') {
|
||||||
|
drawingBuffer.points.push(pos.x - selectedEntity.x, pos.y - selectedEntity.y);
|
||||||
|
await selectedEntityAdapter.setDrawingBuffer(drawingBuffer);
|
||||||
|
selectedEntityAdapter.finalizeDrawingBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,7 +534,6 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
}
|
}
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
// Cancel shape drawing on escape
|
// Cancel shape drawing on escape
|
||||||
setIsDrawing(false);
|
|
||||||
setLastMouseDownPos(null);
|
setLastMouseDownPos(null);
|
||||||
} else if (e.key === ' ') {
|
} else if (e.key === ' ') {
|
||||||
// Select the view tool on space key down
|
// Select the view tool on space key down
|
||||||
|
Loading…
Reference in New Issue
Block a user