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