diff --git a/invokeai/frontend/web/src/features/controlLayers/components/HeadsUpDisplay.tsx b/invokeai/frontend/web/src/features/controlLayers/components/HeadsUpDisplay.tsx
index ba58858304..6e49df0ca7 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/HeadsUpDisplay.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/HeadsUpDisplay.tsx
@@ -1,12 +1,24 @@
import { Box, Flex, Text } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
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 { memo } from 'react';
export const HeadsUpDisplay = memo(() => {
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 document = useAppSelector((s) => s.canvasV2.document);
@@ -25,6 +37,20 @@ export const HeadsUpDisplay = memo(() => {
+
+
+
+
+
);
});
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserLine.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserLine.ts
index f1ce21afd1..76ebf13cfd 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserLine.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserLine.ts
@@ -1,25 +1,8 @@
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
-import { getLayerBboxId, LAYER_BBOX_NAME } from 'features/controlLayers/konva/naming';
-import type { CanvasEntity, EraserLine } from 'features/controlLayers/store/types';
+import type { EraserLine } from 'features/controlLayers/store/types';
import { RGBA_RED } from 'features/controlLayers/store/types';
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 {
id: string;
konvaLineGroup: Konva.Group;
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMask.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMask.ts
index db944ee0ef..c510e3228e 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMask.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasInpaintMask.ts
@@ -22,7 +22,7 @@ export class CanvasInpaintMask {
transformer: Konva.Transformer;
objects: Map;
private drawingBuffer: BrushLine | EraserLine | null;
- private prevInpaintMaskState: InpaintMaskEntity;
+ private inpaintMaskState: InpaintMaskEntity;
constructor(entity: InpaintMaskEntity, manager: CanvasManager) {
this.id = INPAINT_MASK_LAYER_ID;
@@ -60,7 +60,7 @@ export class CanvasInpaintMask {
this.group.add(this.compositingRect);
this.objects = new Map();
this.drawingBuffer = null;
- this.prevInpaintMaskState = entity;
+ this.inpaintMaskState = entity;
}
destroy(): void {
@@ -79,7 +79,7 @@ export class CanvasInpaintMask {
}
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) {
+ this.inpaintMaskState = inpaintMaskState;
+
// Update the layer's position and listening state
this.group.setAttrs({
x: inpaintMaskState.x,
@@ -117,11 +119,18 @@ export class CanvasInpaintMask {
}
for (const obj of inpaintMaskState.objects) {
- didDraw = await this.renderObject(obj);
+ if (await this.renderObject(obj)) {
+ didDraw = true;
+ }
}
- this.updateGroup(didDraw, inpaintMaskState);
- this.prevInpaintMaskState = inpaintMaskState;
+ if (this.drawingBuffer) {
+ if (await this.renderObject(this.drawingBuffer)) {
+ didDraw = true;
+ }
+ }
+
+ this.updateGroup(didDraw);
}
private async renderObject(obj: InpaintMaskEntity['objects'][number], force = false): Promise {
@@ -172,18 +181,15 @@ export class CanvasInpaintMask {
return false;
}
- updateGroup(didDraw: boolean, inpaintMaskState: InpaintMaskEntity) {
- // Only update layer visibility if it has changed.
- if (this.layer.visible() !== inpaintMaskState.isEnabled) {
- this.layer.visible(inpaintMaskState.isEnabled);
- }
+ updateGroup(didDraw: boolean) {
+ this.layer.visible(this.inpaintMaskState.isEnabled);
// 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);
if (didDraw) {
// 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();
this.compositingRect.setAttrs({
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasLayer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasLayer.ts
index f869c009b8..fc074ddc40 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasLayer.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasLayer.ts
@@ -20,7 +20,7 @@ export class CanvasLayer {
transformer: Konva.Transformer;
objects: Map;
private drawingBuffer: BrushLine | EraserLine | null;
- private prevLayerState: LayerEntity;
+ private layerState: LayerEntity;
constructor(entity: LayerEntity, manager: CanvasManager) {
this.id = entity.id;
@@ -59,7 +59,7 @@ export class CanvasLayer {
this.objects = new Map();
this.drawingBuffer = null;
- this.prevLayerState = entity;
+ this.layerState = entity;
}
destroy(): void {
@@ -74,7 +74,7 @@ export class CanvasLayer {
if (obj) {
this.drawingBuffer = obj;
await this.renderObject(this.drawingBuffer, true);
- this.updateGroup(true, this.prevLayerState);
+ this.updateGroup(true);
} else {
this.drawingBuffer = null;
}
@@ -93,6 +93,8 @@ export class CanvasLayer {
}
async render(layerState: LayerEntity) {
+ this.layerState = layerState;
+
// Update the layer's position and listening state
this.group.setAttrs({
x: layerState.x,
@@ -114,24 +116,18 @@ export class CanvasLayer {
}
for (const obj of layerState.objects) {
- didDraw = await this.renderObject(obj);
+ if (await this.renderObject(obj)) {
+ didDraw = true;
+ }
}
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.
- 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;
+ this.updateGroup(didDraw);
}
private async renderObject(obj: LayerEntity['objects'][number], force = false): Promise {
@@ -184,7 +180,7 @@ export class CanvasLayer {
if (!image) {
image = await new CanvasImage(obj, {
onLoad: () => {
- this.updateGroup(true, this.prevLayerState);
+ this.updateGroup(true);
},
});
this.objects.set(image.id, image);
@@ -200,7 +196,10 @@ export class CanvasLayer {
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 selectedTool = this.manager.stateApi.getToolState().selected;
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegion.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegion.ts
index f7a704d4f7..2e735e4b5b 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegion.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRegion.ts
@@ -22,7 +22,7 @@ export class CanvasRegion {
transformer: Konva.Transformer;
objects: Map;
private drawingBuffer: BrushLine | EraserLine | null;
- private prevRegionState: RegionEntity;
+ private regionState: RegionEntity;
constructor(entity: RegionEntity, manager: CanvasManager) {
this.id = entity.id;
@@ -60,7 +60,7 @@ export class CanvasRegion {
this.group.add(this.compositingRect);
this.objects = new Map();
this.drawingBuffer = null;
- this.prevRegionState = entity;
+ this.regionState = entity;
}
destroy(): void {
@@ -78,7 +78,7 @@ export class CanvasRegion {
this.drawingBuffer.color = RGBA_RED;
}
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) {
+ this.regionState = regionState;
+
// Update the layer's position and listening state
this.group.setAttrs({
x: regionState.x,
@@ -116,11 +118,18 @@ export class CanvasRegion {
}
for (const obj of regionState.objects) {
- didDraw = await this.renderObject(obj);
+ if (await this.renderObject(obj)) {
+ didDraw = true;
+ }
}
- this.updateGroup(didDraw, regionState);
- this.prevRegionState = regionState;
+ if (this.drawingBuffer) {
+ if (await this.renderObject(this.drawingBuffer)) {
+ didDraw = true;
+ }
+ }
+
+ this.updateGroup(didDraw);
}
private async renderObject(obj: RegionEntity['objects'][number], force = false): Promise {
@@ -171,18 +180,15 @@ export class CanvasRegion {
return false;
}
- updateGroup(didDraw: boolean, regionState: RegionEntity) {
- // Only update layer visibility if it has changed.
- if (this.layer.visible() !== regionState.isEnabled) {
- this.layer.visible(regionState.isEnabled);
- }
+ updateGroup(didDraw: boolean) {
+ this.layer.visible(this.regionState.isEnabled);
// 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);
if (didDraw) {
// 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();
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
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/events.ts b/invokeai/frontend/web/src/features/controlLayers/konva/events.ts
index 28a0af7801..82ba286a8a 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/events.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/events.ts
@@ -1,14 +1,22 @@
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
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 type Konva from 'konva';
+import type { KonvaEventObject } from 'konva/lib/Node';
import type { Vector2d } from 'konva/lib/types';
import { clamp } from 'lodash-es';
import { v4 as uuidv4 } from 'uuid';
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
@@ -109,9 +117,6 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
getCurrentFill,
setTool,
setToolBuffer,
- getIsDrawing,
- setIsDrawing,
- getIsMouseDown,
setIsMouseDown,
getLastMouseDownPos,
setLastMouseDownPos,
@@ -125,14 +130,36 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
setSpaceKey,
getBbox,
getSettings,
- onBrushLineAdded,
- onEraserLineAdded,
- onPointAddedToLine,
onRectShapeAdded,
onBrushWidthChanged,
onEraserWidthChanged,
} = stateApi;
+ function getIsPrimaryMouseDown(e: KonvaEventObject) {
+ 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
stage.on('mouseenter', () => {
manager.preview.tool.render();
@@ -152,43 +179,32 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
isDrawableEntity(selectedEntity) &&
selectedEntityAdapter &&
isDrawableEntityAdapter(selectedEntityAdapter) &&
- !getSpaceKey()
+ !getSpaceKey() &&
+ getIsPrimaryMouseDown(e)
) {
- setIsDrawing(true);
setLastMouseDownPos(pos);
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) {
const lastAddedPoint = getLastAddedPoint();
// Create a straight line if holding shift
if (lastAddedPoint) {
- onBrushLineAdded(
- {
- id: selectedEntity.id,
- points: [
- lastAddedPoint.x - selectedEntity.x,
- lastAddedPoint.y - selectedEntity.y,
- pos.x - selectedEntity.x,
- pos.y - selectedEntity.y,
- ],
- color: getCurrentFill(),
- width: toolState.brush.width,
- clip,
- },
- selectedEntity.type
- );
+ if (selectedEntityAdapter.getDrawingBuffer()) {
+ selectedEntityAdapter.finalizeDrawingBuffer();
+ }
+ await selectedEntityAdapter.setDrawingBuffer({
+ id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
+ type: 'brush_line',
+ points: [
+ lastAddedPoint.x - selectedEntity.x,
+ lastAddedPoint.y - selectedEntity.y,
+ pos.x - selectedEntity.x,
+ pos.y - selectedEntity.y,
+ ],
+ strokeWidth: toolState.brush.width,
+ color: getCurrentFill(),
+ clip: getClip(selectedEntity),
+ });
}
} else {
if (selectedEntityAdapter.getDrawingBuffer()) {
@@ -205,64 +221,39 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
],
strokeWidth: toolState.brush.width,
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);
}
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) {
// Create a straight line if holding shift
const lastAddedPoint = getLastAddedPoint();
if (lastAddedPoint) {
- onEraserLineAdded(
- {
- id: selectedEntity.id,
- points: [
- lastAddedPoint.x - selectedEntity.x,
- lastAddedPoint.y - selectedEntity.y,
- pos.x - selectedEntity.x,
- pos.y - selectedEntity.y,
- ],
- width: toolState.eraser.width,
- clip,
- },
- selectedEntity.type
- );
+ if (selectedEntityAdapter.getDrawingBuffer()) {
+ selectedEntityAdapter.finalizeDrawingBuffer();
+ }
+ await selectedEntityAdapter.setDrawingBuffer({
+ id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
+ type: 'eraser_line',
+ points: [
+ lastAddedPoint.x - selectedEntity.x,
+ lastAddedPoint.y - selectedEntity.y,
+ pos.x - selectedEntity.x,
+ pos.y - selectedEntity.y,
+ ],
+ strokeWidth: toolState.eraser.width,
+ clip: getClip(selectedEntity),
+ });
}
} else {
if (selectedEntityAdapter.getDrawingBuffer()) {
selectedEntityAdapter.finalizeDrawingBuffer();
}
await selectedEntityAdapter.setDrawingBuffer({
- id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
+ id: getEraserLineId(selectedEntityAdapter.id, uuidv4()),
type: 'eraser_line',
points: [
pos.x - selectedEntity.x,
@@ -271,23 +262,8 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
pos.y - selectedEntity.y,
],
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);
}
@@ -296,7 +272,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
});
//#region mouseup
- stage.on('mouseup', async () => {
+ stage.on('mouseup', async (e) => {
setIsMouseDown(false);
const pos = getLastCursorPos();
const selectedEntity = getSelectedEntity();
@@ -349,7 +325,6 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
}
}
- setIsDrawing(false);
setLastMouseDownPos(null);
}
@@ -357,7 +332,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
});
//#region mousemove
- stage.on('mousemove', async () => {
+ stage.on('mousemove', async (e) => {
const toolState = getToolState();
const pos = updateLastCursorPos(stage, setLastCursorPos);
const selectedEntity = getSelectedEntity();
@@ -370,11 +345,11 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
selectedEntityAdapter &&
isDrawableEntityAdapter(selectedEntityAdapter) &&
!getSpaceKey() &&
- getIsMouseDown()
+ getIsPrimaryMouseDown(e)
) {
if (toolState.selected === 'brush') {
- if (getIsDrawing()) {
- const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
+ const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
+ if (drawingBuffer) {
if (drawingBuffer?.type === 'brush_line') {
const lastAddedPoint = getLastAddedPoint();
const nextPoint = getNextPoint(pos, toolState, lastAddedPoint);
@@ -386,52 +361,31 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
} else {
await selectedEntityAdapter.setDrawingBuffer(null);
}
- // Continue the last line
- // maybeAddNextPoint(
- // selectedEntity,
- // pos,
- // getToolState,
- // getLastAddedPoint,
- // setLastAddedPoint,
- // onPointAddedToLine
- // );
} else {
- const bbox = getBbox();
- const settings = getSettings();
-
- const clip = settings.clipToBbox
- ? {
- x: bbox.x,
- y: bbox.y,
- width: bbox.width,
- height: bbox.height,
- }
- : null;
- // Start a new line
- onBrushLineAdded(
- {
- id: selectedEntity.id,
- 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
- );
+ if (selectedEntityAdapter.getDrawingBuffer()) {
+ selectedEntityAdapter.finalizeDrawingBuffer();
+ }
+ await selectedEntityAdapter.setDrawingBuffer({
+ id: getBrushLineId(selectedEntityAdapter.id, uuidv4()),
+ type: 'brush_line',
+ points: [
+ pos.x - selectedEntity.x,
+ pos.y - selectedEntity.y,
+ pos.x - selectedEntity.x,
+ pos.y - selectedEntity.y,
+ ],
+ strokeWidth: toolState.brush.width,
+ color: getCurrentFill(),
+ clip: getClip(selectedEntity),
+ });
setLastAddedPoint(pos);
- setIsDrawing(true);
}
}
if (toolState.selected === 'eraser') {
- if (getIsDrawing()) {
- const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
- if (drawingBuffer?.type === 'eraser_line') {
+ const drawingBuffer = selectedEntityAdapter.getDrawingBuffer();
+ if (drawingBuffer) {
+ if (drawingBuffer.type === 'eraser_line') {
const lastAddedPoint = getLastAddedPoint();
const nextPoint = getNextPoint(pos, toolState, lastAddedPoint);
if (nextPoint) {
@@ -442,45 +396,23 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
} else {
await selectedEntityAdapter.setDrawingBuffer(null);
}
-
- // Continue the last line
- // maybeAddNextPoint(
- // selectedEntity,
- // pos,
- // getToolState,
- // getLastAddedPoint,
- // setLastAddedPoint,
- // onPointAddedToLine
- // );
} else {
- const bbox = getBbox();
- const settings = getSettings();
-
- const clip = settings.clipToBbox
- ? {
- x: bbox.x,
- y: bbox.y,
- width: bbox.width,
- height: bbox.height,
- }
- : null;
- // Start a new line
- 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
- );
+ if (selectedEntityAdapter.getDrawingBuffer()) {
+ selectedEntityAdapter.finalizeDrawingBuffer();
+ }
+ await selectedEntityAdapter.setDrawingBuffer({
+ id: getEraserLineId(selectedEntityAdapter.id, uuidv4()),
+ type: 'eraser_line',
+ points: [
+ pos.x - selectedEntity.x,
+ pos.y - selectedEntity.y,
+ pos.x - selectedEntity.x,
+ pos.y - selectedEntity.y,
+ ],
+ strokeWidth: toolState.eraser.width,
+ clip: getClip(selectedEntity),
+ });
setLastAddedPoint(pos);
- setIsDrawing(true);
}
}
}
@@ -488,22 +420,32 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
});
//#region mouseleave
- stage.on('mouseleave', () => {
+ stage.on('mouseleave', async (e) => {
const pos = updateLastCursorPos(stage, setLastCursorPos);
- setIsDrawing(false);
setLastCursorPos(null);
setLastMouseDownPos(null);
const selectedEntity = getSelectedEntity();
+ const selectedEntityAdapter = getSelectedEntityAdapter();
const toolState = getToolState();
- if (pos && selectedEntity && isDrawableEntity(selectedEntity) && !getSpaceKey() && getIsMouseDown()) {
- if (getIsMouseDown()) {
- if (toolState.selected === 'brush') {
- onPointAddedToLine({ id: selectedEntity.id, point: [pos.x, pos.y] }, selectedEntity.type);
- }
- if (toolState.selected === 'eraser') {
- onPointAddedToLine({ id: selectedEntity.id, point: [pos.x, pos.y] }, selectedEntity.type);
- }
+ if (
+ pos &&
+ selectedEntity &&
+ isDrawableEntity(selectedEntity) &&
+ selectedEntityAdapter &&
+ isDrawableEntityAdapter(selectedEntityAdapter) &&
+ !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') {
// Cancel shape drawing on escape
- setIsDrawing(false);
setLastMouseDownPos(null);
} else if (e.key === ' ') {
// Select the view tool on space key down