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 { 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>
|
||||
);
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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({
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user