diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasBrushLine.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasBrushLine.ts index 4cb6ca52b0..270459b6ce 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasBrushLine.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasBrushLine.ts @@ -23,7 +23,7 @@ export class CanvasBrushLineRenderer { }; constructor(state: CanvasBrushLineState, parent: CanvasObjectRenderer) { - const { id, strokeWidth, clip, color, points } = state; + const { id, clip } = state; this.id = id; this.parent = parent; this.manager = parent.manager; @@ -42,14 +42,10 @@ export class CanvasBrushLineRenderer { name: `${this.type}:line`, listening: false, shadowForStrokeEnabled: false, - strokeWidth, tension: 0.3, lineCap: 'round', lineJoin: 'round', globalCompositeOperation: 'source-over', - stroke: rgbaColorToString(color), - // A line with only one point will not be rendered, so we duplicate the points to make it visible - points: points.length === 2 ? [...points, ...points] : points, }), }; this.konva.group.add(this.konva.line); @@ -59,12 +55,11 @@ export class CanvasBrushLineRenderer { update(state: CanvasBrushLineState, force = false): boolean { if (force || this.state !== state) { this.log.trace({ state }, 'Updating brush line'); - const { points, color, clip, strokeWidth } = state; + const { points, color, strokeWidth } = state; this.konva.line.setAttrs({ // A line with only one point will not be rendered, so we duplicate the points to make it visible points: points.length === 2 ? [...points, ...points] : points, stroke: rgbaColorToString(color), - clip, strokeWidth, }); this.state = state; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserLine.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserLine.ts index 4417348557..6ef2082007 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserLine.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasEraserLine.ts @@ -1,10 +1,8 @@ import type { JSONObject } from 'common/types'; -import { rgbaColorToString } from 'common/util/colorCodeTransformers'; import { deepClone } from 'common/util/deepClone'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer'; import type { CanvasEraserLineState } from 'features/controlLayers/store/types'; -import { RGBA_RED } from 'features/controlLayers/store/types'; import Konva from 'konva'; import type { Logger } from 'roarr'; @@ -24,7 +22,7 @@ export class CanvasEraserLineRenderer { }; constructor(state: CanvasEraserLineState, parent: CanvasObjectRenderer) { - const { id, strokeWidth, clip, points } = state; + const { id, clip } = state; this.id = id; this.parent = parent; this.manager = parent.manager; @@ -43,14 +41,10 @@ export class CanvasEraserLineRenderer { name: `${this.type}:line`, listening: false, shadowForStrokeEnabled: false, - strokeWidth, tension: 0.3, lineCap: 'round', lineJoin: 'round', globalCompositeOperation: 'destination-out', - stroke: rgbaColorToString(RGBA_RED), - // A line with only one point will not be rendered, so we duplicate the points to make it visible - points: points.length === 2 ? [...points, ...points] : points, }), }; this.konva.group.add(this.konva.line); @@ -60,11 +54,10 @@ export class CanvasEraserLineRenderer { update(state: CanvasEraserLineState, force = false): boolean { if (force || this.state !== state) { this.log.trace({ state }, 'Updating eraser line'); - const { points, clip, strokeWidth } = state; + const { points, strokeWidth } = state; this.konva.line.setAttrs({ // A line with only one point will not be rendered, so we duplicate the points to make it visible points: points.length === 2 ? [...points, ...points] : points, - clip, strokeWidth, }); this.state = state; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObjectRenderer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObjectRenderer.ts index 4ee639315c..5472c04bd5 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObjectRenderer.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasObjectRenderer.ts @@ -1,6 +1,5 @@ import type { JSONObject } from 'common/types'; import { rgbColorToString } from 'common/util/colorCodeTransformers'; -import { deepClone } from 'common/util/deepClone'; import { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine'; import { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine'; import { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage'; @@ -67,8 +66,14 @@ export class CanvasObjectRenderer { * drawn in real-time, such as brush lines. The buffer object state only exists in this renderer and is not part of * the application state until it is committed. */ - buffer: AnyObjectState | null = null; + bufferState: AnyObjectState | null = null; + /** + * The object renderer for the buffer object state. It is created when the buffer object state is set and destroyed + * when the buffer object state is cleared. This is separate from the other object renderers to allow the buffer to + * be rendered separately. + */ + bufferRenderer: AnyObjectRenderer | null = null; /** * A map of object renderers, keyed by their ID. */ @@ -82,6 +87,10 @@ export class CanvasObjectRenderer { * A Konva Group that holds all the object renderers. */ objectGroup: Konva.Group; + /** + * A Konva Group that holds the buffer object renderer. + */ + bufferGroup: Konva.Group; /** * The compositing rect is used to draw the inpaint mask as a single shape with a given opacity. * @@ -113,10 +122,12 @@ export class CanvasObjectRenderer { this.konva = { objectGroup: new Konva.Group({ name: `${this.type}:object_group`, listening: false }), + bufferGroup: new Konva.Group({ name: `${this.type}:buffer_group`, listening: false }), compositing: null, }; this.parent.konva.layer.add(this.konva.objectGroup); + this.parent.konva.layer.add(this.konva.bufferGroup); if (this.parent.state.type === 'inpaint_mask' || this.parent.state.type === 'regional_guidance') { const rect = new Konva.Rect({ @@ -148,7 +159,6 @@ export class CanvasObjectRenderer { this.subscriptions.add( this.manager.stateApi.$stageAttrs.listen(() => { if (this.konva.compositing && this.parent.type === 'mask_adapter') { - this.updateCompositingRectFill(this.parent.state.fill); this.updateCompositingRectSize(); } }) @@ -166,7 +176,7 @@ export class CanvasObjectRenderer { const objectIds = objectStates.map((objectState) => objectState.id); for (const renderer of this.renderers.values()) { - if (!objectIds.includes(renderer.id) && renderer.id !== this.buffer?.id) { + if (!objectIds.includes(renderer.id)) { this.renderers.delete(renderer.id); renderer.destroy(); didRender = true; @@ -177,10 +187,6 @@ export class CanvasObjectRenderer { didRender = (await this.renderObject(objectState)) || didRender; } - if (this.buffer) { - didRender = (await this.renderObject(this.buffer)) || didRender; - } - this.syncCache(didRender); return didRender; @@ -236,6 +242,7 @@ export class CanvasObjectRenderer { this.konva.compositing.group.opacity(opacity); } else { this.konva.objectGroup.opacity(opacity); + this.konva.bufferGroup.opacity(opacity); } }; @@ -253,10 +260,10 @@ export class CanvasObjectRenderer { let renderer = this.renderers.get(objectState.id); - const isFirstRender = renderer === undefined; + const isFirstRender = !renderer; if (objectState.type === 'brush_line') { - assert(renderer instanceof CanvasBrushLineRenderer || renderer === undefined); + assert(renderer instanceof CanvasBrushLineRenderer || !renderer); if (!renderer) { renderer = new CanvasBrushLineRenderer(objectState, this); @@ -266,7 +273,7 @@ export class CanvasObjectRenderer { didRender = renderer.update(objectState, force || isFirstRender); } else if (objectState.type === 'eraser_line') { - assert(renderer instanceof CanvasEraserLineRenderer || renderer === undefined); + assert(renderer instanceof CanvasEraserLineRenderer || !renderer); if (!renderer) { renderer = new CanvasEraserLineRenderer(objectState, this); @@ -276,7 +283,7 @@ export class CanvasObjectRenderer { didRender = renderer.update(objectState, force || isFirstRender); } else if (objectState.type === 'rect') { - assert(renderer instanceof CanvasRectRenderer || renderer === undefined); + assert(renderer instanceof CanvasRectRenderer || !renderer); if (!renderer) { renderer = new CanvasRectRenderer(objectState, this); @@ -286,7 +293,7 @@ export class CanvasObjectRenderer { didRender = renderer.update(objectState, force || isFirstRender); } else if (objectState.type === 'image') { - assert(renderer instanceof CanvasImageRenderer || renderer === undefined); + assert(renderer instanceof CanvasImageRenderer || !renderer); if (!renderer) { renderer = new CanvasImageRenderer(objectState, this); @@ -296,6 +303,9 @@ export class CanvasObjectRenderer { didRender = await renderer.update(objectState, force || isFirstRender); } + // We should always clear the buffer when rendering a "real" object + this.clearBuffer(); + if (didRender && this.konva.objectGroup.isCached()) { this.konva.objectGroup.clearCache(); } @@ -303,12 +313,64 @@ export class CanvasObjectRenderer { return didRender; }; + /** + * Renders the buffer object. If the buffer renderer does not exist, it will be created and its Konva group added to the + * parent entity's buffer object group. + * @returns A promise that resolves to a boolean, indicating if the object was rendered. + */ + renderBufferObject = async (): Promise => { + let didRender = false; + + if (!this.bufferState) { + return false; + } + + if (this.bufferState.type === 'brush_line') { + assert(this.bufferRenderer instanceof CanvasBrushLineRenderer || !this.bufferRenderer); + + if (!this.bufferRenderer) { + this.bufferRenderer = new CanvasBrushLineRenderer(this.bufferState, this); + this.konva.bufferGroup.add(this.bufferRenderer.konva.group); + } + + didRender = this.bufferRenderer.update(this.bufferState, true); + } else if (this.bufferState.type === 'eraser_line') { + assert(this.bufferRenderer instanceof CanvasEraserLineRenderer || !this.bufferRenderer); + + if (!this.bufferRenderer) { + this.bufferRenderer = new CanvasEraserLineRenderer(this.bufferState, this); + this.konva.bufferGroup.add(this.bufferRenderer.konva.group); + } + + didRender = this.bufferRenderer.update(this.bufferState, true); + } else if (this.bufferState.type === 'rect') { + assert(this.bufferRenderer instanceof CanvasRectRenderer || !this.bufferRenderer); + + if (!this.bufferRenderer) { + this.bufferRenderer = new CanvasRectRenderer(this.bufferState, this); + this.konva.bufferGroup.add(this.bufferRenderer.konva.group); + } + + didRender = this.bufferRenderer.update(this.bufferState, true); + } else if (this.bufferState.type === 'image') { + assert(this.bufferRenderer instanceof CanvasImageRenderer || !this.bufferRenderer); + + if (!this.bufferRenderer) { + this.bufferRenderer = new CanvasImageRenderer(this.bufferState, this); + this.konva.bufferGroup.add(this.bufferRenderer.konva.group); + } + didRender = await this.bufferRenderer.update(this.bufferState, true); + } + + return didRender; + }; + /** * Determines if the renderer has a buffer object to render. * @returns Whether the renderer has a buffer object to render. */ hasBuffer = (): boolean => { - return this.buffer !== null; + return this.bufferState !== null || this.bufferRenderer !== null; }; /** @@ -319,33 +381,27 @@ export class CanvasObjectRenderer { setBuffer = async (objectState: AnyObjectState): Promise => { this.log.trace('Setting buffer'); - this.buffer = objectState; - return await this.renderObject(this.buffer, true); + this.bufferState = objectState; + return await this.renderBufferObject(); }; /** * Clears the buffer object state. */ clearBuffer = () => { - this.log.trace('Clearing buffer'); - - if (this.buffer) { - const renderer = this.renderers.get(this.buffer.id); - if (renderer) { - this.log.trace('Destroying buffer object renderer'); - renderer.destroy(); - this.renderers.delete(renderer.id); - } + if (this.bufferState || this.bufferRenderer) { + this.log.trace('Clearing buffer'); + this.bufferRenderer?.destroy(); + this.bufferRenderer = null; + this.bufferState = null; } - - this.buffer = null; }; /** * Commits the current buffer object, pushing the buffer object state back to the application state. */ commitBuffer = () => { - if (!this.buffer) { + if (!this.bufferState) { this.log.trace('No buffer to commit'); return; } @@ -354,25 +410,23 @@ export class CanvasObjectRenderer { // We need to give the objects a fresh ID else they will be considered the same object when they are re-rendered as // a non-buffer object, and we won't trigger things like bbox calculation - this.buffer.id = getPrefixedId(this.buffer.type); + this.bufferState.id = getPrefixedId(this.bufferState.type); - if (this.buffer.type === 'brush_line') { + if (this.bufferState.type === 'brush_line') { this.manager.stateApi.addBrushLine({ entityIdentifier: this.parent.getEntityIdentifier(), - brushLine: this.buffer, + brushLine: this.bufferState, }); - } else if (this.buffer.type === 'eraser_line') { + } else if (this.bufferState.type === 'eraser_line') { this.manager.stateApi.addEraserLine({ entityIdentifier: this.parent.getEntityIdentifier(), - eraserLine: this.buffer, + eraserLine: this.bufferState, }); - } else if (this.buffer.type === 'rect') { - this.manager.stateApi.addRect({ entityIdentifier: this.parent.getEntityIdentifier(), rect: this.buffer }); + } else if (this.bufferState.type === 'rect') { + this.manager.stateApi.addRect({ entityIdentifier: this.parent.getEntityIdentifier(), rect: this.bufferState }); } else { - this.log.warn({ buffer: this.buffer }, 'Invalid buffer object type'); + this.log.warn({ buffer: this.bufferState }, 'Invalid buffer object type'); } - - this.buffer = null; }; hideObjects = (except: string[] = []) => { @@ -416,7 +470,7 @@ export class CanvasObjectRenderer { * @returns Whether the renderer has any objects to render. */ hasObjects = (): boolean => { - return this.renderers.size > 0 || this.buffer !== null; + return this.renderers.size > 0 || this.bufferState !== null || this.bufferRenderer !== null; }; getRasterizedImageCache = (rect: Rect): ImageCache | null => { @@ -455,7 +509,8 @@ export class CanvasObjectRenderer { imageDTO = await uploadImage(blob, `${this.id}_rasterized.png`, 'other', true); const imageObject = imageDTOToImageObject(imageDTO); if (replaceObjects) { - await this.renderObject(imageObject, true); + this.bufferState = imageObject; + await this.renderBufferObject(); } this.manager.stateApi.rasterizeEntity({ entityIdentifier: this.parent.getEntityIdentifier(), @@ -500,7 +555,7 @@ export class CanvasObjectRenderer { type: this.type, parent: this.parent.id, renderers: Array.from(this.renderers.values()).map((renderer) => renderer.repr()), - buffer: deepClone(this.buffer), + buffer: this.bufferRenderer?.repr(), }; }; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRect.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRect.ts index 9bb7340a4f..887916548c 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRect.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasRect.ts @@ -24,7 +24,7 @@ export class CanvasRectRenderer { isFirstRender: boolean = false; constructor(state: CanvasRectState, parent: CanvasObjectRenderer) { - const { id, rect, color } = state; + const { id } = state; this.id = id; this.parent = parent; this.manager = parent.manager; @@ -34,12 +34,7 @@ export class CanvasRectRenderer { this.konva = { group: new Konva.Group({ name: `${this.type}:group`, listening: false }), - rect: new Konva.Rect({ - name: `${this.type}:rect`, - ...rect, - listening: false, - fill: rgbaColorToString(color), - }), + rect: new Konva.Rect({ name: `${this.type}:rect`, listening: false }), }; this.konva.group.add(this.konva.rect); this.state = state; @@ -52,7 +47,10 @@ export class CanvasRectRenderer { this.log.trace({ state }, 'Updating rect'); const { rect, color } = state; this.konva.rect.setAttrs({ - ...rect, + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, fill: rgbaColorToString(color), }); this.state = state; diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTransformer.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTransformer.ts index e4a8e929d8..a23337448b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTransformer.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTransformer.ts @@ -5,6 +5,7 @@ import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskA import { getEmptyRect, getPrefixedId } from 'features/controlLayers/konva/util'; import type { Coordinate, Rect } from 'features/controlLayers/store/types'; import Konva from 'konva'; +import type { GroupConfig } from 'konva/lib/Group'; import { debounce, get } from 'lodash-es'; import type { Logger } from 'roarr'; @@ -543,6 +544,7 @@ export class CanvasTransformer { rotation: 0, }; this.parent.renderer.konva.objectGroup.setAttrs(attrs); + this.parent.renderer.konva.bufferGroup.setAttrs(attrs); this.konva.outlineRect.setAttrs(attrs); this.konva.proxyRect.setAttrs(attrs); }; @@ -555,12 +557,14 @@ export class CanvasTransformer { this.log.trace('Updating position'); const position = get(arg, 'position', this.parent.state.position); - this.parent.renderer.konva.objectGroup.setAttrs({ + const groupAttrs: Partial = { x: position.x + this.pixelRect.x, y: position.y + this.pixelRect.y, offsetX: this.pixelRect.x, offsetY: this.pixelRect.y, - }); + }; + this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs); + this.parent.renderer.konva.bufferGroup.setAttrs(groupAttrs); this.update(position, this.pixelRect); }; @@ -609,12 +613,14 @@ export class CanvasTransformer { this.syncInteractionState(); this.update(this.parent.state.position, this.pixelRect); - this.parent.renderer.konva.objectGroup.setAttrs({ + const groupAttrs: Partial = { x: this.parent.state.position.x + this.pixelRect.x, y: this.parent.state.position.y + this.pixelRect.y, offsetX: this.pixelRect.x, offsetY: this.pixelRect.y, - }); + }; + this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs); + this.parent.renderer.konva.bufferGroup.setAttrs(groupAttrs); }; calculateRect = debounce(() => { diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/events.ts b/invokeai/frontend/web/src/features/controlLayers/konva/events.ts index 084faaf650..c0e94109f2 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/events.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/events.ts @@ -217,7 +217,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width); if (e.evt.shiftKey && lastLinePoint) { // Create a straight line from the last line point - if (selectedEntity.adapter.renderer.buffer) { + if (selectedEntity.adapter.renderer.bufferState) { selectedEntity.adapter.renderer.commitBuffer(); } @@ -236,7 +236,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { clip: getClip(selectedEntity.state), }); } else { - if (selectedEntity.adapter.renderer.buffer) { + if (selectedEntity.adapter.renderer.bufferState) { selectedEntity.adapter.renderer.commitBuffer(); } await selectedEntity.adapter.renderer.setBuffer({ @@ -256,7 +256,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width); if (e.evt.shiftKey && lastLinePoint) { // Create a straight line from the last line point - if (selectedEntity.adapter.renderer.buffer) { + if (selectedEntity.adapter.renderer.bufferState) { selectedEntity.adapter.renderer.commitBuffer(); } await selectedEntity.adapter.renderer.setBuffer({ @@ -273,7 +273,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { clip: getClip(selectedEntity.state), }); } else { - if (selectedEntity.adapter.renderer.buffer) { + if (selectedEntity.adapter.renderer.bufferState) { selectedEntity.adapter.renderer.commitBuffer(); } await selectedEntity.adapter.renderer.setBuffer({ @@ -288,7 +288,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { } if (toolState.selected === 'rect') { - if (selectedEntity.adapter.renderer.buffer) { + if (selectedEntity.adapter.renderer.bufferState) { selectedEntity.adapter.renderer.commitBuffer(); } await selectedEntity.adapter.renderer.setBuffer({ @@ -312,7 +312,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { const toolState = getToolState(); if (toolState.selected === 'brush') { - const drawingBuffer = selectedEntity.adapter.renderer.buffer; + const drawingBuffer = selectedEntity.adapter.renderer.bufferState; if (drawingBuffer?.type === 'brush_line') { selectedEntity.adapter.renderer.commitBuffer(); } else { @@ -321,7 +321,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { } if (toolState.selected === 'eraser') { - const drawingBuffer = selectedEntity.adapter.renderer.buffer; + const drawingBuffer = selectedEntity.adapter.renderer.bufferState; if (drawingBuffer?.type === 'eraser_line') { selectedEntity.adapter.renderer.commitBuffer(); } else { @@ -330,7 +330,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { } if (toolState.selected === 'rect') { - const drawingBuffer = selectedEntity.adapter.renderer.buffer; + const drawingBuffer = selectedEntity.adapter.renderer.bufferState; if (drawingBuffer?.type === 'rect') { selectedEntity.adapter.renderer.commitBuffer(); } else { @@ -365,7 +365,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { getIsPrimaryMouseDown(e) ) { if (toolState.selected === 'brush') { - const drawingBuffer = selectedEntity.adapter.renderer.buffer; + const drawingBuffer = selectedEntity.adapter.renderer.bufferState; if (drawingBuffer) { if (drawingBuffer.type === 'brush_line') { const lastPoint = getLastPointOfLine(drawingBuffer.points); @@ -384,7 +384,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { selectedEntity.adapter.renderer.clearBuffer(); } } else { - if (selectedEntity.adapter.renderer.buffer) { + if (selectedEntity.adapter.renderer.bufferState) { selectedEntity.adapter.renderer.commitBuffer(); } const normalizedPoint = offsetCoord(pos, selectedEntity.state.position); @@ -402,7 +402,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { } if (toolState.selected === 'eraser') { - const drawingBuffer = selectedEntity.adapter.renderer.buffer; + const drawingBuffer = selectedEntity.adapter.renderer.bufferState; if (drawingBuffer) { if (drawingBuffer.type === 'eraser_line') { const lastPoint = getLastPointOfLine(drawingBuffer.points); @@ -421,7 +421,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { selectedEntity.adapter.renderer.clearBuffer(); } } else { - if (selectedEntity.adapter.renderer.buffer) { + if (selectedEntity.adapter.renderer.bufferState) { selectedEntity.adapter.renderer.commitBuffer(); } const normalizedPoint = offsetCoord(pos, selectedEntity.state.position); @@ -438,7 +438,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { } if (toolState.selected === 'rect') { - const drawingBuffer = selectedEntity.adapter.renderer.buffer; + const drawingBuffer = selectedEntity.adapter.renderer.bufferState; if (drawingBuffer) { if (drawingBuffer.type === 'rect') { const normalizedPoint = offsetCoord(pos, selectedEntity.state.position); @@ -469,7 +469,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => { !$spaceKey.get() && getIsPrimaryMouseDown(e) ) { - const drawingBuffer = selectedEntity.adapter.renderer.buffer; + const drawingBuffer = selectedEntity.adapter.renderer.bufferState; const normalizedPoint = offsetCoord(pos, selectedEntity.state.position); if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') { const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);