fix(ui): buffered drawing edge cases

This commit is contained in:
psychedelicious 2024-07-05 10:00:47 +10:00
parent 41c195d936
commit d029680ac1
6 changed files with 211 additions and 250 deletions

View File

@ -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(() => {
<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 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>
);
});

View File

@ -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;

View File

@ -22,7 +22,7 @@ export class CanvasInpaintMask {
transformer: Konva.Transformer;
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect>;
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<boolean> {
@ -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({

View File

@ -20,7 +20,7 @@ export class CanvasLayer {
transformer: Konva.Transformer;
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect | CanvasImage>;
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<boolean> {
@ -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;

View File

@ -22,7 +22,7 @@ export class CanvasRegion {
transformer: Konva.Transformer;
objects: Map<string, CanvasBrushLine | CanvasEraserLine | CanvasRect>;
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<boolean> {
@ -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

View File

@ -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<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
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