mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-01 19:35:04 -05:00
feat(ui): split buffer renderer from object renderer
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import type { CanvasEntityFilterer } from 'features/controlLayers/konva/CanvasEntityFilterer';
|
||||
import type { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import type { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
|
||||
@@ -35,6 +36,11 @@ export abstract class CanvasEntityAdapterBase<
|
||||
*/
|
||||
abstract renderer: CanvasEntityObjectRenderer;
|
||||
|
||||
/**
|
||||
* The buffer renderer for this entity adapter. All entities must have a buffer renderer.
|
||||
*/
|
||||
abstract bufferRenderer: CanvasEntityBufferObjectRenderer;
|
||||
|
||||
/**
|
||||
* The filterer for this entity adapter. Entities that support filtering should implement this property.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapter/CanvasEntityAdapterBase';
|
||||
import { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import { CanvasEntityFilterer } from 'features/controlLayers/konva/CanvasEntityFilterer';
|
||||
import { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
|
||||
@@ -11,14 +12,16 @@ import { omit } from 'lodash-es';
|
||||
export class CanvasEntityAdapterControlLayer extends CanvasEntityAdapterBase<CanvasControlLayerState> {
|
||||
static TYPE = 'control_layer_adapter';
|
||||
|
||||
transformer: CanvasEntityTransformer;
|
||||
renderer: CanvasEntityObjectRenderer;
|
||||
bufferRenderer: CanvasEntityBufferObjectRenderer;
|
||||
transformer: CanvasEntityTransformer;
|
||||
filterer: CanvasEntityFilterer;
|
||||
|
||||
constructor(entityIdentifier: CanvasEntityIdentifier<'control_layer'>, manager: CanvasManager) {
|
||||
super(entityIdentifier, manager, CanvasEntityAdapterControlLayer.TYPE);
|
||||
|
||||
this.renderer = new CanvasEntityObjectRenderer(this);
|
||||
this.bufferRenderer = new CanvasEntityBufferObjectRenderer(this);
|
||||
this.transformer = new CanvasEntityTransformer(this);
|
||||
this.filterer = new CanvasEntityFilterer(this);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapter/CanvasEntityAdapterBase';
|
||||
import { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
@@ -10,14 +11,16 @@ import { omit } from 'lodash-es';
|
||||
export class CanvasEntityAdapterInpaintMask extends CanvasEntityAdapterBase<CanvasInpaintMaskState> {
|
||||
static TYPE = 'inpaint_mask_adapter';
|
||||
|
||||
transformer: CanvasEntityTransformer;
|
||||
renderer: CanvasEntityObjectRenderer;
|
||||
bufferRenderer: CanvasEntityBufferObjectRenderer;
|
||||
transformer: CanvasEntityTransformer;
|
||||
filterer = undefined;
|
||||
|
||||
constructor(entityIdentifier: CanvasEntityIdentifier<'inpaint_mask'>, manager: CanvasManager) {
|
||||
super(entityIdentifier, manager, CanvasEntityAdapterInpaintMask.TYPE);
|
||||
|
||||
this.renderer = new CanvasEntityObjectRenderer(this);
|
||||
this.bufferRenderer = new CanvasEntityBufferObjectRenderer(this);
|
||||
this.transformer = new CanvasEntityTransformer(this);
|
||||
|
||||
this.subscriptions.add(this.manager.stateApi.createStoreSubscription(this.selectState, this.sync));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapter/CanvasEntityAdapterBase';
|
||||
import { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import { CanvasEntityFilterer } from 'features/controlLayers/konva/CanvasEntityFilterer';
|
||||
import { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
|
||||
@@ -11,14 +12,16 @@ import { omit } from 'lodash-es';
|
||||
export class CanvasEntityAdapterRasterLayer extends CanvasEntityAdapterBase<CanvasRasterLayerState> {
|
||||
static TYPE = 'raster_layer_adapter';
|
||||
|
||||
transformer: CanvasEntityTransformer;
|
||||
renderer: CanvasEntityObjectRenderer;
|
||||
bufferRenderer: CanvasEntityBufferObjectRenderer;
|
||||
transformer: CanvasEntityTransformer;
|
||||
filterer: CanvasEntityFilterer;
|
||||
|
||||
constructor(entityIdentifier: CanvasEntityIdentifier<'raster_layer'>, manager: CanvasManager) {
|
||||
super(entityIdentifier, manager, CanvasEntityAdapterRasterLayer.TYPE);
|
||||
|
||||
this.renderer = new CanvasEntityObjectRenderer(this);
|
||||
this.bufferRenderer = new CanvasEntityBufferObjectRenderer(this);
|
||||
this.transformer = new CanvasEntityTransformer(this);
|
||||
this.filterer = new CanvasEntityFilterer(this);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { SerializableObject } from 'common/types';
|
||||
import { CanvasEntityAdapterBase } from 'features/controlLayers/konva/CanvasEntityAdapter/CanvasEntityAdapterBase';
|
||||
import { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntityTransformer';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
@@ -10,14 +11,16 @@ import { omit } from 'lodash-es';
|
||||
export class CanvasEntityAdapterRegionalGuidance extends CanvasEntityAdapterBase<CanvasRegionalGuidanceState> {
|
||||
static TYPE = 'regional_guidance_adapter';
|
||||
|
||||
transformer: CanvasEntityTransformer;
|
||||
renderer: CanvasEntityObjectRenderer;
|
||||
bufferRenderer: CanvasEntityBufferObjectRenderer;
|
||||
transformer: CanvasEntityTransformer;
|
||||
filterer = undefined;
|
||||
|
||||
constructor(entityIdentifier: CanvasEntityIdentifier<'regional_guidance'>, manager: CanvasManager) {
|
||||
super(entityIdentifier, manager, CanvasEntityAdapterRegionalGuidance.TYPE);
|
||||
|
||||
this.renderer = new CanvasEntityObjectRenderer(this);
|
||||
this.bufferRenderer = new CanvasEntityBufferObjectRenderer(this);
|
||||
this.transformer = new CanvasEntityTransformer(this);
|
||||
|
||||
this.subscriptions.add(this.manager.stateApi.createStoreSubscription(this.selectState, this.sync));
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEntityAdapter/types';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { CanvasObjectBrushLine } from 'features/controlLayers/konva/CanvasObjectBrushLine';
|
||||
import { CanvasObjectEraserLine } from 'features/controlLayers/konva/CanvasObjectEraserLine';
|
||||
import { CanvasObjectImage } from 'features/controlLayers/konva/CanvasObjectImage';
|
||||
import { CanvasObjectRect } from 'features/controlLayers/konva/CanvasObjectRect';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import type {
|
||||
CanvasBrushLineState,
|
||||
CanvasEraserLineState,
|
||||
CanvasImageState,
|
||||
CanvasRectState,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import type { Logger } from 'roarr';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
/**
|
||||
* Union of all object renderers.
|
||||
*/
|
||||
type AnyObjectRenderer = CanvasObjectBrushLine | CanvasObjectEraserLine | CanvasObjectRect | CanvasObjectImage;
|
||||
/**
|
||||
* Union of all object states.
|
||||
*/
|
||||
type AnyObjectState = CanvasBrushLineState | CanvasEraserLineState | CanvasImageState | CanvasRectState;
|
||||
|
||||
/**
|
||||
* Handles rendering of objects for a canvas entity.
|
||||
*/
|
||||
export class CanvasEntityBufferObjectRenderer extends CanvasModuleBase {
|
||||
readonly type = 'buffer_renderer';
|
||||
readonly id: string;
|
||||
readonly path: string[];
|
||||
readonly parent: CanvasEntityAdapter;
|
||||
readonly manager: CanvasManager;
|
||||
readonly log: Logger;
|
||||
|
||||
/**
|
||||
* A set of subscriptions that should be cleaned up when the transformer is destroyed.
|
||||
*/
|
||||
subscriptions: Set<() => void> = new Set();
|
||||
|
||||
/**
|
||||
* A buffer object state that is rendered separately from the other objects. This is used for objects that are being
|
||||
* 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.
|
||||
*/
|
||||
state: 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.
|
||||
*/
|
||||
renderer: AnyObjectRenderer | null = null;
|
||||
|
||||
/**
|
||||
* A object containing singleton Konva nodes.
|
||||
*/
|
||||
konva: {
|
||||
/**
|
||||
* A Konva Group that holds the buffer object renderer.
|
||||
*/
|
||||
group: Konva.Group;
|
||||
};
|
||||
|
||||
constructor(parent: CanvasEntityAdapter) {
|
||||
super();
|
||||
this.id = getPrefixedId(this.type);
|
||||
this.parent = parent;
|
||||
this.manager = parent.manager;
|
||||
this.path = this.manager.buildPath(this);
|
||||
this.log = this.manager.buildLogger(this);
|
||||
this.log.debug('Creating module');
|
||||
|
||||
this.konva = {
|
||||
group: new Konva.Group({ name: `${this.type}:buffer_group`, listening: false }),
|
||||
};
|
||||
|
||||
this.parent.konva.layer.add(this.konva.group);
|
||||
|
||||
// When switching tool, commit the buffer. This is necessary to prevent the buffer from being lost when the
|
||||
// user switches tool mid-drawing, for example by pressing space to pan the stage. It's easy to press space
|
||||
// to pan _before_ releasing the mouse button, which would cause the buffer to be lost if we didn't commit it.
|
||||
this.subscriptions.add(
|
||||
this.manager.tool.$tool.listen(() => {
|
||||
if (this.hasBuffer() && !this.manager.$isBusy.get()) {
|
||||
this.commitBuffer();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<boolean> => {
|
||||
let didRender = false;
|
||||
|
||||
if (!this.state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.state.type === 'brush_line') {
|
||||
assert(this.renderer instanceof CanvasObjectBrushLine || !this.renderer);
|
||||
|
||||
if (!this.renderer) {
|
||||
this.renderer = new CanvasObjectBrushLine(this.state, this);
|
||||
this.konva.group.add(this.renderer.konva.group);
|
||||
}
|
||||
|
||||
didRender = this.renderer.update(this.state, true);
|
||||
} else if (this.state.type === 'eraser_line') {
|
||||
assert(this.renderer instanceof CanvasObjectEraserLine || !this.renderer);
|
||||
|
||||
if (!this.renderer) {
|
||||
this.renderer = new CanvasObjectEraserLine(this.state, this);
|
||||
this.konva.group.add(this.renderer.konva.group);
|
||||
}
|
||||
|
||||
didRender = this.renderer.update(this.state, true);
|
||||
} else if (this.state.type === 'rect') {
|
||||
assert(this.renderer instanceof CanvasObjectRect || !this.renderer);
|
||||
|
||||
if (!this.renderer) {
|
||||
this.renderer = new CanvasObjectRect(this.state, this);
|
||||
this.konva.group.add(this.renderer.konva.group);
|
||||
}
|
||||
|
||||
didRender = this.renderer.update(this.state, true);
|
||||
} else if (this.state.type === 'image') {
|
||||
assert(this.renderer instanceof CanvasObjectImage || !this.renderer);
|
||||
|
||||
if (!this.renderer) {
|
||||
this.renderer = new CanvasObjectImage(this.state, this);
|
||||
this.konva.group.add(this.renderer.konva.group);
|
||||
}
|
||||
didRender = await this.renderer.update(this.state, 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.state !== null || this.renderer !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the buffer object state to render.
|
||||
* @param objectState The object state to set as the buffer.
|
||||
* @param resetBufferOffset Whether to reset the buffer's offset to 0,0. This is necessary when previewing filters.
|
||||
* When previewing a filter, the buffer object is an image of the same size as the entity, so it should be rendered
|
||||
* at the top-left corner of the entity.
|
||||
* @returns A promise that resolves to a boolean, indicating if the object was rendered.
|
||||
*/
|
||||
setBuffer = async (objectState: AnyObjectState, resetBufferOffset: boolean = false): Promise<boolean> => {
|
||||
this.log.trace('Setting buffer');
|
||||
|
||||
this.state = objectState;
|
||||
if (resetBufferOffset) {
|
||||
this.konva.group.offset({ x: 0, y: 0 });
|
||||
}
|
||||
return await this.renderBufferObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the buffer object state.
|
||||
*/
|
||||
clearBuffer = () => {
|
||||
if (this.state || this.renderer) {
|
||||
this.log.trace('Clearing buffer');
|
||||
this.renderer?.destroy();
|
||||
this.renderer = null;
|
||||
this.state = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Commits the current buffer object, pushing the buffer object state back to the application state.
|
||||
*/
|
||||
commitBuffer = (options?: { pushToState?: boolean }) => {
|
||||
const { pushToState } = { ...options, pushToState: true };
|
||||
|
||||
if (!this.state || !this.renderer) {
|
||||
this.log.trace('No buffer to commit');
|
||||
return;
|
||||
}
|
||||
|
||||
this.log.trace('Committing buffer');
|
||||
|
||||
// Move the buffer to the persistent objects group/renderers
|
||||
this.parent.renderer.adoptObjectRenderer(this.renderer);
|
||||
|
||||
if (pushToState) {
|
||||
const entityIdentifier = this.parent.entityIdentifier;
|
||||
if (this.state.type === 'brush_line') {
|
||||
this.manager.stateApi.addBrushLine({ entityIdentifier, brushLine: this.state });
|
||||
} else if (this.state.type === 'eraser_line') {
|
||||
this.manager.stateApi.addEraserLine({ entityIdentifier, eraserLine: this.state });
|
||||
} else if (this.state.type === 'rect') {
|
||||
this.manager.stateApi.addRect({ entityIdentifier, rect: this.state });
|
||||
} else {
|
||||
this.log.warn({ buffer: this.state }, 'Invalid buffer object type');
|
||||
}
|
||||
}
|
||||
|
||||
this.renderer = null;
|
||||
this.state = null;
|
||||
};
|
||||
|
||||
destroy = () => {
|
||||
this.log.debug('Destroying module');
|
||||
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
||||
this.subscriptions.clear();
|
||||
if (this.renderer) {
|
||||
this.renderer.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
path: this.path,
|
||||
parent: this.parent.id,
|
||||
bufferState: deepClone(this.state),
|
||||
bufferRenderer: this.renderer?.repr(),
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -96,7 +96,7 @@ export class CanvasEntityFilterer extends CanvasModuleBase {
|
||||
|
||||
this.imageState = imageDTOToImageObject(imageDTO);
|
||||
|
||||
await this.parent.renderer.setBuffer(this.imageState, true);
|
||||
await this.parent.bufferRenderer.setBuffer(this.imageState, true);
|
||||
|
||||
this.parent.renderer.hideObjects();
|
||||
this.$isProcessing.set(false);
|
||||
@@ -120,7 +120,7 @@ export class CanvasEntityFilterer extends CanvasModuleBase {
|
||||
return;
|
||||
}
|
||||
this.log.trace('Applying filter');
|
||||
this.parent.renderer.commitBuffer();
|
||||
this.parent.bufferRenderer.commitBuffer();
|
||||
const rect = this.parent.transformer.getRelativeRect();
|
||||
this.manager.stateApi.rasterizeEntity({
|
||||
entityIdentifier: this.parent.entityIdentifier,
|
||||
@@ -142,7 +142,7 @@ export class CanvasEntityFilterer extends CanvasModuleBase {
|
||||
cancelFilter = () => {
|
||||
this.log.trace('Cancelling filter');
|
||||
|
||||
this.parent.renderer.clearBuffer();
|
||||
this.parent.bufferRenderer.clearBuffer();
|
||||
this.parent.renderer.showObjects();
|
||||
this.parent.transformer.updatePosition();
|
||||
this.parent.renderer.syncCache(true);
|
||||
|
||||
@@ -68,19 +68,6 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
*/
|
||||
subscriptions: Set<() => void> = new Set();
|
||||
|
||||
/**
|
||||
* A buffer object state that is rendered separately from the other objects. This is used for objects that are being
|
||||
* 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.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -96,10 +83,6 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
* 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.
|
||||
*
|
||||
@@ -143,12 +126,10 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
|
||||
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.entityIdentifier.type === 'inpaint_mask' ||
|
||||
@@ -170,17 +151,6 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
this.parent.konva.layer.add(this.konva.compositing.group);
|
||||
}
|
||||
|
||||
// When switching tool, commit the buffer. This is necessary to prevent the buffer from being lost when the
|
||||
// user switches tool mid-drawing, for example by pressing space to pan the stage. It's easy to press space
|
||||
// to pan _before_ releasing the mouse button, which would cause the buffer to be lost if we didn't commit it.
|
||||
this.subscriptions.add(
|
||||
this.manager.tool.$tool.listen(() => {
|
||||
if (this.hasBuffer() && !this.manager.$isBusy.get()) {
|
||||
this.commitBuffer();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// The compositing rect must cover the whole stage at all times. When the stage is scaled, moved or resized, we
|
||||
// need to update the compositing rect to match the stage.
|
||||
this.subscriptions.add(
|
||||
@@ -227,6 +197,11 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
return didRender;
|
||||
};
|
||||
|
||||
adoptObjectRenderer = (renderer: AnyObjectRenderer) => {
|
||||
this.renderers.set(renderer.id, renderer);
|
||||
renderer.konva.group.moveTo(this.konva.objectGroup);
|
||||
};
|
||||
|
||||
syncCache = (force: boolean = false) => {
|
||||
if (this.renderers.size === 0) {
|
||||
this.log.trace('Clearing object group cache');
|
||||
@@ -234,7 +209,7 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
} else if (force || !this.konva.objectGroup.isCached()) {
|
||||
this.log.trace('Caching object group');
|
||||
this.konva.objectGroup.clearCache();
|
||||
this.konva.objectGroup.cache({ pixelRatio: 1 });
|
||||
this.konva.objectGroup.cache({ pixelRatio: 1, imageSmoothingEnabled: false });
|
||||
this.parent.renderer.updatePreviewCanvas();
|
||||
}
|
||||
};
|
||||
@@ -295,8 +270,8 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
this.konva.compositing.group.opacity(opacity);
|
||||
} else {
|
||||
this.konva.objectGroup.opacity(opacity);
|
||||
this.konva.bufferGroup.opacity(opacity);
|
||||
}
|
||||
this.parent.bufferRenderer.konva.group.opacity(opacity);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -363,131 +338,6 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
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<boolean> => {
|
||||
let didRender = false;
|
||||
|
||||
if (!this.bufferState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.bufferState.type === 'brush_line') {
|
||||
assert(this.bufferRenderer instanceof CanvasObjectBrushLine || !this.bufferRenderer);
|
||||
|
||||
if (!this.bufferRenderer) {
|
||||
this.bufferRenderer = new CanvasObjectBrushLine(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 CanvasObjectEraserLine || !this.bufferRenderer);
|
||||
|
||||
if (!this.bufferRenderer) {
|
||||
this.bufferRenderer = new CanvasObjectEraserLine(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 CanvasObjectRect || !this.bufferRenderer);
|
||||
|
||||
if (!this.bufferRenderer) {
|
||||
this.bufferRenderer = new CanvasObjectRect(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 CanvasObjectImage || !this.bufferRenderer);
|
||||
|
||||
if (!this.bufferRenderer) {
|
||||
this.bufferRenderer = new CanvasObjectImage(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.bufferState !== null || this.bufferRenderer !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the buffer object state to render.
|
||||
* @param objectState The object state to set as the buffer.
|
||||
* @param resetBufferOffset Whether to reset the buffer's offset to 0,0. This is necessary when previewing filters.
|
||||
* When previewing a filter, the buffer object is an image of the same size as the entity, so it should be rendered
|
||||
* at the top-left corner of the entity.
|
||||
* @returns A promise that resolves to a boolean, indicating if the object was rendered.
|
||||
*/
|
||||
setBuffer = async (objectState: AnyObjectState, resetBufferOffset: boolean = false): Promise<boolean> => {
|
||||
this.log.trace('Setting buffer');
|
||||
|
||||
this.bufferState = objectState;
|
||||
if (resetBufferOffset) {
|
||||
this.konva.bufferGroup.offset({ x: 0, y: 0 });
|
||||
}
|
||||
return await this.renderBufferObject();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the buffer object state.
|
||||
*/
|
||||
clearBuffer = () => {
|
||||
if (this.bufferState || this.bufferRenderer) {
|
||||
this.log.trace('Clearing buffer');
|
||||
this.bufferRenderer?.destroy();
|
||||
this.bufferRenderer = null;
|
||||
this.bufferState = null;
|
||||
this.syncCache(true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Commits the current buffer object, pushing the buffer object state back to the application state.
|
||||
*/
|
||||
commitBuffer = (options?: { pushToState?: boolean }) => {
|
||||
const { pushToState } = { ...options, pushToState: true };
|
||||
|
||||
if (!this.bufferState || !this.bufferRenderer) {
|
||||
this.log.trace('No buffer to commit');
|
||||
return;
|
||||
}
|
||||
|
||||
this.log.trace('Committing buffer');
|
||||
|
||||
// Move the buffer to the persistent objects group/renderers
|
||||
this.bufferRenderer.konva.group.moveTo(this.konva.objectGroup);
|
||||
this.renderers.set(this.bufferState.id, this.bufferRenderer);
|
||||
|
||||
if (pushToState) {
|
||||
const entityIdentifier = this.parent.entityIdentifier;
|
||||
if (this.bufferState.type === 'brush_line') {
|
||||
this.manager.stateApi.addBrushLine({ entityIdentifier, brushLine: this.bufferState });
|
||||
} else if (this.bufferState.type === 'eraser_line') {
|
||||
this.manager.stateApi.addEraserLine({ entityIdentifier, eraserLine: this.bufferState });
|
||||
} else if (this.bufferState.type === 'rect') {
|
||||
this.manager.stateApi.addRect({ entityIdentifier, rect: this.bufferState });
|
||||
} else {
|
||||
this.log.warn({ buffer: this.bufferState }, 'Invalid buffer object type');
|
||||
}
|
||||
}
|
||||
|
||||
this.bufferRenderer = null;
|
||||
this.bufferState = null;
|
||||
};
|
||||
|
||||
hideObjects = (except: string[] = []) => {
|
||||
for (const renderer of this.renderers.values()) {
|
||||
renderer.setVisibility(except.includes(renderer.id));
|
||||
@@ -529,7 +379,7 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
* @returns Whether the renderer has any objects to render.
|
||||
*/
|
||||
hasObjects = (): boolean => {
|
||||
return this.renderers.size > 0 || this.bufferState !== null || this.bufferRenderer !== null;
|
||||
return this.renderers.size > 0 || this.parent.bufferRenderer.hasBuffer();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -576,8 +426,8 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
});
|
||||
const imageObject = imageDTOToImageObject(imageDTO);
|
||||
if (replaceObjects) {
|
||||
await this.setBuffer(imageObject);
|
||||
this.commitBuffer({ pushToState: false });
|
||||
await this.parent.bufferRenderer.setBuffer(imageObject);
|
||||
this.parent.bufferRenderer.commitBuffer({ pushToState: false });
|
||||
}
|
||||
this.manager.stateApi.rasterizeEntity({
|
||||
entityIdentifier: this.parent.entityIdentifier,
|
||||
@@ -667,7 +517,6 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
|
||||
path: this.path,
|
||||
parent: this.parent.id,
|
||||
renderers: Array.from(this.renderers.values()).map((renderer) => renderer.repr()),
|
||||
buffer: this.bufferRenderer?.repr(),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -587,7 +587,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
rotation: 0,
|
||||
};
|
||||
this.parent.renderer.konva.objectGroup.setAttrs(attrs);
|
||||
this.parent.renderer.konva.bufferGroup.setAttrs(attrs);
|
||||
this.parent.bufferRenderer.konva.group.setAttrs(attrs);
|
||||
this.konva.outlineRect.setAttrs(attrs);
|
||||
this.konva.proxyRect.setAttrs(attrs);
|
||||
};
|
||||
@@ -608,7 +608,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
offsetY: pixelRect.y,
|
||||
};
|
||||
this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs);
|
||||
this.parent.renderer.konva.bufferGroup.setAttrs(groupAttrs);
|
||||
this.parent.bufferRenderer.konva.group.setAttrs(groupAttrs);
|
||||
|
||||
this.update(position, pixelRect);
|
||||
};
|
||||
@@ -669,7 +669,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
offsetY: pixelRect.y,
|
||||
};
|
||||
this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs);
|
||||
this.parent.renderer.konva.bufferGroup.setAttrs(groupAttrs);
|
||||
this.parent.bufferRenderer.konva.group.setAttrs(groupAttrs);
|
||||
}
|
||||
|
||||
this.parent.renderer.updatePreviewCanvas();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import type { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
@@ -11,7 +12,7 @@ export class CanvasObjectBrushLine extends CanvasModuleBase {
|
||||
readonly type = 'object_brush_line';
|
||||
readonly id: string;
|
||||
readonly path: string[];
|
||||
readonly parent: CanvasEntityObjectRenderer;
|
||||
readonly parent: CanvasEntityObjectRenderer | CanvasEntityBufferObjectRenderer;
|
||||
readonly manager: CanvasManager;
|
||||
readonly log: Logger;
|
||||
|
||||
@@ -21,7 +22,7 @@ export class CanvasObjectBrushLine extends CanvasModuleBase {
|
||||
line: Konva.Line;
|
||||
};
|
||||
|
||||
constructor(state: CanvasBrushLineState, parent: CanvasEntityObjectRenderer) {
|
||||
constructor(state: CanvasBrushLineState, parent: CanvasEntityObjectRenderer | CanvasEntityBufferObjectRenderer) {
|
||||
super();
|
||||
const { id, clip } = state;
|
||||
this.id = id;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import type { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
@@ -10,7 +11,7 @@ export class CanvasObjectEraserLine extends CanvasModuleBase {
|
||||
readonly type = 'object_eraser_line';
|
||||
readonly id: string;
|
||||
readonly path: string[];
|
||||
readonly parent: CanvasEntityObjectRenderer;
|
||||
readonly parent: CanvasEntityObjectRenderer | CanvasEntityBufferObjectRenderer;
|
||||
readonly manager: CanvasManager;
|
||||
readonly log: Logger;
|
||||
|
||||
@@ -20,7 +21,7 @@ export class CanvasObjectEraserLine extends CanvasModuleBase {
|
||||
line: Konva.Line;
|
||||
};
|
||||
|
||||
constructor(state: CanvasEraserLineState, parent: CanvasEntityObjectRenderer) {
|
||||
constructor(state: CanvasEraserLineState, parent: CanvasEntityObjectRenderer | CanvasEntityBufferObjectRenderer) {
|
||||
super();
|
||||
this.id = state.id;
|
||||
this.parent = parent;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Mutex } from 'async-mutex';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import type { CanvasEntityFilterer } from 'features/controlLayers/konva/CanvasEntityFilterer';
|
||||
import type { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
@@ -16,7 +17,11 @@ export class CanvasObjectImage extends CanvasModuleBase {
|
||||
readonly type = 'object_image';
|
||||
readonly id: string;
|
||||
readonly path: string[];
|
||||
readonly parent: CanvasEntityObjectRenderer | CanvasStagingAreaModule | CanvasEntityFilterer;
|
||||
readonly parent:
|
||||
| CanvasEntityObjectRenderer
|
||||
| CanvasEntityBufferObjectRenderer
|
||||
| CanvasStagingAreaModule
|
||||
| CanvasEntityFilterer;
|
||||
readonly manager: CanvasManager;
|
||||
readonly log: Logger;
|
||||
|
||||
@@ -33,7 +38,11 @@ export class CanvasObjectImage extends CanvasModuleBase {
|
||||
|
||||
constructor(
|
||||
state: CanvasImageState,
|
||||
parent: CanvasEntityObjectRenderer | CanvasStagingAreaModule | CanvasEntityFilterer
|
||||
parent:
|
||||
| CanvasEntityObjectRenderer
|
||||
| CanvasEntityBufferObjectRenderer
|
||||
| CanvasStagingAreaModule
|
||||
| CanvasEntityFilterer
|
||||
) {
|
||||
super();
|
||||
this.id = state.id;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import type { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntityBufferObjectRenderer';
|
||||
import type { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntityObjectRenderer';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
@@ -11,7 +12,7 @@ export class CanvasObjectRect extends CanvasModuleBase {
|
||||
readonly type = 'object_rect';
|
||||
readonly id: string;
|
||||
readonly path: string[];
|
||||
readonly parent: CanvasEntityObjectRenderer;
|
||||
readonly parent: CanvasEntityObjectRenderer | CanvasEntityBufferObjectRenderer;
|
||||
readonly manager: CanvasManager;
|
||||
readonly log: Logger;
|
||||
|
||||
@@ -22,7 +23,7 @@ export class CanvasObjectRect extends CanvasModuleBase {
|
||||
};
|
||||
isFirstRender: boolean = false;
|
||||
|
||||
constructor(state: CanvasRectState, parent: CanvasEntityObjectRenderer) {
|
||||
constructor(state: CanvasRectState, parent: CanvasEntityObjectRenderer | CanvasEntityBufferObjectRenderer) {
|
||||
super();
|
||||
this.id = state.id;
|
||||
this.parent = parent;
|
||||
|
||||
@@ -307,15 +307,15 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedEntity.renderer.bufferState?.type !== 'rect' && selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity.bufferRenderer.state?.type !== 'rect' && selectedEntity.bufferRenderer.hasBuffer()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
if (tool === 'brush') {
|
||||
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
|
||||
const alignedPoint = alignCoordForTool(normalizedPoint, settings.brushWidth);
|
||||
await selectedEntity.renderer.setBuffer({
|
||||
await selectedEntity.bufferRenderer.setBuffer({
|
||||
id: getPrefixedId('brush_line'),
|
||||
type: 'brush_line',
|
||||
points: [alignedPoint.x, alignedPoint.y],
|
||||
@@ -329,10 +329,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
if (tool === 'eraser') {
|
||||
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
|
||||
const alignedPoint = alignCoordForTool(normalizedPoint, settings.brushWidth);
|
||||
if (selectedEntity.renderer.bufferState && selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity.bufferRenderer.state && selectedEntity.bufferRenderer.hasBuffer()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.renderer.setBuffer({
|
||||
await selectedEntity.bufferRenderer.setBuffer({
|
||||
id: getPrefixedId('eraser_line'),
|
||||
type: 'eraser_line',
|
||||
points: [alignedPoint.x, alignedPoint.y],
|
||||
@@ -380,11 +380,11 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
const alignedPoint = alignCoordForTool(normalizedPoint, settings.brushWidth);
|
||||
if (e.evt.shiftKey && lastLinePoint) {
|
||||
// Create a straight line from the last line point
|
||||
if (selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity.bufferRenderer.hasBuffer()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
}
|
||||
|
||||
await selectedEntity.renderer.setBuffer({
|
||||
await selectedEntity.bufferRenderer.setBuffer({
|
||||
id: getPrefixedId('brush_line'),
|
||||
type: 'brush_line',
|
||||
points: [
|
||||
@@ -399,10 +399,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
clip: this.getClip(selectedEntity.state),
|
||||
});
|
||||
} else {
|
||||
if (selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity.bufferRenderer.hasBuffer()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.renderer.setBuffer({
|
||||
await selectedEntity.bufferRenderer.setBuffer({
|
||||
id: getPrefixedId('brush_line'),
|
||||
type: 'brush_line',
|
||||
points: [alignedPoint.x, alignedPoint.y],
|
||||
@@ -418,10 +418,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
const alignedPoint = alignCoordForTool(normalizedPoint, settings.eraserWidth);
|
||||
if (e.evt.shiftKey && lastLinePoint) {
|
||||
// Create a straight line from the last line point
|
||||
if (selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity.bufferRenderer.hasBuffer()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.renderer.setBuffer({
|
||||
await selectedEntity.bufferRenderer.setBuffer({
|
||||
id: getPrefixedId('eraser_line'),
|
||||
type: 'eraser_line',
|
||||
points: [
|
||||
@@ -435,10 +435,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
clip: this.getClip(selectedEntity.state),
|
||||
});
|
||||
} else {
|
||||
if (selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity.bufferRenderer.hasBuffer()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.renderer.setBuffer({
|
||||
await selectedEntity.bufferRenderer.setBuffer({
|
||||
id: getPrefixedId('eraser_line'),
|
||||
type: 'eraser_line',
|
||||
points: [alignedPoint.x, alignedPoint.y],
|
||||
@@ -449,10 +449,10 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
}
|
||||
|
||||
if (tool === 'rect') {
|
||||
if (selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity.bufferRenderer.hasBuffer()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.renderer.setBuffer({
|
||||
await selectedEntity.bufferRenderer.setBuffer({
|
||||
id: getPrefixedId('rect'),
|
||||
type: 'rect',
|
||||
rect: { x: Math.round(normalizedPoint.x), y: Math.round(normalizedPoint.y), width: 0, height: 0 },
|
||||
@@ -483,26 +483,32 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
const tool = this.$tool.get();
|
||||
|
||||
if (tool === 'brush') {
|
||||
if (selectedEntity.renderer.bufferState?.type === 'brush_line' && selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (
|
||||
selectedEntity.bufferRenderer.state?.type === 'brush_line' &&
|
||||
selectedEntity.bufferRenderer.hasBuffer()
|
||||
) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
} else {
|
||||
selectedEntity.renderer.clearBuffer();
|
||||
selectedEntity.bufferRenderer.clearBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
if (tool === 'eraser') {
|
||||
if (selectedEntity.renderer.bufferState?.type === 'eraser_line' && selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (
|
||||
selectedEntity.bufferRenderer.state?.type === 'eraser_line' &&
|
||||
selectedEntity.bufferRenderer.hasBuffer()
|
||||
) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
} else {
|
||||
selectedEntity.renderer.clearBuffer();
|
||||
selectedEntity.bufferRenderer.clearBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
if (tool === 'rect') {
|
||||
if (selectedEntity.renderer.bufferState?.type === 'rect' && selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity.bufferRenderer.state?.type === 'rect' && selectedEntity.bufferRenderer.hasBuffer()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
} else {
|
||||
selectedEntity.renderer.clearBuffer();
|
||||
selectedEntity.bufferRenderer.clearBuffer();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -535,7 +541,7 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
return;
|
||||
}
|
||||
|
||||
const bufferState = selectedEntity.renderer.bufferState;
|
||||
const bufferState = selectedEntity.bufferRenderer.state;
|
||||
|
||||
if (!bufferState) {
|
||||
return;
|
||||
@@ -559,7 +565,7 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
}
|
||||
|
||||
bufferState.points.push(alignedPoint.x, alignedPoint.y);
|
||||
await selectedEntity.renderer.setBuffer(bufferState);
|
||||
await selectedEntity.bufferRenderer.setBuffer(bufferState);
|
||||
} else if (tool === 'eraser' && bufferState.type === 'eraser_line') {
|
||||
const lastPoint = getLastPointOfLine(bufferState.points);
|
||||
const minDistance = settings.eraserWidth * this.config.BRUSH_SPACING_TARGET_SCALE;
|
||||
@@ -576,15 +582,15 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
}
|
||||
|
||||
bufferState.points.push(alignedPoint.x, alignedPoint.y);
|
||||
await selectedEntity.renderer.setBuffer(bufferState);
|
||||
await selectedEntity.bufferRenderer.setBuffer(bufferState);
|
||||
} else if (tool === 'rect' && bufferState.type === 'rect') {
|
||||
const normalizedPoint = offsetCoord(cursorPos, selectedEntity.state.position);
|
||||
const alignedPoint = floorCoord(normalizedPoint);
|
||||
bufferState.rect.width = Math.round(alignedPoint.x - bufferState.rect.x);
|
||||
bufferState.rect.height = Math.round(alignedPoint.y - bufferState.rect.y);
|
||||
await selectedEntity.renderer.setBuffer(bufferState);
|
||||
await selectedEntity.bufferRenderer.setBuffer(bufferState);
|
||||
} else {
|
||||
selectedEntity?.renderer.clearBuffer();
|
||||
selectedEntity?.bufferRenderer.clearBuffer();
|
||||
}
|
||||
} finally {
|
||||
this.render();
|
||||
@@ -599,8 +605,12 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
this.$cursorPos.set(null);
|
||||
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
|
||||
|
||||
if (selectedEntity && selectedEntity.renderer.bufferState?.type !== 'rect' && selectedEntity.renderer.hasBuffer()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (
|
||||
selectedEntity &&
|
||||
selectedEntity.bufferRenderer.state?.type !== 'rect' &&
|
||||
selectedEntity.bufferRenderer.hasBuffer()
|
||||
) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
}
|
||||
|
||||
this.render();
|
||||
@@ -640,8 +650,8 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
this.$isMouseDown.set(false);
|
||||
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
|
||||
|
||||
if (selectedEntity && selectedEntity.renderer.hasBuffer() && !this.manager.$isBusy.get()) {
|
||||
selectedEntity.renderer.commitBuffer();
|
||||
if (selectedEntity && selectedEntity.bufferRenderer.hasBuffer() && !this.manager.$isBusy.get()) {
|
||||
selectedEntity.bufferRenderer.commitBuffer();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -659,7 +669,7 @@ export class CanvasToolModule extends CanvasModuleBase {
|
||||
e.preventDefault();
|
||||
const selectedEntity = this.manager.stateApi.getSelectedEntityAdapter();
|
||||
if (selectedEntity) {
|
||||
selectedEntity.renderer.clearBuffer();
|
||||
selectedEntity.bufferRenderer.clearBuffer();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user