mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): rename konva node manager
This commit is contained in:
parent
dab42e258c
commit
382bc6d978
@ -1,112 +0,0 @@
|
||||
import type { BrushLine, EraserLine, ImageObject, RectShape } from 'features/controlLayers/store/types';
|
||||
import type Konva from 'konva';
|
||||
|
||||
export type BrushLineEntry = {
|
||||
id: string;
|
||||
type: BrushLine['type'];
|
||||
konvaLine: Konva.Line;
|
||||
konvaLineGroup: Konva.Group;
|
||||
};
|
||||
|
||||
export type EraserLineEntry = {
|
||||
id: string;
|
||||
type: EraserLine['type'];
|
||||
konvaLine: Konva.Line;
|
||||
konvaLineGroup: Konva.Group;
|
||||
};
|
||||
|
||||
export type RectShapeEntry = {
|
||||
id: string;
|
||||
type: RectShape['type'];
|
||||
konvaRect: Konva.Rect;
|
||||
};
|
||||
|
||||
export type ImageEntry = {
|
||||
id: string;
|
||||
type: ImageObject['type'];
|
||||
konvaImageGroup: Konva.Group;
|
||||
konvaPlaceholderGroup: Konva.Group;
|
||||
konvaPlaceholderText: Konva.Text;
|
||||
konvaImage: Konva.Image | null; // The image is loaded asynchronously, so it may not be available immediately
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
};
|
||||
|
||||
type Entry = BrushLineEntry | EraserLineEntry | RectShapeEntry | ImageEntry;
|
||||
|
||||
export class EntityToKonvaMap {
|
||||
stage: Konva.Stage;
|
||||
mappings: Record<string, EntityToKonvaMapping>;
|
||||
|
||||
constructor(stage: Konva.Stage) {
|
||||
this.stage = stage;
|
||||
this.mappings = {};
|
||||
}
|
||||
|
||||
addMapping(id: string, konvaLayer: Konva.Layer, konvaObjectGroup: Konva.Group): EntityToKonvaMapping {
|
||||
const mapping = new EntityToKonvaMapping(id, konvaLayer, konvaObjectGroup, this);
|
||||
this.mappings[id] = mapping;
|
||||
return mapping;
|
||||
}
|
||||
|
||||
getMapping(id: string): EntityToKonvaMapping | undefined {
|
||||
return this.mappings[id];
|
||||
}
|
||||
|
||||
getMappings(): EntityToKonvaMapping[] {
|
||||
return Object.values(this.mappings);
|
||||
}
|
||||
|
||||
destroyMapping(id: string): void {
|
||||
const mapping = this.getMapping(id);
|
||||
if (!mapping) {
|
||||
return;
|
||||
}
|
||||
mapping.konvaObjectGroup.destroy();
|
||||
delete this.mappings[id];
|
||||
}
|
||||
}
|
||||
|
||||
export class EntityToKonvaMapping {
|
||||
id: string;
|
||||
konvaLayer: Konva.Layer;
|
||||
konvaObjectGroup: Konva.Group;
|
||||
konvaNodeEntries: Record<string, Entry>;
|
||||
map: EntityToKonvaMap;
|
||||
|
||||
constructor(id: string, konvaLayer: Konva.Layer, konvaObjectGroup: Konva.Group, map: EntityToKonvaMap) {
|
||||
this.id = id;
|
||||
this.konvaLayer = konvaLayer;
|
||||
this.konvaObjectGroup = konvaObjectGroup;
|
||||
this.konvaNodeEntries = {};
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
addEntry<T extends Entry>(entry: T): T {
|
||||
this.konvaNodeEntries[entry.id] = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
getEntry<T extends Entry>(id: string): T | undefined {
|
||||
return this.konvaNodeEntries[id] as T | undefined;
|
||||
}
|
||||
|
||||
getEntries<T extends Entry>(): T[] {
|
||||
return Object.values(this.konvaNodeEntries) as T[];
|
||||
}
|
||||
|
||||
destroyEntry(id: string): void {
|
||||
const entry = this.getEntry(id);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
if (entry.type === 'brush_line' || entry.type === 'eraser_line') {
|
||||
entry.konvaLineGroup.destroy();
|
||||
} else if (entry.type === 'rect_shape') {
|
||||
entry.konvaRect.destroy();
|
||||
} else if (entry.type === 'image') {
|
||||
entry.konvaImageGroup.destroy();
|
||||
}
|
||||
delete this.konvaNodeEntries[id];
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
import type { BrushLine, EraserLine, ImageObject, RectShape } from 'features/controlLayers/store/types';
|
||||
import type Konva from 'konva';
|
||||
|
||||
export type BrushLineObjectRecord = {
|
||||
id: string;
|
||||
type: BrushLine['type'];
|
||||
konvaLine: Konva.Line;
|
||||
konvaLineGroup: Konva.Group;
|
||||
};
|
||||
|
||||
export type EraserLineObjectRecord = {
|
||||
id: string;
|
||||
type: EraserLine['type'];
|
||||
konvaLine: Konva.Line;
|
||||
konvaLineGroup: Konva.Group;
|
||||
};
|
||||
|
||||
export type RectShapeObjectRecord = {
|
||||
id: string;
|
||||
type: RectShape['type'];
|
||||
konvaRect: Konva.Rect;
|
||||
};
|
||||
|
||||
export type ImageObjectRecord = {
|
||||
id: string;
|
||||
type: ImageObject['type'];
|
||||
konvaImageGroup: Konva.Group;
|
||||
konvaPlaceholderGroup: Konva.Group;
|
||||
konvaPlaceholderRect: Konva.Rect;
|
||||
konvaPlaceholderText: Konva.Text;
|
||||
konvaImage: Konva.Image | null; // The image is loaded asynchronously, so it may not be available immediately
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
};
|
||||
|
||||
type ObjectRecord = BrushLineObjectRecord | EraserLineObjectRecord | RectShapeObjectRecord | ImageObjectRecord;
|
||||
|
||||
export class KonvaNodeManager {
|
||||
stage: Konva.Stage;
|
||||
adapters: Map<string, EntityKonvaAdapter>;
|
||||
|
||||
constructor(stage: Konva.Stage) {
|
||||
this.stage = stage;
|
||||
this.adapters = new Map();
|
||||
}
|
||||
|
||||
add(id: string, konvaLayer: Konva.Layer, konvaObjectGroup: Konva.Group): EntityKonvaAdapter {
|
||||
const adapter = new EntityKonvaAdapter(id, konvaLayer, konvaObjectGroup, this);
|
||||
this.adapters.set(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
get(id: string): EntityKonvaAdapter | undefined {
|
||||
return this.adapters.get(id);
|
||||
}
|
||||
|
||||
getAll(): EntityKonvaAdapter[] {
|
||||
return Array.from(this.adapters.values());
|
||||
}
|
||||
|
||||
destroy(id: string): boolean {
|
||||
const adapter = this.get(id);
|
||||
if (!adapter) {
|
||||
return false;
|
||||
}
|
||||
adapter.konvaLayer.destroy();
|
||||
return this.adapters.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
export class EntityKonvaAdapter {
|
||||
id: string;
|
||||
konvaLayer: Konva.Layer; // Every entity is associated with a konva layer
|
||||
konvaObjectGroup: Konva.Group; // Every entity's nodes are part of an object group
|
||||
objectRecords: Map<string, ObjectRecord>;
|
||||
manager: KonvaNodeManager;
|
||||
|
||||
constructor(id: string, konvaLayer: Konva.Layer, konvaObjectGroup: Konva.Group, manager: KonvaNodeManager) {
|
||||
this.id = id;
|
||||
this.konvaLayer = konvaLayer;
|
||||
this.konvaObjectGroup = konvaObjectGroup;
|
||||
this.objectRecords = new Map();
|
||||
this.manager = manager;
|
||||
this.konvaLayer.add(this.konvaObjectGroup);
|
||||
this.manager.stage.add(this.konvaLayer);
|
||||
}
|
||||
|
||||
add<T extends ObjectRecord>(objectRecord: T): T {
|
||||
this.objectRecords.set(objectRecord.id, objectRecord);
|
||||
if (objectRecord.type === 'brush_line' || objectRecord.type === 'eraser_line') {
|
||||
objectRecord.konvaLineGroup.add(objectRecord.konvaLine);
|
||||
this.konvaObjectGroup.add(objectRecord.konvaLineGroup);
|
||||
} else if (objectRecord.type === 'rect_shape') {
|
||||
this.konvaObjectGroup.add(objectRecord.konvaRect);
|
||||
} else if (objectRecord.type === 'image') {
|
||||
objectRecord.konvaPlaceholderGroup.add(objectRecord.konvaPlaceholderRect);
|
||||
objectRecord.konvaPlaceholderGroup.add(objectRecord.konvaPlaceholderText);
|
||||
objectRecord.konvaImageGroup.add(objectRecord.konvaPlaceholderGroup);
|
||||
this.konvaObjectGroup.add(objectRecord.konvaImageGroup);
|
||||
}
|
||||
return objectRecord;
|
||||
}
|
||||
|
||||
get<T extends ObjectRecord>(id: string): T | undefined {
|
||||
return this.objectRecords.get(id) as T | undefined;
|
||||
}
|
||||
|
||||
getAll<T extends ObjectRecord>(): T[] {
|
||||
return Array.from(this.objectRecords.values()) as T[];
|
||||
}
|
||||
|
||||
destroy(id: string): boolean {
|
||||
const record = this.get(id);
|
||||
if (!record) {
|
||||
return false;
|
||||
}
|
||||
if (record.type === 'brush_line' || record.type === 'eraser_line') {
|
||||
record.konvaLineGroup.destroy();
|
||||
} else if (record.type === 'rect_shape') {
|
||||
record.konvaRect.destroy();
|
||||
} else if (record.type === 'image') {
|
||||
record.konvaImageGroup.destroy();
|
||||
}
|
||||
return this.objectRecords.delete(id);
|
||||
}
|
||||
}
|
@ -1,27 +1,27 @@
|
||||
import type { EntityToKonvaMap } from 'features/controlLayers/konva/entityToKonvaMap';
|
||||
import { BACKGROUND_LAYER_ID, PREVIEW_LAYER_ID } from 'features/controlLayers/konva/naming';
|
||||
import type { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||
import type { ControlAdapterEntity, LayerEntity, RegionEntity } from 'features/controlLayers/store/types';
|
||||
import type Konva from 'konva';
|
||||
|
||||
export const arrangeEntities = (
|
||||
stage: Konva.Stage,
|
||||
layerMap: EntityToKonvaMap,
|
||||
layerManager: KonvaNodeManager,
|
||||
layers: LayerEntity[],
|
||||
controlAdapterMap: EntityToKonvaMap,
|
||||
controlAdapterManager: KonvaNodeManager,
|
||||
controlAdapters: ControlAdapterEntity[],
|
||||
regionMap: EntityToKonvaMap,
|
||||
regionManager: KonvaNodeManager,
|
||||
regions: RegionEntity[]
|
||||
): void => {
|
||||
let zIndex = 0;
|
||||
stage.findOne<Konva.Layer>(`#${BACKGROUND_LAYER_ID}`)?.zIndex(++zIndex);
|
||||
for (const layer of layers) {
|
||||
layerMap.getMapping(layer.id)?.konvaLayer.zIndex(++zIndex);
|
||||
layerManager.get(layer.id)?.konvaLayer.zIndex(++zIndex);
|
||||
}
|
||||
for (const ca of controlAdapters) {
|
||||
controlAdapterMap.getMapping(ca.id)?.konvaLayer.zIndex(++zIndex);
|
||||
controlAdapterManager.get(ca.id)?.konvaLayer.zIndex(++zIndex);
|
||||
}
|
||||
for (const rg of regions) {
|
||||
regionMap.getMapping(rg.id)?.konvaLayer.zIndex(++zIndex);
|
||||
regionManager.get(rg.id)?.konvaLayer.zIndex(++zIndex);
|
||||
}
|
||||
stage.findOne<Konva.Layer>(`#${PREVIEW_LAYER_ID}`)?.zIndex(++zIndex);
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { EntityToKonvaMap, EntityToKonvaMapping, ImageEntry } from 'features/controlLayers/konva/entityToKonvaMap';
|
||||
import { LightnessToAlphaFilter } from 'features/controlLayers/konva/filters';
|
||||
import { CA_LAYER_IMAGE_NAME, CA_LAYER_NAME, CA_LAYER_OBJECT_GROUP_NAME } from 'features/controlLayers/konva/naming';
|
||||
import type { EntityKonvaAdapter, ImageObjectRecord, KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||
import {
|
||||
createImageObjectGroup,
|
||||
createObjectGroup,
|
||||
@ -21,10 +21,10 @@ import { assert } from 'tsafe';
|
||||
* @param stage The konva stage
|
||||
* @param entity The control adapter layer state
|
||||
*/
|
||||
const getControlAdapter = (map: EntityToKonvaMap, entity: ControlAdapterEntity): EntityToKonvaMapping => {
|
||||
let mapping = map.getMapping(entity.id);
|
||||
if (mapping) {
|
||||
return mapping;
|
||||
const getControlAdapter = (manager: KonvaNodeManager, entity: ControlAdapterEntity): EntityKonvaAdapter => {
|
||||
const adapter = manager.get(entity.id);
|
||||
if (adapter) {
|
||||
return adapter;
|
||||
}
|
||||
const konvaLayer = new Konva.Layer({
|
||||
id: entity.id,
|
||||
@ -33,9 +33,9 @@ const getControlAdapter = (map: EntityToKonvaMap, entity: ControlAdapterEntity):
|
||||
listening: false,
|
||||
});
|
||||
const konvaObjectGroup = createObjectGroup(konvaLayer, CA_LAYER_OBJECT_GROUP_NAME);
|
||||
map.stage.add(konvaLayer);
|
||||
mapping = map.addMapping(entity.id, konvaLayer, konvaObjectGroup);
|
||||
return mapping;
|
||||
konvaLayer.add(konvaObjectGroup);
|
||||
manager.stage.add(konvaLayer);
|
||||
return manager.add(entity.id, konvaLayer, konvaObjectGroup);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -45,26 +45,26 @@ const getControlAdapter = (map: EntityToKonvaMap, entity: ControlAdapterEntity):
|
||||
* @param entity The control adapter layer state
|
||||
* @param getImageDTO A function to retrieve an image DTO from the server, used to update the image source
|
||||
*/
|
||||
export const renderControlAdapter = async (map: EntityToKonvaMap, entity: ControlAdapterEntity): Promise<void> => {
|
||||
const mapping = getControlAdapter(map, entity);
|
||||
export const renderControlAdapter = async (manager: KonvaNodeManager, entity: ControlAdapterEntity): Promise<void> => {
|
||||
const adapter = getControlAdapter(manager, entity);
|
||||
const imageObject = entity.processedImageObject ?? entity.imageObject;
|
||||
|
||||
if (!imageObject) {
|
||||
// The user has deleted/reset the image
|
||||
mapping.getEntries().forEach((entry) => {
|
||||
mapping.destroyEntry(entry.id);
|
||||
adapter.getAll().forEach((entry) => {
|
||||
adapter.destroy(entry.id);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let entry = mapping.getEntries<ImageEntry>()[0];
|
||||
let entry = adapter.getAll<ImageObjectRecord>()[0];
|
||||
const opacity = entity.opacity;
|
||||
const visible = entity.isEnabled;
|
||||
const filters = entity.filter === 'LightnessToAlphaFilter' ? [LightnessToAlphaFilter] : [];
|
||||
|
||||
if (!entry) {
|
||||
entry = await createImageObjectGroup({
|
||||
mapping,
|
||||
adapter: adapter,
|
||||
obj: imageObject,
|
||||
name: CA_LAYER_IMAGE_NAME,
|
||||
onLoad: (konvaImage) => {
|
||||
@ -83,7 +83,7 @@ export const renderControlAdapter = async (map: EntityToKonvaMap, entity: Contro
|
||||
assert(imageSource instanceof HTMLImageElement, `Image source must be an HTMLImageElement`);
|
||||
if (imageSource.id !== imageObject.image.name) {
|
||||
updateImageSource({
|
||||
entry,
|
||||
objectRecord: entry,
|
||||
image: imageObject.image,
|
||||
onLoad: (konvaImage) => {
|
||||
konvaImage.filters(filters);
|
||||
@ -103,14 +103,14 @@ export const renderControlAdapter = async (map: EntityToKonvaMap, entity: Contro
|
||||
}
|
||||
};
|
||||
|
||||
export const renderControlAdapters = (map: EntityToKonvaMap, entities: ControlAdapterEntity[]): void => {
|
||||
export const renderControlAdapters = (manager: KonvaNodeManager, entities: ControlAdapterEntity[]): void => {
|
||||
// Destroy nonexistent layers
|
||||
for (const mapping of map.getMappings()) {
|
||||
if (!entities.find((ca) => ca.id === mapping.id)) {
|
||||
map.destroyMapping(mapping.id);
|
||||
for (const adapters of manager.getAll()) {
|
||||
if (!entities.find((ca) => ca.id === adapters.id)) {
|
||||
manager.destroy(adapters.id);
|
||||
}
|
||||
}
|
||||
for (const ca of entities) {
|
||||
renderControlAdapter(map, ca);
|
||||
for (const entity of entities) {
|
||||
renderControlAdapter(manager, entity);
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { EntityToKonvaMap, EntityToKonvaMapping } from 'features/controlLayers/konva/entityToKonvaMap';
|
||||
import {
|
||||
RASTER_LAYER_BRUSH_LINE_NAME,
|
||||
RASTER_LAYER_ERASER_LINE_NAME,
|
||||
@ -7,6 +6,7 @@ import {
|
||||
RASTER_LAYER_OBJECT_GROUP_NAME,
|
||||
RASTER_LAYER_RECT_SHAPE_NAME,
|
||||
} from 'features/controlLayers/konva/naming';
|
||||
import type { EntityKonvaAdapter, KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||
import {
|
||||
createImageObjectGroup,
|
||||
createObjectGroup,
|
||||
@ -29,13 +29,13 @@ import Konva from 'konva';
|
||||
* @param onPosChanged Callback for when the layer's position changes
|
||||
*/
|
||||
const getLayer = (
|
||||
map: EntityToKonvaMap,
|
||||
map: KonvaNodeManager,
|
||||
entity: LayerEntity,
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void
|
||||
): EntityToKonvaMapping => {
|
||||
let mapping = map.getMapping(entity.id);
|
||||
if (mapping) {
|
||||
return mapping;
|
||||
): EntityKonvaAdapter => {
|
||||
const adapter = map.get(entity.id);
|
||||
if (adapter) {
|
||||
return adapter;
|
||||
}
|
||||
// This layer hasn't been added to the konva state yet
|
||||
const konvaLayer = new Konva.Layer({
|
||||
@ -54,9 +54,7 @@ const getLayer = (
|
||||
}
|
||||
|
||||
const konvaObjectGroup = createObjectGroup(konvaLayer, RASTER_LAYER_OBJECT_GROUP_NAME);
|
||||
map.stage.add(konvaLayer);
|
||||
mapping = map.addMapping(entity.id, konvaLayer, konvaObjectGroup);
|
||||
return mapping;
|
||||
return map.add(entity.id, konvaLayer, konvaObjectGroup);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -67,15 +65,15 @@ const getLayer = (
|
||||
* @param onPosChanged Callback for when the layer's position changes
|
||||
*/
|
||||
export const renderLayer = async (
|
||||
map: EntityToKonvaMap,
|
||||
manager: KonvaNodeManager,
|
||||
entity: LayerEntity,
|
||||
tool: Tool,
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void
|
||||
) => {
|
||||
const mapping = getLayer(map, entity, onPosChanged);
|
||||
const adapter = getLayer(manager, entity, onPosChanged);
|
||||
|
||||
// Update the layer's position and listening state
|
||||
mapping.konvaLayer.setAttrs({
|
||||
adapter.konvaLayer.setAttrs({
|
||||
listening: tool === 'move', // The layer only listens when using the move tool - otherwise the stage is handling mouse events
|
||||
x: Math.floor(entity.x),
|
||||
y: Math.floor(entity.y),
|
||||
@ -83,35 +81,35 @@ export const renderLayer = async (
|
||||
|
||||
const objectIds = entity.objects.map(mapId);
|
||||
// Destroy any objects that are no longer in state
|
||||
for (const entry of mapping.getEntries()) {
|
||||
if (!objectIds.includes(entry.id)) {
|
||||
mapping.destroyEntry(entry.id);
|
||||
for (const objectRecord of adapter.getAll()) {
|
||||
if (!objectIds.includes(objectRecord.id)) {
|
||||
adapter.destroy(objectRecord.id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const obj of entity.objects) {
|
||||
if (obj.type === 'brush_line') {
|
||||
const entry = getBrushLine(mapping, obj, RASTER_LAYER_BRUSH_LINE_NAME);
|
||||
const objectRecord = getBrushLine(adapter, obj, RASTER_LAYER_BRUSH_LINE_NAME);
|
||||
// Only update the points if they have changed.
|
||||
if (entry.konvaLine.points().length !== obj.points.length) {
|
||||
entry.konvaLine.points(obj.points);
|
||||
if (objectRecord.konvaLine.points().length !== obj.points.length) {
|
||||
objectRecord.konvaLine.points(obj.points);
|
||||
}
|
||||
} else if (obj.type === 'eraser_line') {
|
||||
const entry = getEraserLine(mapping, obj, RASTER_LAYER_ERASER_LINE_NAME);
|
||||
const objectRecord = getEraserLine(adapter, obj, RASTER_LAYER_ERASER_LINE_NAME);
|
||||
// Only update the points if they have changed.
|
||||
if (entry.konvaLine.points().length !== obj.points.length) {
|
||||
entry.konvaLine.points(obj.points);
|
||||
if (objectRecord.konvaLine.points().length !== obj.points.length) {
|
||||
objectRecord.konvaLine.points(obj.points);
|
||||
}
|
||||
} else if (obj.type === 'rect_shape') {
|
||||
getRectShape(mapping, obj, RASTER_LAYER_RECT_SHAPE_NAME);
|
||||
getRectShape(adapter, obj, RASTER_LAYER_RECT_SHAPE_NAME);
|
||||
} else if (obj.type === 'image') {
|
||||
createImageObjectGroup({ mapping, obj, name: RASTER_LAYER_IMAGE_NAME });
|
||||
createImageObjectGroup({ adapter, obj, name: RASTER_LAYER_IMAGE_NAME });
|
||||
}
|
||||
}
|
||||
|
||||
// Only update layer visibility if it has changed.
|
||||
if (mapping.konvaLayer.visible() !== entity.isEnabled) {
|
||||
mapping.konvaLayer.visible(entity.isEnabled);
|
||||
if (adapter.konvaLayer.visible() !== entity.isEnabled) {
|
||||
adapter.konvaLayer.visible(entity.isEnabled);
|
||||
}
|
||||
|
||||
// const bboxRect = konvaLayer.findOne<Konva.Rect>(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(layerState, konvaLayer);
|
||||
@ -132,22 +130,22 @@ export const renderLayer = async (
|
||||
// bboxRect.visible(false);
|
||||
// }
|
||||
|
||||
mapping.konvaObjectGroup.opacity(entity.opacity);
|
||||
adapter.konvaObjectGroup.opacity(entity.opacity);
|
||||
};
|
||||
|
||||
export const renderLayers = (
|
||||
map: EntityToKonvaMap,
|
||||
manager: KonvaNodeManager,
|
||||
entities: LayerEntity[],
|
||||
tool: Tool,
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void
|
||||
): void => {
|
||||
// Destroy nonexistent layers
|
||||
for (const mapping of map.getMappings()) {
|
||||
if (!entities.find((l) => l.id === mapping.id)) {
|
||||
map.destroyMapping(mapping.id);
|
||||
for (const adapter of manager.getAll()) {
|
||||
if (!entities.find((l) => l.id === adapter.id)) {
|
||||
manager.destroy(adapter.id);
|
||||
}
|
||||
}
|
||||
for (const layer of entities) {
|
||||
renderLayer(map, layer, tool, onPosChanged);
|
||||
for (const entity of entities) {
|
||||
renderLayer(manager, entity, tool, onPosChanged);
|
||||
}
|
||||
};
|
||||
|
@ -1,11 +1,4 @@
|
||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
||||
import type {
|
||||
BrushLineEntry,
|
||||
EntityToKonvaMapping,
|
||||
EraserLineEntry,
|
||||
ImageEntry,
|
||||
RectShapeEntry,
|
||||
} from 'features/controlLayers/konva/entityToKonvaMap';
|
||||
import {
|
||||
getLayerBboxId,
|
||||
getObjectGroupId,
|
||||
@ -13,6 +6,13 @@ import {
|
||||
LAYER_BBOX_NAME,
|
||||
PREVIEW_GENERATION_BBOX_DUMMY_RECT,
|
||||
} from 'features/controlLayers/konva/naming';
|
||||
import type {
|
||||
BrushLineObjectRecord,
|
||||
EntityKonvaAdapter,
|
||||
EraserLineObjectRecord,
|
||||
ImageObjectRecord,
|
||||
RectShapeObjectRecord,
|
||||
} from 'features/controlLayers/konva/nodeManager';
|
||||
import type {
|
||||
BrushLine,
|
||||
CanvasEntity,
|
||||
@ -39,32 +39,33 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
* @param layerObjectGroup The konva layer's object group to add the line to
|
||||
* @param name The konva name for the line
|
||||
*/
|
||||
export const getBrushLine = (mapping: EntityToKonvaMapping, brushLine: BrushLine, name: string): BrushLineEntry => {
|
||||
let entry = mapping.getEntry<BrushLineEntry>(brushLine.id);
|
||||
if (entry) {
|
||||
return entry;
|
||||
export const getBrushLine = (
|
||||
adapter: EntityKonvaAdapter,
|
||||
brushLine: BrushLine,
|
||||
name: string
|
||||
): BrushLineObjectRecord => {
|
||||
const objectRecord = adapter.get<BrushLineObjectRecord>(brushLine.id);
|
||||
if (objectRecord) {
|
||||
return objectRecord;
|
||||
}
|
||||
|
||||
const { id, strokeWidth, clip, color } = brushLine;
|
||||
const konvaLineGroup = new Konva.Group({
|
||||
clip: brushLine.clip,
|
||||
clip,
|
||||
listening: false,
|
||||
});
|
||||
const konvaLine = new Konva.Line({
|
||||
id: brushLine.id,
|
||||
id,
|
||||
name,
|
||||
strokeWidth: brushLine.strokeWidth,
|
||||
strokeWidth,
|
||||
tension: 0,
|
||||
lineCap: 'round',
|
||||
lineJoin: 'round',
|
||||
shadowForStrokeEnabled: false,
|
||||
globalCompositeOperation: 'source-over',
|
||||
listening: false,
|
||||
stroke: rgbaColorToString(brushLine.color),
|
||||
stroke: rgbaColorToString(color),
|
||||
});
|
||||
konvaLineGroup.add(konvaLine);
|
||||
mapping.konvaObjectGroup.add(konvaLineGroup);
|
||||
entry = mapping.addEntry({ id: brushLine.id, type: 'brush_line', konvaLine, konvaLineGroup });
|
||||
return entry;
|
||||
return adapter.add({ id, type: 'brush_line', konvaLine, konvaLineGroup });
|
||||
};
|
||||
|
||||
/**
|
||||
@ -73,20 +74,25 @@ export const getBrushLine = (mapping: EntityToKonvaMapping, brushLine: BrushLine
|
||||
* @param layerObjectGroup The konva layer's object group to add the line to
|
||||
* @param name The konva name for the line
|
||||
*/
|
||||
export const getEraserLine = (mapping: EntityToKonvaMapping, eraserLine: EraserLine, name: string): EraserLineEntry => {
|
||||
let entry = mapping.getEntry<EraserLineEntry>(eraserLine.id);
|
||||
if (entry) {
|
||||
return entry;
|
||||
export const getEraserLine = (
|
||||
adapter: EntityKonvaAdapter,
|
||||
eraserLine: EraserLine,
|
||||
name: string
|
||||
): EraserLineObjectRecord => {
|
||||
const objectRecord = adapter.get<EraserLineObjectRecord>(eraserLine.id);
|
||||
if (objectRecord) {
|
||||
return objectRecord;
|
||||
}
|
||||
|
||||
const { id, strokeWidth, clip } = eraserLine;
|
||||
const konvaLineGroup = new Konva.Group({
|
||||
clip: eraserLine.clip,
|
||||
clip,
|
||||
listening: false,
|
||||
});
|
||||
const konvaLine = new Konva.Line({
|
||||
id: eraserLine.id,
|
||||
id,
|
||||
name,
|
||||
strokeWidth: eraserLine.strokeWidth,
|
||||
strokeWidth,
|
||||
tension: 0,
|
||||
lineCap: 'round',
|
||||
lineJoin: 'round',
|
||||
@ -94,12 +100,8 @@ export const getEraserLine = (mapping: EntityToKonvaMapping, eraserLine: EraserL
|
||||
globalCompositeOperation: 'destination-out',
|
||||
listening: false,
|
||||
stroke: rgbaColorToString(DEFAULT_RGBA_COLOR),
|
||||
clip: eraserLine.clip,
|
||||
});
|
||||
konvaLineGroup.add(konvaLine);
|
||||
mapping.konvaObjectGroup.add(konvaLineGroup);
|
||||
entry = mapping.addEntry({ id: eraserLine.id, type: 'eraser_line', konvaLine, konvaLineGroup });
|
||||
return entry;
|
||||
return adapter.add({ id, type: 'eraser_line', konvaLine, konvaLineGroup });
|
||||
};
|
||||
|
||||
/**
|
||||
@ -108,87 +110,89 @@ export const getEraserLine = (mapping: EntityToKonvaMapping, eraserLine: EraserL
|
||||
* @param layerObjectGroup The konva layer's object group to add the rect to
|
||||
* @param name The konva name for the rect
|
||||
*/
|
||||
export const getRectShape = (mapping: EntityToKonvaMapping, rectShape: RectShape, name: string): RectShapeEntry => {
|
||||
let entry = mapping.getEntry<RectShapeEntry>(rectShape.id);
|
||||
if (entry) {
|
||||
return entry;
|
||||
export const getRectShape = (
|
||||
adapter: EntityKonvaAdapter,
|
||||
rectShape: RectShape,
|
||||
name: string
|
||||
): RectShapeObjectRecord => {
|
||||
const objectRecord = adapter.get<RectShapeObjectRecord>(rectShape.id);
|
||||
if (objectRecord) {
|
||||
return objectRecord;
|
||||
}
|
||||
const { id, x, y, width, height } = rectShape;
|
||||
const konvaRect = new Konva.Rect({
|
||||
id: rectShape.id,
|
||||
key: rectShape.id,
|
||||
id,
|
||||
name,
|
||||
x: rectShape.x,
|
||||
y: rectShape.y,
|
||||
width: rectShape.width,
|
||||
height: rectShape.height,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
listening: false,
|
||||
fill: rgbaColorToString(rectShape.color),
|
||||
});
|
||||
mapping.konvaObjectGroup.add(konvaRect);
|
||||
entry = mapping.addEntry({ id: rectShape.id, type: 'rect_shape', konvaRect });
|
||||
return entry;
|
||||
return adapter.add({ id: rectShape.id, type: 'rect_shape', konvaRect });
|
||||
};
|
||||
|
||||
export const updateImageSource = async (arg: {
|
||||
entry: ImageEntry;
|
||||
objectRecord: ImageObjectRecord;
|
||||
image: ImageWithDims;
|
||||
getImageDTO?: (imageName: string) => Promise<ImageDTO | null>;
|
||||
onLoading?: () => void;
|
||||
onLoad?: (konvaImage: Konva.Image) => void;
|
||||
onError?: () => void;
|
||||
}) => {
|
||||
const { entry, image, getImageDTO = defaultGetImageDTO, onLoading, onLoad, onError } = arg;
|
||||
const { objectRecord, image, getImageDTO = defaultGetImageDTO, onLoading, onLoad, onError } = arg;
|
||||
|
||||
try {
|
||||
entry.isLoading = true;
|
||||
if (!entry.konvaImage) {
|
||||
entry.konvaPlaceholderGroup.visible(true);
|
||||
entry.konvaPlaceholderText.text(t('common.loadingImage', 'Loading Image'));
|
||||
objectRecord.isLoading = true;
|
||||
if (!objectRecord.konvaImage) {
|
||||
objectRecord.konvaPlaceholderGroup.visible(true);
|
||||
objectRecord.konvaPlaceholderText.text(t('common.loadingImage', 'Loading Image'));
|
||||
}
|
||||
onLoading?.();
|
||||
|
||||
const imageDTO = await getImageDTO(image.name);
|
||||
if (!imageDTO) {
|
||||
entry.isLoading = false;
|
||||
entry.isError = true;
|
||||
entry.konvaPlaceholderGroup.visible(true);
|
||||
entry.konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
|
||||
objectRecord.isLoading = false;
|
||||
objectRecord.isError = true;
|
||||
objectRecord.konvaPlaceholderGroup.visible(true);
|
||||
objectRecord.konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
|
||||
onError?.();
|
||||
return;
|
||||
}
|
||||
const imageEl = new Image();
|
||||
imageEl.onload = () => {
|
||||
if (entry.konvaImage) {
|
||||
entry.konvaImage.setAttrs({
|
||||
if (objectRecord.konvaImage) {
|
||||
objectRecord.konvaImage.setAttrs({
|
||||
image: imageEl,
|
||||
});
|
||||
} else {
|
||||
entry.konvaImage = new Konva.Image({
|
||||
id: entry.id,
|
||||
objectRecord.konvaImage = new Konva.Image({
|
||||
id: objectRecord.id,
|
||||
listening: false,
|
||||
image: imageEl,
|
||||
});
|
||||
entry.konvaImageGroup.add(entry.konvaImage);
|
||||
objectRecord.konvaImageGroup.add(objectRecord.konvaImage);
|
||||
}
|
||||
entry.isLoading = false;
|
||||
entry.isError = false;
|
||||
entry.konvaPlaceholderGroup.visible(false);
|
||||
onLoad?.(entry.konvaImage);
|
||||
objectRecord.isLoading = false;
|
||||
objectRecord.isError = false;
|
||||
objectRecord.konvaPlaceholderGroup.visible(false);
|
||||
onLoad?.(objectRecord.konvaImage);
|
||||
};
|
||||
imageEl.onerror = () => {
|
||||
entry.isLoading = false;
|
||||
entry.isError = true;
|
||||
entry.konvaPlaceholderGroup.visible(true);
|
||||
entry.konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
|
||||
objectRecord.isLoading = false;
|
||||
objectRecord.isError = true;
|
||||
objectRecord.konvaPlaceholderGroup.visible(true);
|
||||
objectRecord.konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
|
||||
onError?.();
|
||||
};
|
||||
imageEl.id = image.name;
|
||||
imageEl.src = imageDTO.image_url;
|
||||
} catch {
|
||||
entry.isLoading = false;
|
||||
entry.isError = true;
|
||||
entry.konvaPlaceholderGroup.visible(true);
|
||||
entry.konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
|
||||
objectRecord.isLoading = false;
|
||||
objectRecord.isError = true;
|
||||
objectRecord.konvaPlaceholderGroup.visible(true);
|
||||
objectRecord.konvaPlaceholderText.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
|
||||
onError?.();
|
||||
}
|
||||
};
|
||||
@ -199,18 +203,18 @@ export const updateImageSource = async (arg: {
|
||||
* @returns The konva group for the image placeholder, and callbacks to handle loading and error states
|
||||
*/
|
||||
export const createImageObjectGroup = (arg: {
|
||||
mapping: EntityToKonvaMapping;
|
||||
adapter: EntityKonvaAdapter;
|
||||
obj: ImageObject;
|
||||
name: string;
|
||||
getImageDTO?: (imageName: string) => Promise<ImageDTO | null>;
|
||||
onLoad?: (konvaImage: Konva.Image) => void;
|
||||
onLoading?: () => void;
|
||||
onError?: () => void;
|
||||
}): ImageEntry => {
|
||||
const { mapping, obj, name, getImageDTO = defaultGetImageDTO, onLoad, onLoading, onError } = arg;
|
||||
let entry = mapping.getEntry<ImageEntry>(obj.id);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}): ImageObjectRecord => {
|
||||
const { adapter, obj, name, getImageDTO = defaultGetImageDTO, onLoad, onLoading, onError } = arg;
|
||||
let objectRecord = adapter.get<ImageObjectRecord>(obj.id);
|
||||
if (objectRecord) {
|
||||
return objectRecord;
|
||||
}
|
||||
const { id, image } = obj;
|
||||
const { width, height } = obj;
|
||||
@ -234,23 +238,19 @@ export const createImageObjectGroup = (arg: {
|
||||
text: t('common.loadingImage', 'Loading Image'),
|
||||
listening: false,
|
||||
});
|
||||
konvaPlaceholderGroup.add(konvaPlaceholderRect);
|
||||
konvaPlaceholderGroup.add(konvaPlaceholderText);
|
||||
konvaImageGroup.add(konvaPlaceholderGroup);
|
||||
mapping.konvaObjectGroup.add(konvaImageGroup);
|
||||
|
||||
entry = mapping.addEntry({
|
||||
objectRecord = adapter.add({
|
||||
id,
|
||||
type: 'image',
|
||||
konvaImageGroup,
|
||||
konvaPlaceholderGroup,
|
||||
konvaPlaceholderRect,
|
||||
konvaPlaceholderText,
|
||||
konvaImage: null,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
});
|
||||
updateImageSource({ entry, image, getImageDTO, onLoad, onLoading, onError });
|
||||
return entry;
|
||||
updateImageSource({ objectRecord, image, getImageDTO, onLoad, onLoading, onError });
|
||||
return objectRecord;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||
import type { EntityToKonvaMap, EntityToKonvaMapping } from 'features/controlLayers/konva/entityToKonvaMap';
|
||||
import {
|
||||
COMPOSITING_RECT_NAME,
|
||||
RG_LAYER_BRUSH_LINE_NAME,
|
||||
@ -8,6 +7,7 @@ import {
|
||||
RG_LAYER_OBJECT_GROUP_NAME,
|
||||
RG_LAYER_RECT_SHAPE_NAME,
|
||||
} from 'features/controlLayers/konva/naming';
|
||||
import type { EntityKonvaAdapter, KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||
import { getLayerBboxFast } from 'features/controlLayers/konva/renderers/bbox';
|
||||
import {
|
||||
createObjectGroup,
|
||||
@ -49,13 +49,13 @@ const createCompositingRect = (konvaLayer: Konva.Layer): Konva.Rect => {
|
||||
* @param onLayerPosChanged Callback for when the layer's position changes
|
||||
*/
|
||||
const getRegion = (
|
||||
map: EntityToKonvaMap,
|
||||
manager: KonvaNodeManager,
|
||||
entity: RegionEntity,
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void
|
||||
): EntityToKonvaMapping => {
|
||||
let mapping = map.getMapping(entity.id);
|
||||
if (mapping) {
|
||||
return mapping;
|
||||
): EntityKonvaAdapter => {
|
||||
const adapter = manager.get(entity.id);
|
||||
if (adapter) {
|
||||
return adapter;
|
||||
}
|
||||
// This layer hasn't been added to the konva state yet
|
||||
const konvaLayer = new Konva.Layer({
|
||||
@ -74,9 +74,7 @@ const getRegion = (
|
||||
}
|
||||
|
||||
const konvaObjectGroup = createObjectGroup(konvaLayer, RG_LAYER_OBJECT_GROUP_NAME);
|
||||
map.stage.add(konvaLayer);
|
||||
mapping = map.addMapping(entity.id, konvaLayer, konvaObjectGroup);
|
||||
return mapping;
|
||||
return manager.add(entity.id, konvaLayer, konvaObjectGroup);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -88,17 +86,17 @@ const getRegion = (
|
||||
* @param onPosChanged Callback for when the layer's position changes
|
||||
*/
|
||||
export const renderRegion = (
|
||||
map: EntityToKonvaMap,
|
||||
manager: KonvaNodeManager,
|
||||
entity: RegionEntity,
|
||||
globalMaskLayerOpacity: number,
|
||||
tool: Tool,
|
||||
selectedEntityIdentifier: CanvasEntityIdentifier | null,
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void
|
||||
): void => {
|
||||
const mapping = getRegion(map, entity, onPosChanged);
|
||||
const adapter = getRegion(manager, entity, onPosChanged);
|
||||
|
||||
// Update the layer's position and listening state
|
||||
mapping.konvaLayer.setAttrs({
|
||||
adapter.konvaLayer.setAttrs({
|
||||
listening: tool === 'move', // The layer only listens when using the move tool - otherwise the stage is handling mouse events
|
||||
x: Math.floor(entity.x),
|
||||
y: Math.floor(entity.y),
|
||||
@ -112,67 +110,67 @@ export const renderRegion = (
|
||||
|
||||
const objectIds = entity.objects.map(mapId);
|
||||
// Destroy any objects that are no longer in state
|
||||
for (const entry of mapping.getEntries()) {
|
||||
if (!objectIds.includes(entry.id)) {
|
||||
mapping.destroyEntry(entry.id);
|
||||
for (const objectRecord of adapter.getAll()) {
|
||||
if (!objectIds.includes(objectRecord.id)) {
|
||||
adapter.destroy(objectRecord.id);
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const obj of entity.objects) {
|
||||
if (obj.type === 'brush_line') {
|
||||
const entry = getBrushLine(mapping, obj, RG_LAYER_BRUSH_LINE_NAME);
|
||||
const objectRecord = getBrushLine(adapter, obj, RG_LAYER_BRUSH_LINE_NAME);
|
||||
|
||||
// Only update the points if they have changed. The point values are never mutated, they are only added to the
|
||||
// array, so checking the length is sufficient to determine if we need to re-cache.
|
||||
if (entry.konvaLine.points().length !== obj.points.length) {
|
||||
entry.konvaLine.points(obj.points);
|
||||
if (objectRecord.konvaLine.points().length !== obj.points.length) {
|
||||
objectRecord.konvaLine.points(obj.points);
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
// Only update the color if it has changed.
|
||||
if (entry.konvaLine.stroke() !== rgbColor) {
|
||||
entry.konvaLine.stroke(rgbColor);
|
||||
if (objectRecord.konvaLine.stroke() !== rgbColor) {
|
||||
objectRecord.konvaLine.stroke(rgbColor);
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
} else if (obj.type === 'eraser_line') {
|
||||
const entry = getEraserLine(mapping, obj, RG_LAYER_ERASER_LINE_NAME);
|
||||
const objectRecord = getEraserLine(adapter, obj, RG_LAYER_ERASER_LINE_NAME);
|
||||
|
||||
// Only update the points if they have changed. The point values are never mutated, they are only added to the
|
||||
// array, so checking the length is sufficient to determine if we need to re-cache.
|
||||
if (entry.konvaLine.points().length !== obj.points.length) {
|
||||
entry.konvaLine.points(obj.points);
|
||||
if (objectRecord.konvaLine.points().length !== obj.points.length) {
|
||||
objectRecord.konvaLine.points(obj.points);
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
// Only update the color if it has changed.
|
||||
if (entry.konvaLine.stroke() !== rgbColor) {
|
||||
entry.konvaLine.stroke(rgbColor);
|
||||
if (objectRecord.konvaLine.stroke() !== rgbColor) {
|
||||
objectRecord.konvaLine.stroke(rgbColor);
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
} else if (obj.type === 'rect_shape') {
|
||||
const entry = getRectShape(mapping, obj, RG_LAYER_RECT_SHAPE_NAME);
|
||||
const objectRecord = getRectShape(adapter, obj, RG_LAYER_RECT_SHAPE_NAME);
|
||||
|
||||
// Only update the color if it has changed.
|
||||
if (entry.konvaRect.fill() !== rgbColor) {
|
||||
entry.konvaRect.fill(rgbColor);
|
||||
if (objectRecord.konvaRect.fill() !== rgbColor) {
|
||||
objectRecord.konvaRect.fill(rgbColor);
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only update layer visibility if it has changed.
|
||||
if (mapping.konvaLayer.visible() !== entity.isEnabled) {
|
||||
mapping.konvaLayer.visible(entity.isEnabled);
|
||||
if (adapter.konvaLayer.visible() !== entity.isEnabled) {
|
||||
adapter.konvaLayer.visible(entity.isEnabled);
|
||||
groupNeedsCache = true;
|
||||
}
|
||||
|
||||
if (mapping.konvaObjectGroup.getChildren().length === 0) {
|
||||
if (adapter.konvaObjectGroup.getChildren().length === 0) {
|
||||
// No objects - clear the cache to reset the previous pixel data
|
||||
mapping.konvaObjectGroup.clearCache();
|
||||
adapter.konvaObjectGroup.clearCache();
|
||||
return;
|
||||
}
|
||||
|
||||
const compositingRect =
|
||||
mapping.konvaLayer.findOne<Konva.Rect>(`.${COMPOSITING_RECT_NAME}`) ?? createCompositingRect(mapping.konvaLayer);
|
||||
adapter.konvaLayer.findOne<Konva.Rect>(`.${COMPOSITING_RECT_NAME}`) ?? createCompositingRect(adapter.konvaLayer);
|
||||
const isSelected = selectedEntityIdentifier?.id === entity.id;
|
||||
|
||||
/**
|
||||
@ -188,32 +186,32 @@ export const renderRegion = (
|
||||
*/
|
||||
if (isSelected && tool !== 'move') {
|
||||
// We must clear the cache first so Konva will re-draw the group with the new compositing rect
|
||||
if (mapping.konvaObjectGroup.isCached()) {
|
||||
mapping.konvaObjectGroup.clearCache();
|
||||
if (adapter.konvaObjectGroup.isCached()) {
|
||||
adapter.konvaObjectGroup.clearCache();
|
||||
}
|
||||
// The user is allowed to reduce mask opacity to 0, but we need the opacity for the compositing rect to work
|
||||
mapping.konvaObjectGroup.opacity(1);
|
||||
adapter.konvaObjectGroup.opacity(1);
|
||||
|
||||
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
|
||||
...(!entity.bboxNeedsUpdate && entity.bbox ? entity.bbox : getLayerBboxFast(mapping.konvaLayer)),
|
||||
...(!entity.bboxNeedsUpdate && entity.bbox ? entity.bbox : getLayerBboxFast(adapter.konvaLayer)),
|
||||
fill: rgbColor,
|
||||
opacity: globalMaskLayerOpacity,
|
||||
// Draw this rect only where there are non-transparent pixels under it (e.g. the mask shapes)
|
||||
globalCompositeOperation: 'source-in',
|
||||
visible: true,
|
||||
// This rect must always be on top of all other shapes
|
||||
zIndex: mapping.konvaObjectGroup.getChildren().length,
|
||||
zIndex: adapter.konvaObjectGroup.getChildren().length,
|
||||
});
|
||||
} else {
|
||||
// The compositing rect should only be shown when the layer is selected.
|
||||
compositingRect.visible(false);
|
||||
// Cache only if needed - or if we are on this code path and _don't_ have a cache
|
||||
if (groupNeedsCache || !mapping.konvaObjectGroup.isCached()) {
|
||||
mapping.konvaObjectGroup.cache();
|
||||
if (groupNeedsCache || !adapter.konvaObjectGroup.isCached()) {
|
||||
adapter.konvaObjectGroup.cache();
|
||||
}
|
||||
// Updating group opacity does not require re-caching
|
||||
mapping.konvaObjectGroup.opacity(globalMaskLayerOpacity);
|
||||
adapter.konvaObjectGroup.opacity(globalMaskLayerOpacity);
|
||||
}
|
||||
|
||||
// const bboxRect =
|
||||
@ -236,7 +234,7 @@ export const renderRegion = (
|
||||
};
|
||||
|
||||
export const renderRegions = (
|
||||
map: EntityToKonvaMap,
|
||||
manager: KonvaNodeManager,
|
||||
entities: RegionEntity[],
|
||||
maskOpacity: number,
|
||||
tool: Tool,
|
||||
@ -244,12 +242,12 @@ export const renderRegions = (
|
||||
onPosChanged?: (arg: PosChangedArg, entityType: CanvasEntity['type']) => void
|
||||
): void => {
|
||||
// Destroy nonexistent layers
|
||||
for (const mapping of map.getMappings()) {
|
||||
if (!entities.find((rg) => rg.id === mapping.id)) {
|
||||
map.destroyMapping(mapping.id);
|
||||
for (const adapter of manager.getAll()) {
|
||||
if (!entities.find((rg) => rg.id === adapter.id)) {
|
||||
manager.destroy(adapter.id);
|
||||
}
|
||||
}
|
||||
for (const rg of entities) {
|
||||
renderRegion(map, rg, maskOpacity, tool, selectedEntityIdentifier, onPosChanged);
|
||||
for (const entity of entities) {
|
||||
renderRegion(manager, entity, maskOpacity, tool, selectedEntityIdentifier, onPosChanged);
|
||||
}
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ import type { Store } from '@reduxjs/toolkit';
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { $isDebugging } from 'app/store/nanostores/isDebugging';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { EntityToKonvaMap } from 'features/controlLayers/konva/entityToKonvaMap';
|
||||
import { KonvaNodeManager } from 'features/controlLayers/konva/nodeManager';
|
||||
import { setStageEventHandlers } from 'features/controlLayers/konva/events';
|
||||
import { arrangeEntities } from 'features/controlLayers/konva/renderers/arrange';
|
||||
import { renderBackgroundLayer } from 'features/controlLayers/konva/renderers/background';
|
||||
@ -282,9 +282,9 @@ export const initializeRenderer = (
|
||||
// the entire state over when needed.
|
||||
const debouncedUpdateBboxes = debounce(updateBboxes, 300);
|
||||
|
||||
const regionMap = new EntityToKonvaMap(stage);
|
||||
const layerMap = new EntityToKonvaMap(stage);
|
||||
const controlAdapterMap = new EntityToKonvaMap(stage);
|
||||
const regionMap = new KonvaNodeManager(stage);
|
||||
const layerMap = new KonvaNodeManager(stage);
|
||||
const controlAdapterMap = new KonvaNodeManager(stage);
|
||||
|
||||
const renderCanvas = () => {
|
||||
const { canvasV2 } = store.getState();
|
||||
|
Loading…
Reference in New Issue
Block a user